javascript 继承:实例与原型
javascript inheritance: Instance vs prototype
使用继承时,ChildObject.prototype = new ParentObject();
或 ChildObject.prototype = ParentObject.prototype;
之间有什么区别,哪种方法更好。
我在 Douglas Crockford 的例子中看到了 ChildObject.prototype = new ParentObject();
。
我们通常使用原型来提供方法 - 以避免为 class 的每个实例重复相同的函数。换句话说,对于类似...
ChildObject.prototype.foo = function() {
console.log('foo');
};
现在您可能看到了第二种方法的问题:修改 ChildObject.prototype
也会修改 ParentObject.prototype
(在 ChildObject.prototype = ParentObject.prototype
行之后是同一个对象)。这意味着此示例中的所有 ParentObject
都将有 foo
方法可供他们使用。实际上,说 ParentObject
是 ChildObject
的 parent 甚至是不正确的——两者具有相同的原型链。
然而还有另一个令人讨厌的副作用。考虑一下:
function ParentObj() {}
function ChildObj() {}
ChildObj.prototype = ParentObj.prototype;
var i = new ParentObj();
console.log(i); // ParentObj {}
var j = new ChildObj();
console.log(j); // ParentObj {}
看,两个原型,因为它们是同一个对象,具有相同的 constructor
属性。所以为了区分它们,我们必须在构造函数中引入一些标志,从根本上覆盖已经存在的机制。
如果你这样做
ChildObject.prototype = ParentObject.prototype;
那么你的parent和child有相同的原型。对于后者,
ChildObject.prototype = new ParentObject();
child的原型是parent本身。
两者都没有。
改为:
ChildObject.prototype = Object.create(ParentObject.prototype);
ChildObject.prototype.constructor = ChildObject;
结合ChildObject
开头的这一行:
ParentObject.call(this/*, appropriate args if any*/);
或者如果传递 all args:
ParentObject.apply(this, arguments); // `arguments` literally -- it's provided by the JavaScript engine
ChildObject.prototype = new ParentObject();
的问题在于,如果 ParentObject
需要参数,它就无法工作,就像许多(大多数?)构造函数那样。因此,该模式在常见(可能是大多数)情况下不起作用。
ChildObject.prototype = ParentObject.prototype;
的问题在于,您让它们都指向 同一个对象 。因此,在 ChildObject.prototype
上设置 属性 会使它显示在 ParentObject
的实例上(因为您刚刚也将其设置在 ParentObject.prototype
上)。
Object.create
版本推迟调用 ParentObject
直到你有一个对象要初始化,但给你一个对象,你可以在上面放置特定于 ChildObject
实例的东西。 (constructor
反向链接是为了使替换对象看起来像 ChildObject
最初的默认对象;这在过去并不重要,但现在在某些情况下 ES6 和一些库实际上使用了它即使 ES5 和更早版本没有使用过它[他们只是把它放在那里]。)
最后:Object.create
的单参数版本可以在 2009 年之前添加到 ES5 中时针对过时的浏览器进行填充,或者如果您不喜欢部分功能,则可以使用工厂函数代替-匀场:
function objectCreate(proto) {
function ctor() { }
ctor.prototype = proto;
return new ctor;
}
使用继承时,ChildObject.prototype = new ParentObject();
或 ChildObject.prototype = ParentObject.prototype;
之间有什么区别,哪种方法更好。
我在 Douglas Crockford 的例子中看到了 ChildObject.prototype = new ParentObject();
。
我们通常使用原型来提供方法 - 以避免为 class 的每个实例重复相同的函数。换句话说,对于类似...
ChildObject.prototype.foo = function() {
console.log('foo');
};
现在您可能看到了第二种方法的问题:修改 ChildObject.prototype
也会修改 ParentObject.prototype
(在 ChildObject.prototype = ParentObject.prototype
行之后是同一个对象)。这意味着此示例中的所有 ParentObject
都将有 foo
方法可供他们使用。实际上,说 ParentObject
是 ChildObject
的 parent 甚至是不正确的——两者具有相同的原型链。
然而还有另一个令人讨厌的副作用。考虑一下:
function ParentObj() {}
function ChildObj() {}
ChildObj.prototype = ParentObj.prototype;
var i = new ParentObj();
console.log(i); // ParentObj {}
var j = new ChildObj();
console.log(j); // ParentObj {}
看,两个原型,因为它们是同一个对象,具有相同的 constructor
属性。所以为了区分它们,我们必须在构造函数中引入一些标志,从根本上覆盖已经存在的机制。
如果你这样做
ChildObject.prototype = ParentObject.prototype;
那么你的parent和child有相同的原型。对于后者,
ChildObject.prototype = new ParentObject();
child的原型是parent本身。
两者都没有。
改为:
ChildObject.prototype = Object.create(ParentObject.prototype);
ChildObject.prototype.constructor = ChildObject;
结合ChildObject
开头的这一行:
ParentObject.call(this/*, appropriate args if any*/);
或者如果传递 all args:
ParentObject.apply(this, arguments); // `arguments` literally -- it's provided by the JavaScript engine
ChildObject.prototype = new ParentObject();
的问题在于,如果 ParentObject
需要参数,它就无法工作,就像许多(大多数?)构造函数那样。因此,该模式在常见(可能是大多数)情况下不起作用。
ChildObject.prototype = ParentObject.prototype;
的问题在于,您让它们都指向 同一个对象 。因此,在 ChildObject.prototype
上设置 属性 会使它显示在 ParentObject
的实例上(因为您刚刚也将其设置在 ParentObject.prototype
上)。
Object.create
版本推迟调用 ParentObject
直到你有一个对象要初始化,但给你一个对象,你可以在上面放置特定于 ChildObject
实例的东西。 (constructor
反向链接是为了使替换对象看起来像 ChildObject
最初的默认对象;这在过去并不重要,但现在在某些情况下 ES6 和一些库实际上使用了它即使 ES5 和更早版本没有使用过它[他们只是把它放在那里]。)
最后:Object.create
的单参数版本可以在 2009 年之前添加到 ES5 中时针对过时的浏览器进行填充,或者如果您不喜欢部分功能,则可以使用工厂函数代替-匀场:
function objectCreate(proto) {
function ctor() { }
ctor.prototype = proto;
return new ctor;
}