[英]If JavaScript doesn't support Classical inheritance why am I able to create constructors and use new keyword?
According to MDN javascript only support prototypical inheritance. 根据MDN javascript只支持原型继承。 Yet I can do the following:
但我可以做到以下几点:
function Human() {
this.eyes = 2;
this.feet = 2;
}
Var Mark = new Human();
What's even more confusing, I can add a method to the constructor using .prototype
keyword: 什么更令人困惑,我可以使用
.prototype
关键字向构造函数添加一个方法:
Human.prototype.walk = function(distance) {
//code
}
Yet there's a proper way to create objects using Object.Create which is apparently the proper prototype based object creation: 然而,有一种使用Object.Create创建对象的正确方法,这显然是基于原型的对象创建:
var Human = {
eyes: 2,
feets: 2
}
var Mark = Object.create(Human);
Can someone please clear this up for me? 有人可以帮我清楚一下吗? Thank you
谢谢
The first thing you should understand is that the snippet you've provided as an example is still prototypal inheritance, and here's why: 您应该首先理解的是,您提供的代码片段仍然是原型继承,这就是原因:
Human
is a function which contains a prototype
object. Human
是一个包含prototype
对象的函数。 Instances of Human
extend that prototype object with their own data initialized in the Human
constructor. Human
实例扩展了原型对象,并在Human
构造函数中初始化了自己的数据。 prototype
object can be modified at runtime. prototype
对象。 Even after you've created instances of the class, you can still modify their inherited behavior by adding or changing properties on the prototype
object. prototype
对象上添加或更改属性来修改其继承的行为。 None of this is possible with classical inheritance. new
keyword, but otherwise, can be treated like any other object. new
关键字调用,但除此之外,可以像任何其他对象一样对待。 Given this information, let's demonstrate a few key similarities and differences between Object.create()
and new
: 鉴于此信息,让我们演示
Object.create()
和new
之间的一些关键相似点和不同点:
function Human() { this.eyes = 2; this.feet = 2; } Human.prototype.walk = function () { }; var josh = new Human(); console.log(josh);
var human = { constructor: function Human() { this.eyes = 2; this.feet = 2; }, walk: function () { } }; // create josh with prototype of human var josh = Object.create(human); // initialize own properties by calling constructor human.constructor.call(josh); // or josh.constructor(); console.log(josh);
It may not look it at first, but these two snippets are actually creating an instance josh
with the exact same layout : 它一开始可能看起来不一样,但这两个片段实际上是创建一个具有完全相同布局的实例
josh
:
{
eyes: 2,
feet: 2,
__proto__: {
walk: f (),
constructor: f Human(),
__proto__: Object.prototype
}
}
That is to say: 也就是说:
var proto = Object.getPrototypeOf(josh);
var protoProto = Object.getPrototypeOf(proto);
console.log(proto === Human.prototype); // or proto === human
console.log(protoProto === Object.prototype);
<- true
<- true
This demonstrates the prototype chain of josh
. 这展示了
josh
的原型链 。 It is the path of inheritance that determines the behavior of the object, and shows that josh
inherits from Human
, which inherits from Object
. 它是继承的路径,决定了对象的行为,并表明
josh
继承自Human
,它继承自Object
。
The difference between the two Stack Snippet consoles above is due to the fact that the first snippet's constructor
is a non-enumerable property of Human.prototype
, while the second snippet's constructor
is an enumerable property of human
. 上面两个Stack Snippet控制台之间的区别是由于第一个片段的
constructor
是Human.prototype
的不可枚举属性,而第二个片段的constructor
是human
的可枚举属性。
If you want to pick apart the second snippet, I highly suggest taking a closer look at the documentation for Object.create()
on MDN, and possibly even glancing at the specification for it if you can grok the dense language. 如果你想拆开第二个片段,我强烈建议你仔细看看MDN上
Object.create()
的文档,如果你能学习密集的语言,甚至可能会看一下它的规范。
Here's how you can use Object.create()
with our definition of Human
instead: 以下是如何将
Object.create()
与我们对Human
的定义一起使用:
function Human() { this.eyes = 2; this.feet = 2; } Human.prototype.walk = function () { }; // create prototypal inheritance var josh = Object.create(Human.prototype); // initialize own properties Human.call(josh); // or josh.constructor(); console.log(josh);
This initializes the instance properties of the instance josh
by calling the ES5 constructor with josh
as the context (the this
keyword). 这通过使用
josh
作为上下文( this
关键字) 调用 ES5构造函数来初始化实例josh
的实例属性。
Lastly, since it was mentioned in comments, all of this can be abstracted for simplicity using the ES6 class
keyword, which still uses prototypal inheritance: 最后,由于在评论中提到过,所有这些都可以使用ES6
class
关键字进行抽象,这仍然使用原型继承:
class Human { constructor() { this.eyes = 2; this.feet = 2; } walk() { } } var josh = new Human(); console.log(josh);
The output may appear different but if you check in the real Developer Console, you'll find that the only difference in the layout of josh
is due to the fact that ES6 classes declare member methods like walk()
as non-enumerable properties of Human.prototype
, which is why it doesn't show up in the Stack Snippet console. 输出可能看起来不同但是如果你检查真正的开发者控制台,你会发现
josh
布局的唯一区别是由于ES6类声明像walk()
这样的成员方法作为Human.prototype
非可枚举属性Human.prototype
,这就是为什么它不会出现在Stack Snippet控制台中。
You cannot use Object.create()
the same way as demonstrated in ES5 because an ES6 class
is only constructable (invoke-able with new
) and not callable (invoke-able without new
): 你不能像在ES5中演示的那样使用
Object.create()
,因为ES6 class
只能构造 (可以使用new
调用)而不能调用 (不使用new
调用):
class Human { constructor() { this.eyes = 2; this.feet = 2; } walk() { } } var josh = Object.create(Human.prototype); // still works // no own properties yet because the constructor has not been invoked console.log(josh); // cannot call constructor to initialize own properties Human.call(josh); // josh.constructor(); would not work either
I tried to come up with a way to more easily see the prototype chain of objects in the Stack Snippet console, so I wrote this function layout()
. 我试图想出一种更容易在Stack Snippet控制台中看到对象原型链的方法,所以我写了这个函数
layout()
。 It recurses into an object's prototype chain and makes all the properties enumerable. 它可以递归到对象的原型链中,并使所有属性都可以枚举。 Since prototype chains cannot have cycles, this can never get stuck in infinite recursion:
由于原型链不能有循环,因此永远不会陷入无限递归:
// makes all properties in an object's prototype chain enumerable // don't worry about understanding this implementation function layout (o) { if (typeof o !== 'object' || !o || o === Object.prototype) return o; return [...Object.getOwnPropertyNames(o), '__proto__'].reduce( (a, p) => Object.assign(a, { [p]: layout(o[p]) }), Object.create(null) ); } // this is intentionally one line in order to // make Stack Snippet Console output readable function HumanES5() { this.eyes = 2; this.feet = 2; } HumanES5.prototype.walk = function () { }; var josh = new HumanES5(); console.log(layout(josh)); var josh = Object.create(HumanES5.prototype); HumanES5.call(josh); // or josh.constructor(); console.log(layout(josh)); class HumanES6 { constructor () { this.eyes = 2; this.feet = 2; } walk () { } } var josh = new HumanES6(); console.log(layout(josh)); var josh = Object.create(HumanES6.prototype); // HumanES6.call(josh); will fail, remember? console.log(layout(josh));
.as-console-wrapper{min-height:100%!important}
There are two things to note here. 这里有两点需要注意。
class HumanES6 { ... }
actually refers to the constructor
function in the class declaration. class HumanES6 { ... }
实际上是指类声明中的constructor
函数。 In prototypal inheritance, the class and its constructor are synonymous. eyes
and feet
since the constructor
was never invoked to initialize that instance of josh
. constructor
来初始化josh
实例,因此最后一个输出没有自己的属性eyes
和feet
。 You can use new
because the language specification defined it that way. 您可以使用
new
因为语言规范以这种方式定义它。 The creators of JavaScript could also have omitted the possibility of using the new
keyword or Object.create()
. JavaScript的创建者也可能忽略了使用
new
关键字或Object.create()
的可能性。
new
does not in and of itself suggest anything about inheritance; new
本身并没有提出有关继承的任何建议; it could also exist in languages with no inheritance at all. 它也可以存在于没有继承的语言中。 It just happens to be a keyword to create a new "object" in JavaScript.
它恰好是在JavaScript中创建新“对象”的关键字。
And depending on the language, new
has different meanings. 根据语言的不同,
new
有不同的含义。 It could just define the creation of a new object, but could also include the meaning of where/how to allocate the memory, and/or about what is responsible for the memory lifecycle. 它可以只定义新对象的创建,但也可以包括在何处/如何分配内存的含义,和/或负责内存生命周期的内容。
A classical inheritance based language could work without a new
keyword at all. 基于经典继承的语言可以在没有
new
关键字的情况下工作。 Or it could have a deprecated new
keyword in favor of a better way to create objects in a newer version of the language. 或者它可能有一个已弃用的
new
关键字,支持更好的方法来在较新版本的语言中创建对象。
You could conceive various ways of creating a new object from a descriptor: 您可以设想从描述符创建新对象的各种方法:
new Descriptor(arg1, arg2);
Descriptor obj(arg1, arg2);
obj = Descriptor.alloc().init(arg1, arg2);
obj = Descriptor.new(arg1, arg2);
obj = create(Descriptor, arg1, arg2);
...
All of those could have slightly different meanings in different languages. 所有这些在不同语言中的含义可能略有不同。 So you should not bother too much if one language borrows a keyword or concept from another language, because most of the time they differ in minor (or even critical) details.
因此,如果一种语言借用另一种语言的关键词或概念,你就不应该费心,因为大多数时候它们在次要(甚至是关键)细节上都有所不同。
So use your previous knowledge to aid in learning the new language, but don't try too hard to perfectly synonymize these concepts between different languages. 因此,请使用您以前的知识来帮助学习新语言,但不要太努力,以便在不同语言之间完美地同义这些概念。 You have to keep in mind that other languages have different concepts even if they look similar.
您必须记住,即使它们看起来相似,其他语言也有不同的概念。 Therefore, it is often helpful to simply accept it as given by the specs.
因此,按照规范给出的简单接受通常是有帮助的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.