在javascript中创建和扩展对象会覆盖基础对象的变量

时间:2014-01-11 21:41:05

标签: javascript inheritance

我有一个Object.create的基本包装器用于我们的应用程序。基本上,该过程是在父对象上使用Object.create,然后.extend({})为其添加新功能。如果我们需要在父类上调用一个方法,我们只需在方法覆盖中使用ParentClass.myFunc.apply(this, arguments);。我遇到的问题是当我初始化一个对象并调用父对象的init函数时,它会覆盖父对象的属性。我怎样才能防止这种情况发生?

这是主要的Object.create包装器:

/**
 * Simple wrapper for Object.create and Object.extend
 * functionality.
 */
var My_Object = {
    /**
     * Simple extend function for inheritance
     * @param props An object of functions to place in the new object.
     * @returns {My_Object}
     */
    extend: function(props) {
        for(var prop in props)
            this[prop] = props[prop];

        return this;
    },

    /**
     * Basic function that checks if a function is native or user-created
     * @author Yong (http://stackoverflow.com/users/962254/yong)
     *
     * @param f The function to test if it is native
     * @returns {boolean}
     *
     * @see http://stackoverflow.com/questions/6598945/detect-if-function-is-native-to-browser
     */
    isFuncNative: function(f) {
        return !!f && (typeof f).toLowerCase() == 'function'
            && (f === Function.prototype
            || /^\s*function\s*(\b[a-z$_][a-z0-9$_]*\b)*\s*\((|([a-z$_][a-z0-9$_]*)(\s*,[a-z$_][a-z0-9$_]*)*)\)\s*{\s*\[native code\]\s*}\s*$/i.test(String(f)));
    },


    /**
     * Simple polyfill wrapper for native Object.create. Using a polyfill
     * instead of native incase for some reason consumer would have overwritten
     * it with unexpected usage.
     */
    create: function(obj) {
        // If the browser supports Object.create and hasn't been overwritten,
        // use the native version instead of the polyfill
        if(My_Object.isFuncNative(Object.create)) {
            // If extend hasn't been defined, add it
            if(!obj.hasOwnProperty('extend'))
                obj.extend = My_Object.extend;

            return Object.create(obj);
        }


        // Create empty function for polyfill
        function F(){}
        F.prototype = o;

        // If extend hasn't been defined, add it
        if(!F.prototype.extend)
            F.prototype.extend = My_Object.extend;

        return new F()
    }
};

然后我定义一个基础对象:

var SomeParentObject = {
    options: {
        a: 'a',
        b: 'b'
    },

    init: function(options) {
        this.options = $.extend(this.options, options || {});

        return this;
    }
};

有些孩子反对:

var ChildObject1 = My_Object.create(SomeParentObject).extend({
    init: function() {
        SomeParentObject.init.call(this, {b: 'c'});        

        return this;
    }
}).init();

var ChildObject2 = My_Object.create(SomeParentObject).extend({}).init();

现在期望的结果是永远不应该修改SomeParentObject的选项。我无法弄清楚究竟是什么原因,但我确信这是我做的蠢事。如果您执行了一些基本的console.log,则可以看到当ChildObject2有一些要覆盖的选项时,它会覆盖SomeParentObject,因此会覆盖所有子项。

console.log('<<SomeParentObject.a, SomeParentObject.b>>');
console.log(SomeParentObject.options.a + ', ' + SomeParentObject.options.b);

console.log('<<ChildObject1.a, ChildeObject1.b>>');
console.log(ChildObject1.options.a + ', ' + ChildObject1.options.b);

console.log('<<ChildObject2.a, ChildeObject2.b>>');
console.log(ChildObject2.options.a + ', ' + ChildObject2.options.b);

哪个输出:

<<SomeParentObject.a, SomeParentObject.b>>
a, c
<<ChildObject1.a, ChildeObject1.b>>
a, c
<<ChildObject2.a, ChildeObject2.b>>
a, c 

应该输出的是:

<<SomeParentObject.a, SomeParentObject.b>>
a, b
<<ChildObject1.a, ChildeObject1.b>>
a, c
<<ChildObject2.a, ChildeObject2.b>>
a, b 

1 个答案:

答案 0 :(得分:2)

如果您不喜欢JavaScript中原型设计的工作方式以实现简单的继承和OOP方式,我建议您看一下:https://github.com/haroldiedema/joii

它基本上允许您执行以下操作(以及更多):

// First (bottom level)
var Person = new Class(function() {
    this.name = "Unknown Person";
});

// Employee, extend on Person & apply the Role property.
var Employee = new Class({ extends: Person }, function() {
    this.name = 'Unknown Employee';
    this.role = 'Employee';

    this.getValue = function() {
        return "Hello World";
    }
});

// 3rd level, extend on Employee. Modify existing properties.
var Manager = new Class({ extends: Employee }, function() {

    // Overwrite the value of 'role'.
    this.role = this.role + ': Manager';

    // Class constructor to apply the given 'name' value.
    this.__construct = function(name) {
        this.name = name;
    }

    // Parent inheritance & override
    this.getValue = function() {
        return this.parent.getValue().toUpperCase();
    }
});

// And to use the final result:
var myManager = new Manager("John Smith");
console.log( myManager.name ); // John Smith
console.log( myManager.role ); // Manager

console.log( myManager.getValue() ); // HELLO WORLD