我正在读Kyle Simpson" YDKJS:这个&对象原型",并查看他的行为委托示例。这是以下代码:
Foo = {
init: function(who) {
this.me = who;
},
identify: function() {
return "I am " + this.me;
}
};
Bar = Object.create( Foo );
Bar.speak = function() {
alert( "Hello, " + this.identify() + "." );
};
var b1 = Object.create( Bar );
b1.init( "b1" );
var b2 = Object.create( Bar );
b2.init( "b2" );
b1.speak();
b2.speak();
他使用此图来描述这些对象之间的关系:
现在我的问题:
在Chrome开发者工具中修改此代码时,我发现__proto__
对象的评估结果为Object
。这是为什么?不应该__proto__
对象描绘图表中的实际关系,其中b1
和b2
委托给Bar
,Foo
转而委托给__proto__
?为什么这些关系没有在Object.create()
对象中明确命名?总而言之,此实现使用proto
方法,并在__proto__
参数(第一个参数)中放置一个参数。是不是期望控制台将该命名参数作为CALIBRATED
对象的值返回?
答案 0 :(得分:2)
这不是那么简单。 __proto__
属性 是.constructor
原型的动态链接。
但是,.constructor
在这些情况下不会被覆盖(尽管它可以在某些手工制作的库中)。
当您正在寻找对Foo
显示在该链中的引用时,它是一个非常“经典”的观点,与原型继承关系不大,原型继承是从具体实例继承的,而不是类,在JS的情况下,引用(当不是标量时),而不是副本。
tl; dr原因很简单:Object.create
的构造函数是Object
,{}
的构造函数是Object
,而Object.prototype
的构造函数是Object
。
首先要知道的是:
这些查找中的名称与变量无关,而是通常与函数相关联
曾几何时,可以找到可以找到此信息的字段,现在发现它现在可以在许多浏览器中找到函数.name
的属性(function Foo () { } Foo.name; //Foo
)(很像{{1以前是不可见的。
第二位:
您在控制台引用和类型检查中看到的名称不是基于 proto ,而是基于名为__proto__
的属性。
.constructor
如果我们要稍微改变一下动态,并为实例创建引入更“经典”的方法,那就是paseé,但会看到ES6中的// Equivalent assignments
var obj1 = { };
var obj2 = new Object();
var obj3 = Object.create(Object.prototype);
obj1.constructor; // Object
obj2.constructor; // Object
obj3.constructor; // Object
糖复活......
class
最后一点有说明问题,直截了当的问题; function Foo () {
this.isFoo = true;
}
var foo = new Foo();
foo.isFoo; // true
foo.constructor; // Foo
foo.__proto__ === Foo.prototype; // true
foo.__proto__.constructor; // Object
只不过是一个简单的“Foo.prototype
实例。”
在JS的日子里,每个人都在寻找让JS感觉像Java / C#的最佳方式,你会看到类似的东西:
Object
一方面,这种作品,因为我们已经能够在Bar的实例上重用Foo的方法。
大。
直接的缺点是function Foo () { }
function Bar() { }
Bar.prototype = new Foo();
var foo = new Foo();
var bar = new Bar();
bar instanceof Bar; // true
bar instanceof Foo; // true
的所有实例共享Bar
的同一个实例,包括其所有特定于实例的属性。
虽然这种方法远非我建议的重用模式,但它确实很好地展示了你的困境。
Foo
更多现代形式的“经典”继承(调用 // using the last set of assignments
bar instanceof Foo;
// equivalent to:
bar.constructor === Foo; // false
bar.__proto__.constructor === Foo; // true
// not equivalent to
bar.__proto__ == Foo;
构造函数,将其他构造函数的原型复制到新构造函数上等)可以更好地组合和重用方法...
但这是以能够依赖super
链查找借出方法来源的成本为代价的。
从历史上看,“重用不好,有利于类型检查”模式让您搜索
__proto__
直到它与您的val.__proto__.__proto__.__proto__.__proto__. (...) .__proto__.constructor
匹配,或直到您在该行的末尾点击instanceof
。
较新的表单将值直接复制到Object
(/ val.__proto__
,我们在创建val.constructor.prototype
时看到的是同一个对象,这意味着你的原型链用完了很快。