安全地继承JavaScript中的原型
Safely inheriting prototypes in JavaScript
假设我正在为应用程序中的一些基本继承而努力,我可以通过将孩子的原型设置为父对象来实现这一点。
// Parent "class"
var Car = function(year) {
this.year = year;
};
Car.prototype.drive = function() {
console.log('Vrooom');
};
// Child "class"
var Toyota = function() {
Car.apply(this, arguments);
};
Toyota.prototype = Car.prototype;
var myCar = new Car(2010), myToyota = new Toyota(2010);
myCar.drive(); // Vrooom
myToyota.drive(); // Vrooom
似乎有效,但显然很糟糕,因为如果我在Toyota.prototype
上设置一个新方法,它将在Car
的原型上设置。
Toyota.prototype.stop = function() {
console.log('stooop');
};
myCar.stop(); // stooop <--- bad
myToyota.stop(); // stooop <--- good
为了解决这个问题,我可以添加一个中介:,而不是Toyota.prototype = Car.prototype;
var ctor = function() { };
ctor.prototype = Car.prototype;
Toyota.prototype = new ctor();
Toyota.prototype.stop = function() {
console.log('stooop');
};
myCar.stop(); // throws undefined error <--- good
myToyota.stop(); // stooop <--- good
但我不明白为什么这样有效。ctor
创建一个新实例,其原型设置为Car's
原型,然后Toyota
将其原型设置为此新实例。
但是,为什么要创建一个空对象,其中包含一个被Toyota
的原型引用的原型呢
为什么在Toyota
上设置一个新方法不像在第一个示例中那样在Car
上设置它?如果我想要几层继承,似乎每次都需要用new ctor
将它们粘合在一起,该怎么办?
但是,为什么要创建一个带有被引用的原型的空对象呢丰田的原型?
因为如果你这样做:Toyota.prototype = new Car();
现在Toyota的原型是Car实例,这意味着除了链中有Car原型外,它还具有Car构造函数本身中设置的任何属性。基本上,您不希望Toyota.protype有一个类似year
的属性:Toyota.prototype.year
。正因为如此,有一个空的构造函数要好得多,比如:
var ctor = function() { };
ctor.prototype = Car.prototype;
Toyota.prototype = new ctor();
现在Toyota.protype将new ctor()
实例作为其原型,而Car.prototype
又在其自己的链中。这意味着丰田的实例现在可以执行汽车原型中存在的方法。
为什么不在丰田上设置一个新方法,就像在Car中设置一样在第一个例子中?
通过这个:Toyota.prototype = Car.prototype;
,您将丰田的原型设置为与Car.prototype
所包含的对象完全相同的对象。由于它是同一个对象,在一个地方更改它也会在其他地方更改它。这意味着对象是通过引用而不是通过值传递的,这是另一种说法,即当您将一个对象分配给3个不同的变量时,无论您使用哪个变量,它都是同一个对象。例如,字符串是按值传递的。下面是一个字符串示例:
var str1 = 'alpha';
var str2 = str1;
var str3 = str1;
str2 = 'beta';
// Changing str2 doesn't change the rest.
console.log(str1); //=> "alpha"
console.log(str3); //=> "alpha"
console.log(str2); //=> "beta"
现在比较对象及其属性;
var obj1 = {name: 'joe doe'};
var obj2 = obj1;
var obj3 = obj1;
console.log(obj1.name); //=> "joe doe"
console.log(obj2.name); //=> "joe doe"
console.log(obj3.name); //=> "joe doe"
obj2.name = 'JOHN SMITH';
console.log(obj1.name); //=> "JOHN SMITH"
console.log(obj2.name); //=> "JOHN SMITH"
console.log(obj3.name); //=> "JOHN SMITH"
如果我想要几层继承呢。。。
这里有另一种创建继承层的方法:
var Car = function(year) {
this.year = year;
};
Car.prototype.drive = function() {
console.log('Vrooom');
};
var Toyota = function() {
Car.apply(this, arguments);
};
// `Object.create` does basically the same thing as what you were doing with `ctor()`
// Check out the documentation for `Object.create` as it takes a 2nd argument too.
Toyota.prototype = Object.create(Car.prototype);
// Create instances
var
myCar = new Car(2010),
myToyota = new Toyota(2010);
// Add method to Toyota's prototype
Toyota.prototype.stop = function() {
console.log('stooop');
};
让我们现在试试:
myToyota.stop(); //=> 'stooop'
myCar.stop(); //=> 'TypeError: undefined is not a function'
myCar.drive(); //=> Vrooom
myToyota.drive(); //=> Vrooom
您的问题是以下行:
Toyota.prototype = Car.prototype;
然后修改这个对象:
Toyota.prototype.stop = function() {
console.log('stooop');
};
因为在第一行中,您将Toyota.prototype
设置为与Car.prototype
完全相同的对象这不是副本,而是对同一对象的引用因此,一旦在Toyota.prototype
上修改stop
,实际上就同时修改了Toyota.prototype
和Car.prototype
,因为它是一个相同的。
你真正想做的是将第一行替换为:
Toyota.prototype = Object.create(Car);
因此,现在Toyota
的原型指向Car
函数,而应该指向Car
自己的prototype
。祝贺你,你已经使用了原型链!(注意:使用Object.create
进行类继承本质上更稳定可靠,因为它不运行Car
函数中可能包含的任何代码,而只设置原型链接。)
为了进一步讨论这里到底发生了什么,以及为什么你最好而不是在JS中使用"类继承",你可能想阅读《你不知道JS》(对象和原型)的第4-6章。
关于你的最后一个问题:"如果我想要几层继承,似乎每次都需要用一个新的ctor把它们粘在一起怎么办?"——是的,你需要这样做,这是JavaScript中(假)类和继承的标准模式。
- 使用“;这个“;JavaScript原型方法中的关键字
- 如何从对象的原型方法访问JavaScript对象属性
- Node.js中的JavaScript原型对象效率
- 为什么要返回'这'在导致循环的JavaScript原型中
- 原型和用法 Javascript
- JavaScript对象不是从原型链继承的
- javascript对象原型与jquery冲突
- 原型Javascript中的错误“;类别“-不是函数和未定义的变量
- 从字符串原型javascript获取字符串值
- 原型JavaScript Event.observe-如何观察可能存在或不存在的元素
- 与原型javascript冲突
- 为什么原型JavaScript在这种情况下不起作用
- 使用原型JavaScript - 最佳实践
- 添加到另一个原型实例的原型:JavaScript
- 函数.原型Javascript
- 如何在选定的原型javascript中更新基于第一选择框的第二选择
- 原型Javascript框架-获取PHP响应
- 如何访问类原型Javascript中定义的事件处理程序中的类成员变量
- 原型 javascript 不显眼的点击事件添加到按钮
- 字符串vs数组原型Javascript