Javascript: 还是被 instanceof 运算符搞糊涂了
Javascript: Still confused by the instanceof operator
这个 article 定义 instanceof 如下:
The instanceof operator tests whether an object has in its prototype
chain the prototype property of a constructor.
这是一个公平的解释,生活很美好,直到我从书中看到这段代码 Eloquent Javascript:
function TextCell(text) {
this.text = text.split("\n");
}
TextCell.prototype.minWidth = function() {
return this.text.reduce(function(width, line) {
return Math.max(width, line.length);
}, 0);
}
TextCell.prototype.minHeight = function() {
return this.text.length;
}
TextCell.prototype.draw = function(width, height) {
var result = [];
for (var i = 0; i < height; i++) {
var line = this.text[i] || "";
result.push(line + repeat(" ", width - line.length));
}
return result;
}
function RTextCell(text) {
TextCell.call(this, text);
}
RTextCell.prototype = Object.create(TextCell.prototype);
RTextCell.prototype.draw = function(width, height) {
var result = [];
for (var i = 0; i < height; i++) {
var line = this.text[i] || "";
result.push(repeat(" ", width - line.length) + line);
}
return result;
};
让我们创建一个 RTextCell 实例并执行下面的 c
var rt = new RTextCell("ABC");
console.log(rt instanceof RTextCell); // true
console.log(rt instanceof TextCell); // true
我明白为什么第二个 console.log 的输出是 "true" - 因为构造函数 TextCell 是原型链的一部分。
但是第一个 console.log 让我很困惑。
如果您查看代码(从底部开始第 10 行),RTextCell 的原型将更新为一个新的对象,其原型设置为 TextCell.prototype。
RTextCell.prototype = Object.create(TextCell.prototype);
.
查看下面的快照,在对象 "rt" 的原型链中没有提到构造函数 "RTextCell"。那么,按照我在 post 开头提到的定义,输出不应该是假的吗?为什么它 returns 是一个真值?
我也读过 this 但没有帮助我理解这个具体问题。
按顺序查看 rt、RTextCell、TextCell 的快照。
obj instanceof RTextCell
正在测试obj的原型链中是否存在RTextCell.prototype。这是因为 obj 是使用新的 RTextCell 制作的。 RTextCell.prototype 具有原型 TextCell.prototype 的事实与这里的重点无关,似乎让您感到困惑。
RTextCell.prototype = Object.create(TextCell.prototype);
没有去掉RTextCell.prototype,它还是一个对象,只是它的原型恰好是TextCell.prototype.
准确的措辞很重要。你说 constructor 在原型链中,但原来的引用没有:
The instanceof operator tests whether an object has in its prototype
chain the prototype property of a constructor.
所以表达式 rt instanceof RTextCell
实际上是在测试这样的东西(记住 __proto__
不是标准的):
var p = rt.__proto__;
while(p)
{
if(p == RTextCell.prototype)
return true;
p = p.__proto__;
}
return false;
所以即使在上面的对象树中没有直接引用函数 RTextCell
,RTextCell.prototype
对象是。
您确实更改了 RTextCell.prototype
,但是您在构建任何 RTextCell
实例之前更改了它。考虑这个截然不同的示例,其中 RTextCell.prototype
在使用原始原型创建实例后被修改:
var rt = new RTextCell();
RTextCell.prototype = somethingTotallyDifferent;
rt instanceof RTextCell; // false!
创建rt
时,rt.__proto__ === RTextCell.prototype
为真。一旦 RTextCell.prototype
改变,那就不再是真的了。
您不是在测试 rt
是否在其原型链中具有来自 RTextCell
的 原始 prototype
属性。相反,您正在测试 RTextCell.prototype
现在 的值是否存在于对象的原型链中。 RTextCell
实例总是如此,因为由 RTextCell
构造函数创建的实例总是在它们的原型链中获得 RTextCell.prototype
的当前值,并且你永远不会在之后更改 RTextCell.prototype
你开始构建实例。
这个 article 定义 instanceof 如下:
The instanceof operator tests whether an object has in its prototype chain the prototype property of a constructor.
这是一个公平的解释,生活很美好,直到我从书中看到这段代码 Eloquent Javascript:
function TextCell(text) {
this.text = text.split("\n");
}
TextCell.prototype.minWidth = function() {
return this.text.reduce(function(width, line) {
return Math.max(width, line.length);
}, 0);
}
TextCell.prototype.minHeight = function() {
return this.text.length;
}
TextCell.prototype.draw = function(width, height) {
var result = [];
for (var i = 0; i < height; i++) {
var line = this.text[i] || "";
result.push(line + repeat(" ", width - line.length));
}
return result;
}
function RTextCell(text) {
TextCell.call(this, text);
}
RTextCell.prototype = Object.create(TextCell.prototype);
RTextCell.prototype.draw = function(width, height) {
var result = [];
for (var i = 0; i < height; i++) {
var line = this.text[i] || "";
result.push(repeat(" ", width - line.length) + line);
}
return result;
};
让我们创建一个 RTextCell 实例并执行下面的 c
var rt = new RTextCell("ABC");
console.log(rt instanceof RTextCell); // true
console.log(rt instanceof TextCell); // true
我明白为什么第二个 console.log 的输出是 "true" - 因为构造函数 TextCell 是原型链的一部分。
但是第一个 console.log 让我很困惑。
如果您查看代码(从底部开始第 10 行),RTextCell 的原型将更新为一个新的对象,其原型设置为 TextCell.prototype。
RTextCell.prototype = Object.create(TextCell.prototype);
.
查看下面的快照,在对象 "rt" 的原型链中没有提到构造函数 "RTextCell"。那么,按照我在 post 开头提到的定义,输出不应该是假的吗?为什么它 returns 是一个真值?
我也读过 this 但没有帮助我理解这个具体问题。
按顺序查看 rt、RTextCell、TextCell 的快照。
obj instanceof RTextCell
正在测试obj的原型链中是否存在RTextCell.prototype。这是因为 obj 是使用新的 RTextCell 制作的。 RTextCell.prototype 具有原型 TextCell.prototype 的事实与这里的重点无关,似乎让您感到困惑。
RTextCell.prototype = Object.create(TextCell.prototype);
没有去掉RTextCell.prototype,它还是一个对象,只是它的原型恰好是TextCell.prototype.
准确的措辞很重要。你说 constructor 在原型链中,但原来的引用没有:
The instanceof operator tests whether an object has in its prototype chain the prototype property of a constructor.
所以表达式 rt instanceof RTextCell
实际上是在测试这样的东西(记住 __proto__
不是标准的):
var p = rt.__proto__;
while(p)
{
if(p == RTextCell.prototype)
return true;
p = p.__proto__;
}
return false;
所以即使在上面的对象树中没有直接引用函数 RTextCell
,RTextCell.prototype
对象是。
您确实更改了 RTextCell.prototype
,但是您在构建任何 RTextCell
实例之前更改了它。考虑这个截然不同的示例,其中 RTextCell.prototype
在使用原始原型创建实例后被修改:
var rt = new RTextCell();
RTextCell.prototype = somethingTotallyDifferent;
rt instanceof RTextCell; // false!
创建rt
时,rt.__proto__ === RTextCell.prototype
为真。一旦 RTextCell.prototype
改变,那就不再是真的了。
您不是在测试 rt
是否在其原型链中具有来自 RTextCell
的 原始 prototype
属性。相反,您正在测试 RTextCell.prototype
现在 的值是否存在于对象的原型链中。 RTextCell
实例总是如此,因为由 RTextCell
构造函数创建的实例总是在它们的原型链中获得 RTextCell.prototype
的当前值,并且你永远不会在之后更改 RTextCell.prototype
你开始构建实例。