[英]Clarifying javascript prototype nomenclature and mechanism
Recognizing that JavaScript doesn't have the concept of class per se, and that the "type" of all objects is "object", I'm trying to get my head around just what a "prototype" consists of and, in particular, how its "name" is associated with it. 认识到JavaScript没有类本身的概念,并且所有对象的“类型”都是“对象”,我试图了解“原型”包含的内容,特别是它的“名称”如何与之相关联。 For example, in the following: 例如,在以下内容中:
function Foo(){};
console.log(Foo.prototype); // => "Foo {}"
How does console.log
know to output Foo
before the braces and what is that name referring to? console.log
如何知道在大括号之前输出Foo
以及该名称是指什么?
(Note: I'm aware that in the above, I'm referring to the prototype property of functions and not the prototype per se (ie not the thing accessible by __proto__
), but the same question applies to the actual prototype objects. I just used the prototype property to simplify my example.) (注意:我知道在上面,我指的是函数的原型属性而不是原型本身(即不是__proto__
可以访问的东西),但同样的问题适用于实际的原型对象。只是使用了prototype属性来简化我的例子。)
Update: Based on the comment thread, this question is really focused on what Chrome is doing and, in particular, rationalizing its behavior in the following: 更新:基于评论主题,这个问题实际上集中在Chrome正在做什么,特别是在以下方面合理化其行为:
function Foo(){};
Foo.prototype.constructor = function Bar(){};
f = new Foo();
console.log(f); // => Foo{} (remembering that f created by Foo, ignoring constructor)
console.log(Foo.prototype) // => Bar{} (reporting constructor value)
See https://gist.github.com/getify/5793213 for more discussion. 有关更多讨论,请参阅https://gist.github.com/getify/5793213 。
JavaScript has a very twisted form of prototypal inheritance. JavaScript具有非常扭曲的原型继承形式。 I like to call it the constructor pattern of prototypal inheritance . 我喜欢称它为原型继承的构造函数模式 。 There is another pattern of prototypal inheritance as well - the prototypal pattern of prototypal inheritance . 还有另一种原型继承模式 - 原型继承的原型模式 。 I'll explain the latter first. 我先解释后者。
In JavaScript objects inherit from objects. 在JavaScript中,对象继承自对象。 There's no need for classes. 没有必要上课。 This is a good thing. 这是一件好事。 It makes life easier. 它让生活更轻松。 For example say we have a class for lines: 例如,假设我们有一个行类:
class Line {
int x1, y1, x2, y2;
public:
Line(int x1, int y1, int x2, int y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
int length() {
int dx = x2 - x1;
int dy = y2 - y1;
return sqrt(dx * dx + dy * dy);
}
}
Yes, this is C++. 是的,这是C ++。 Now that we created a class we may now create objects: 现在我们创建了一个类,现在我们可以创建对象:
Line line1(0, 0, 0, 100);
Line line2(0, 100, 100, 100);
Line line3(100, 100, 100, 0);
Line line4(100, 0, 0, 0);
These four lines form a square. 这四条线形成一个正方形。
JavaScript doesn't have any classes. JavaScript没有任何类。 It has prototypal inheritance. 它具有原型继承。 If you wanted to do the same thing using the prototypal pattern you would do this: 如果你想使用原型模式做同样的事情,你会这样做:
var line = {
create: function (x1, y1, x2, y2) {
var line = Object.create(this);
line.x1 = x1;
line.y1 = y1;
line.x2 = x2;
line.y2 = y2;
return line;
},
length: function () {
var dx = this.x2 - this.x1;
var dy = this.y2 - this.y1;
return Math.sqrt(dx * dx + dy * dy);
}
};
Then you create instances of the object line
as follows: 然后,您创建对象line
实例,如下所示:
var line1 = line.create(0, 0, 0, 100);
var line2 = line.create(0, 100, 100, 100);
var line3 = line.create(100, 100, 100, 0);
var line4 = line.create(100, 0, 0, 0);
That's all there is to it. 这里的所有都是它的。 No confusing constructor functions with prototype
properties. 没有令人困惑的构造函数与prototype
属性。 The only function needed for inheritance is Object.create
. 继承所需的唯一功能是Object.create
。 This function takes an object (the prototype) and returns another object which inherits from the prototype. 此函数接受一个对象(原型)并返回另一个继承自原型的对象。
Unfortunately, unlike Lua, JavaScript endorses the constructor pattern of prototypal inheritance which makes it more difficult to understand prototypal inheritance. 不幸的是,与Lua不同,JavaScript支持原型继承的构造函数模式,这使得理解原型继承更加困难。 The constructor pattern is the inverse of the prototypal pattern. 构造函数模式是原型模式的反转。
The above program would look like this when written using the constructor pattern: 使用构造函数模式编写时,上面的程序看起来像这样:
function Line(x1, y1, x2, y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
Line.prototype.length = function () {
var dx = this.x2 - this.x1;
var dy = this.y2 - this.y1;
return Math.sqrt(dx * dx + dy * dy);
};
You may now create instances of Line.prototype
as follows: 您现在可以创建Line.prototype
实例,如下所示:
var line1 = new Line(0, 0, 0, 100);
var line2 = new Line(0, 100, 100, 100);
var line3 = new Line(100, 100, 100, 0);
var line4 = new Line(100, 0, 0, 0);
Notice the similarity between the constructor pattern and the prototypal pattern? 注意构造函数模式和原型模式之间的相似性?
create
method. 在原型模式中,我们只需创建一个具有create
方法的对象。 In the constructor pattern we create a function and JavaScript automatically creates a prototype
object for us. 在构造函数模式中,我们创建了一个函数,JavaScript自动为我们创建了一个prototype
对象。 create
and length
. 在原型模式中,我们有两种方法 - create
和length
。 In the constructor pattern too we have two methods - constructor
and length
. 在构造函数模式中,我们也有两个方法 - constructor
和length
。 The constructor pattern is the inverse of the prototypal pattern because when you create a function JavaScript automatically creates a prototype
object for the function. 构造函数模式是原型模式的反转,因为当您创建函数时,JavaScript会自动为函数创建prototype
对象。 The prototype
object has a property called constructor
which points back to the function itself : prototype
对象有一个名为constructor
的属性,它指向函数本身 :
As Eric said, the reason console.log
knows to output Foo
is because when you pass Foo.prototype
to console.log
: 正如Eric所说, console.log
知道输出Foo
的原因是因为当你将Foo.prototype
传递给console.log
:
Foo.prototype.constructor
which is Foo
itself. 它找到了Foo.prototype.constructor
,它本身就是Foo
。 name
. JavaScript中的每个命名函数都有一个名为name
的属性。 Foo.name
is "Foo"
. 因此Foo.name
是"Foo"
。 So it finds the string "Foo"
on Foo.prototype.constructor.name
. 所以它在Foo.prototype.constructor.name
上找到字符串"Foo"
。 Edit: Alright, I understand that you have a problem with the redefining the prototype.constructor
property in JavaScript. 编辑:好的,我知道您在JavaScript中重新定义prototype.constructor
属性时遇到问题。 To understand the problem let's first understand how the new
operator works. 要理解这个问题,我们首先要了解new
运算符的工作原理。
new
keyword before a constructor JS creates a new object. 当我们在构造函数JS创建新对象之前使用new
关键字创建实例时。 [[proto]]
property of this new object is set to point to whatever constructor.prototype
points to at the time of object creation . 此新对象的内部[[proto]]
属性设置为指向在创建对象时指向的constructor.prototype
。 What does this imply? 这意味着什么? Consider the following program: 考虑以下程序:
function Foo() {}
function Bar() {}
var foo = new Foo;
Foo.prototype = Bar.prototype;
var bar = new Foo;
alert(foo.constructor.name); // Foo
alert(bar.constructor.name); // Bar
See the output here: http://jsfiddle.net/z6b8w/ 请参阅此处的输出: http : //jsfiddle.net/z6b8w/
foo
inherits from Foo.prototype
. 实例foo
继承自Foo.prototype
。 foo.constructor.name
displays "Foo"
. 因此foo.constructor.name
显示"Foo"
。 Foo.prototype
to Bar.prototype
. 然后我们将Foo.prototype
设置为Bar.prototype
。 bar
inherits from Bar.prototype
although it was created by new Foo
. 因此bar
继承自Bar.prototype
尽管它是由new Foo
创建的。 bar.constructor.name
is "Bar"
. 因此bar.constructor.name
是"Bar"
。 In the JS fiddle you provided you created a function Foo
and then set Foo.prototype.constructor
to function Bar() {}
: 在你提供的JS小提示中 ,你创建了一个函数Foo
,然后将Foo.prototype.constructor
设置为function Bar() {}
:
function Foo() {}
Foo.prototype.constructor = function Bar() {};
var f = new Foo;
console.log(f.hasOwnProperty("constructor"));
console.log(f.constructor);
console.log(f);
Because you modified a property of Foo.prototype
every instance of Foo.prototype
will reflect this change. 因为你修改的属性Foo.prototype
的每个实例Foo.prototype
将反映这一变化。 Hence f.constructor
is function Bar() {}
. 因此f.constructor
是function Bar() {}
。 Thus f.constructor.name
is "Bar"
, not "Foo"
. 因此f.constructor.name
是"Bar"
,而不是"Foo"
。
See it for yourself - f.constructor.name
is "Bar"
. 亲自看看 - f.constructor.name
是"Bar"
。
Chrome is known to do weird things like that. 众所周知,Chrome会做出类似奇怪的事情。 What's important to understand is that Chrome is a debugging utility and console.log
is primarily used for debugging purposes. 重要的是要理解Chrome是一个调试实用程序, console.log
主要用于调试目的。
Hence when you create a new instance Chrome probably records the original constructor in an internal property which is accessed by console.log
. 因此,当您创建新实例时,Chrome可能会将原始构造函数记录在console.log
访问的内部属性中。 Thus it displays Foo
, not Bar
. 因此它显示Foo
,而不是Bar
。
This is not actual JavaScript behavior. 这不是实际的JavaScript行为。 According to the specification when you overwrite the prototype.constructor
property there's no link between the instance and the original constructor. 根据规范,当你覆盖prototype.constructor
属性时,实例和原始构造函数之间没有链接。
Other JavaScript implementations (like the Opera console, node.js and RingoJS) do the right thing and display Bar
. 其他JavaScript实现(如Opera控制台,node.js和RingoJS)做正确的事情并显示Bar
。 Hence Chrome's behavior is non-standard and browser-specific, so don't panic. 因此Chrome的行为是非标准的,特定于浏览器,所以不要惊慌。
What's important to understand is that even though Chrome displays Foo
instead of Bar
the constructor
property of the object is still function Bar() {}
as with other implementations: 重要的是要理解,即使Chrome显示Foo
而不是Bar
,对象的constructor
属性仍然是function Bar() {}
,与其他实现一样:
The constructor
property (which refers to a function originally used as a generator of the corresponding objects) is used to give a name to a prototype
object in the console log. constructor
属性(引用最初用作相应对象的生成器的函数)用于为控制台日志中的prototype
对象指定名称。 Consider the following: 考虑以下:
function Foo() {
this.x = 1;
}
console.log(Foo.prototype); // Foo {}
Foo.prototype.constructor = function Bar() {
this.y = 2
}
console.log(Foo.prototype); // Bar {}
var f = new Foo();
console.log(f.constructor); // function Bar() { this.y = 2}
console.log(f.x); // 1
console.log(f.y); // undefined
console.log(f); // Foo {x:1}
Here we've switched constructor
to another function, giving a new name to prototype
object. 在这里,我们将constructor
函数切换到另一个函数,为prototype
对象赋予一个新名称。 Note that the same function is returned when constructor
property is queried directly from an object , created with Foo() function (as we go up the inheritance chain). 请注意,直接从使用Foo()函数创建的对象查询constructor
属性时会返回相同的函数(因为我们继承了继承链)。
Still, it doesn't mean that another function ( Bar()
) was actually used to create the corresponding objects; 但是,这并不意味着另一个函数( Bar()
)实际上用于创建相应的对象; it's still Foo()
, and you can see it both by querying properties -- and f
directly. 它仍然是Foo()
,你可以通过查询属性 - 和f
直接看到它。 Basically, objects remember the function that was used to create them, even if constructor
property of prototype
was "redirected". 基本上,对象记住用于创建它们的函数,即使prototype
constructor
属性被“重定向”。
function Foo(){};
Working down the chain: 在链条上工作:
console.log(Foo.prototype);
console.log(Foo.prototype.constructor);
console.log(Foo.prototype.constructor.name);
Took a bit of digging online but I found this article that really illustrates how prototypes and other key core javascript functionality works: 在网上进行了一些挖掘,但我发现这篇文章真正说明了原型和其他关键核心JavaScript功能的工作原理:
http://dmitrysoshnikov.com/ecmascript/javascript-the-core/ http://dmitrysoshnikov.com/ecmascript/javascript-the-core/
I particularly like the diagram on how the prototype chain looks like. 我特别喜欢关于原型链如何看起来的图表。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.