JavaScript .prototype如何工作?

Joh*_*ren 1988 javascript dynamic-languages prototype-oriented

我不是那种动态编程语言,但是我写了很多JavaScript代码.我从来没有真正了解这个基于原型的编程,有没有人知道这是如何工作的?

var obj = new Object();
obj.prototype.test = function() { alert('Hello?'); };
var obj2 = new obj();
obj2.test();
Run Code Online (Sandbox Code Playgroud)

我记得很久以前我和人们进行了很多讨论(我不确定我在做什么)但是据我所知,没有一个类的概念.它只是一个对象,这些对象的实例是原始的克隆,对吧?

但是JavaScript中这个".prototype"属性的确切目的是什么?它与实例化对象有什么关系?

更新:正确的方式

var obj = new Object(); // not a functional object
obj.prototype.test = function() { alert('Hello?'); }; // this is wrong!

function MyObject() {} // a first class functional object
MyObject.prototype.test = function() { alert('OK'); } // OK
Run Code Online (Sandbox Code Playgroud)

这些幻灯片也非常有帮助.

sti*_*vlo 1791

在实现Java,C#或C++等经典继承的语言中,您首先要创建一个类 - 对象的蓝图 - 然后您可以从该类创建新对象,或者您可以扩展该类,定义一个增强的新类原班.

在JavaScript中,您首先创建一个对象(没有类的概念),然后您可以扩充自己的对象或从中创建新对象.这并不困难,但对于习惯于经典方式的人来说,有点外来并难以代谢.

例:

//Define a functional object to hold persons in JavaScript
var Person = function(name) {
  this.name = name;
};

//Add dynamically to the already defined object a new getter
Person.prototype.getName = function() {
  return this.name;
};

//Create a new object of type Person
var john = new Person("John");

//Try the getter
alert(john.getName());

//If now I modify person, also John gets the updates
Person.prototype.sayMyName = function() {
  alert('Hello, my name is ' + this.getName());
};

//Call the new method on john
john.sayMyName();
Run Code Online (Sandbox Code Playgroud)

到目前为止,我一直在扩展基础对象,现在我创建了另一个对象,然后从Person继承.

//Create a new object of type Customer by defining its constructor. It's not 
//related to Person for now.
var Customer = function(name) {
    this.name = name;
};

//Now I link the objects and to do so, we link the prototype of Customer to 
//a new instance of Person. The prototype is the base that will be used to 
//construct all new instances and also, will modify dynamically all already 
//constructed objects because in JavaScript objects retain a pointer to the 
//prototype
Customer.prototype = new Person();     

//Now I can call the methods of Person on the Customer, let's try, first 
//I need to create a Customer.
var myCustomer = new Customer('Dream Inc.');
myCustomer.sayMyName();

//If I add new methods to Person, they will be added to Customer, but if I
//add new methods to Customer they won't be added to Person. Example:
Customer.prototype.setAmountDue = function(amountDue) {
    this.amountDue = amountDue;
};
Customer.prototype.getAmountDue = function() {
    return this.amountDue;
};

//Let's try:       
myCustomer.setAmountDue(2000);
alert(myCustomer.getAmountDue());
Run Code Online (Sandbox Code Playgroud)

var Person = function (name) {
    this.name = name;
};
Person.prototype.getName = function () {
    return this.name;
};
var john = new Person("John");
alert(john.getName());
Person.prototype.sayMyName = function () {
    alert('Hello, my name is ' + this.getName());
};
john.sayMyName();
var Customer = function (name) {
    this.name = name;
};
Customer.prototype = new Person();

var myCustomer = new Customer('Dream Inc.');
myCustomer.sayMyName();
Customer.prototype.setAmountDue = function (amountDue) {
    this.amountDue = amountDue;
};
Customer.prototype.getAmountDue = function () {
    return this.amountDue;
};
myCustomer.setAmountDue(2000);
alert(myCustomer.getAmountDue());
Run Code Online (Sandbox Code Playgroud)

虽然如上所述我不能在Person上调用setAmountDue(),getAmountDue().

//The following statement generates an error.
john.setAmountDue(1000);
Run Code Online (Sandbox Code Playgroud)

  • 我认为stackoverflow的答案不仅对原始海报有趣,而且对潜伏或来自搜索的其他人的大社区也很有意义.我一直是其中之一,我从老帖子中受益.我想我可以为添加一些代码示例的其他答案做出贡献.关于你的问题:如果你遗漏了新的,它就不起作用了.当我调用myCustomer.sayMyName()时,它返回"myCustomer.sayMyName不是函数".最简单的方法是尝试使用firebug,看看会发生什么. (349认同)
  • 警告:此答案忽略了不基于每个实例调用父类构造函数的事实.它起作用的唯一原因是因为他在子构造函数和父构造函数中完成了相同的操作(设置名称).有关在JavaScript中尝试继承时常见错误(以及最终解决方案)的更深入解释,请参阅:[此堆栈溢出帖子](http://stackoverflow.com/questions/14564155/javascript-prototypal-inheritance-后代修调每个-其他/ 14576273#14576273) (17认同)
  • 关于`Customer.prototype = new Person();`行,MDN显示了一个使用`Customer.prototype = Object.create(Person.prototype)`的示例,并声明_这里常见的错误是使用"new Person" ()"'_.[源(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript) (10认同)
  • 据我所知var Person = function(name){...}; 定义了一个能够构建Person Objects的构造函数.所以还没有Object,只有匿名构造函数被赋给Person.这是一个非常好的解释:http://helephant.com/2008/08/how-javascript-objects-work/ (7认同)
  • 我注意到这个答案也没有提到通过使用"new Person()"作为原型,你实际上是将"Person"的"name"实例属性设置为"Customer"的静态属性(所以所有客户实例将具有相同的属性).虽然这是一个很好的基本例子,但请不要这样做.:)通过将其原型设置为"Person.prototype",创建一个新的匿名函数作为"桥",然后从中创建一个实例,并将"Customer.prototype"设置为该匿名实例. (3认同)
  • 但为什么?价值仍然是一个对象?在这里,对象和功能应该是难以区分的吗?除了你不能调用一个对象的事实. (2认同)

Chr*_*oph 986

每个JavaScript对象都有一个名为[[Prototype]]的内部属性.如果您通过obj.propName或查找属性obj['propName']并且该对象没有这样的属性 - 可以通过检查obj.hasOwnProperty('propName')- 运行时会在[[Prototype]]引用的对象中查找该属性.如果prototype-object也没有这样的属性,则依次检查其原型,从而遍历原始对象的原型链,直到找到匹配或达到其结束.

一些JavaScript实现允许直接访问[[Prototype]]属性,例如通过名为的非标准属性__proto__.通常,只能在对象创建期间设置对象的原型:如果通过创建新对象new Func(),则对象的[[Prototype]]属性将设置为引用的对象Func.prototype.

这允许在JavaScript中模拟类,尽管JavaScript的继承系统 - 正如我们所见 - 原型,而不是基于类:

只需将构造函数作为类和原型的属性(即构造函数的prototype属性引用的对象)视为共享成员,即每个实例的成员相同.在基于类的系统中,方法以相同的方式为每个实例实现,因此通常将方法添加到原型中,而对象的字段是特定于实例的,因此在构造期间添加到对象本身.

  • 注意*[[Prototype]]*的使用是故意的 - ECMA-262用双方括号括起内部属性的名称 (16认同)
  • 我讨厌非标准的东西,特别是在编程语言中,为什么有一个__proto__,当它显然不需要时? (7认同)
  • 我认为这就是将函数对象作为一等公民的意义. (3认同)

Meh*_*ami 179

我扮演JavaScript老师的角色,原型概念在我教授时一直是一个有争议的话题.我花了一段时间才想出一个澄清概念的好方法,现在在本文中我将试图解释JavaScript .prototype是如何工作的.


这是一个非常简单的基于原型的对象模型,在解释过程中将被视为一个样本,尚无评论:

function Person(name){
    this.name = name;
}
Person.prototype.getName = function(){
    console.log(this.name);
}
var person = new Person("George");
Run Code Online (Sandbox Code Playgroud)

在完成原型概念之前,我们必须考虑一些关键点.

1- JavaScript函数如何实际工作:

要迈出第一步,我们必须弄清楚,JavaScript函数实际上是如何工作的,像类似函数的类使用this关键字,或者作为常规函数及其参数,它做什么以及它返回什么.

假设我们想要创建一个Person对象模型.但是在这一步中,我将尝试在不使用prototypenew关键字的情况下做同样的事情.

因此,在这一步functions,objectsthis关键字,都是我们.

第一个问题是关键字如何this在不使用new关键字的情况下有用.

所以回答一下,假设我们有一个空对象,有两个函数,比如:

var person = {};
function Person(name){  this.name = name;  }

function getName(){
    console.log(this.name);
}
Run Code Online (Sandbox Code Playgroud)

现在不使用new关键字我们如何使用这些功能.所以JavaScript有3种不同的方法:

一个.第一种方法是将函数作为常规函数调用:

Person("George");
getName();//would print the "George" in the console
Run Code Online (Sandbox Code Playgroud)

在这种情况下,这将是当前上下文对象,这通常是全球 window在浏览器或对象GLOBALNode.js.这意味着我们将在浏览器中使用window.name或在Node.js中使用GLOBAL.name,并使用"George"作为其值.

湾 我们可以它们作为属性附加到对象上

- 最简单的方法是修改空person对象,如:

person.Person = Person;
person.getName = getName;
Run Code Online (Sandbox Code Playgroud)

通过这种方式我们可以称之为:

person.Person("George");
person.getName();// -->"George"
Run Code Online (Sandbox Code Playgroud)

现在person对象就像:

Object {Person: function, getName: function, name: "George"}
Run Code Online (Sandbox Code Playgroud)

- 将属性附加到对象的另一种方法是使用prototype可以在名称为的任何JavaScript对象中找到的该对象__proto__,并且我试图在摘要部分稍微解释一下.所以我们可以通过这样做得到类似的结果:

person.__proto__.Person = Person;
person.__proto__.getName = getName;
Run Code Online (Sandbox Code Playgroud)

但是这样我们实际上正在做的是修改Object.prototype,因为无论何时我们使用literals({ ... })创建一个JavaScript对象,它都是基于创建的Object.prototype,这意味着它作为一个名为的属性附加到新创建的对象__proto__,所以如果我们改变它正如我们在之前的代码片段中所做的那样,所有JavaScript对象都会被更改,这不是一个好习惯.那么现在可能是更好的做法:

person.__proto__ = {
    Person: Person,
    getName: getName
};
Run Code Online (Sandbox Code Playgroud)

现在其他物品都很平静,但它似乎仍然不是一个好习惯.所以我们还有一个解决方案,但是要使用这个解决方案,我们应该回到person创建对象的那一行代码(var person = {};)然后改变它:

var propertiesObject = {
    Person: Person,
    getName: getName
};
var person = Object.create(propertiesObject);
Run Code Online (Sandbox Code Playgroud)

它的作用是创建一个新的JavaScript Object并附propertiesObject加到__proto__属性.所以要确保你能做到:

console.log(person.__proto__===propertiesObject); //true
Run Code Online (Sandbox Code Playgroud)

但这里棘手的一点是您可以访问对象__proto__第一级中定义的所有属性person(有关更多详细信息,请阅读摘要部分).


正如你所看到的那样使用这两种方式中的任何一种this都会指向person对象.

C.JavaScript提供了另一种提供函数的方法this,即使用callapply来调用函数.

apply()方法调用一个给定此值的函数,并将参数作为数组(或类数组对象)提供.

call()方法调用一个给定此值的函数和单独提供的参数.

这种方式是我最喜欢的,我们可以轻松调用我们的功能:

Person.call(person, "George");
Run Code Online (Sandbox Code Playgroud)

要么

//apply is more useful when params count is not fixed
Person.apply(person, ["George"]);

getName.call(person);   
getName.apply(person);
Run Code Online (Sandbox Code Playgroud)

这三种方法是确定.prototype功能的重要初始步骤.


2- new关键字如何运作?

这是理解.prototype功能的第二步.这是我用来模拟过程的:

function Person(name){  this.name = name;  }
my_person_prototype = { getName: function(){ console.log(this.name); } };
Run Code Online (Sandbox Code Playgroud)

在这部分中,我将尝试采用JavaScript所采取的所有步骤,而不使用new关键字,并且prototype当您使用new关键字时.所以当我们这样做时new Person("George"),Person函数充当构造函数,这些是JavaScript所做的,一个接一个:

一个.首先它创建一个空对象,基本上是一个空哈希,如:

var newObject = {};
Run Code Online (Sandbox Code Playgroud)

湾 JavaScript采取的下一步是所有原型对象附加到新创建的对象

我们在my_person_prototype这里类似于原型对象.

for(var key in my_person_prototype){
    newObject[key] = my_person_prototype[key];
}
Run Code Online (Sandbox Code Playgroud)

这不是JavaScript实际附加原型中定义的属性的方式.实际的方式与原型链概念有关.


一个.&b.除了这两个步骤,您可以通过执行以下操作获得完全相同的结果:

var newObject = Object.create(my_person_prototype);
//here you can check out the __proto__ attribute
console.log(newObject.__proto__ === my_person_prototype); //true
//and also check if you have access to your desired properties
console.log(typeof newObject.getName);//"function"
Run Code Online (Sandbox Code Playgroud)

现在我们可以调用我们的getName函数my_person_prototype:

newObject.getName();
Run Code Online (Sandbox Code Playgroud)

C.然后它将该对象赋予构造函数,

我们可以使用以下示例执行此操作:

Person.call(newObject, "George");
Run Code Online (Sandbox Code Playgroud)

要么

Person.apply(newObject, ["George"]);
Run Code Online (Sandbox Code Playgroud)

然后构造函数可以做任何想做的事情,因为这个构造函数内部是刚刚创建的对象.

现在是模拟其他步骤之前的最终结果:对象{name:"George"}


摘要:

基本上,当你在一个函数上使用new关键字时,你正在调用它,并且该函数用作构造函数,所以当你说:

new FunctionName()
Run Code Online (Sandbox Code Playgroud)

JavaScript在内部创建一个对象,一个空哈希,然后它将该对象提供给构造函数,然后构造函数可以做任何想做的事情,因为这个构造函数内部是刚刚创建的对象,然后它给了你那个对象当然如果你没有在你的函数中使用return语句,或者你已经return undefined;在函数体的末尾放了一个.

因此,当JavaScript在一个对象上查找一个属性时,它所做的第一件事就是它在该对象上查找它.然后有一个[[prototype]]我们通常喜欢的秘密属性,__proto__这个属性就是JavaScript接下来要看的东西.当它查看时__proto__,就它再次是另一个JavaScript对象而言,它有自己的__proto__属性,它会一直向上和向上,直到它到达下一个__proto__为空的点.关键点是JavaScript中唯一一个__proto__属性为null的Object.prototype对象是object:

console.log(Object.prototype.__proto__===null);//true
Run Code Online (Sandbox Code Playgroud)

这就是继承在JavaScript中的工作方式.

原型链

换句话说,当你在一个函数上有一个prototype属性并且你在它上面调用一个新属性时,在JavaScript完成查找新创建的属性对象之后,它将查看该函数,.prototype并且该对象也可能有它的自己的内部原型.等等.

  • 你能说点简单吗?你是对的,但阅读这个解释的学生可能真的第一次感到困惑.拿起任何更简单的例子,让代码解释自己或添加一堆注释来澄清你的意思. (7认同)
  • a)请不要通过复制属性来解释原型b)设置内部*[[prototype]]*在实例上应用构造函数之前发生,请更改该顺序c)jQuery在这个问题中完全是offtopic (6认同)
  • @PM:感谢您的反馈.我试图让它变得尽可能简单,但我认为你是对的它还有一些模糊点.所以我会尝试修改它,也更具描述性.:) (2认同)

Ram*_*esh 74

prototype允许你上课.如果你不使用prototype那么它就变成了静态的.

这是一个简短的例子.

var obj = new Object();
obj.test = function() { alert('Hello?'); };
Run Code Online (Sandbox Code Playgroud)

在上面的例子中,你有静态函数调用测试.只能通过obj.test访问此函数,您可以将obj想象成一个类.

如下面的代码所示

function obj()
{
}

obj.prototype.test = function() { alert('Hello?'); };
var obj2 = new obj();
obj2.test();
Run Code Online (Sandbox Code Playgroud)

obj已成为一个现在可以实例化的类.可以存在多个obj实例,它们都具有该test功能.

以上是我的理解.我正在制作社区维基,所以如果我错了,人们可以纠正我.

  • -1:`prototype`是构造函数的属性,而不是实例,即你的代码错了!也许你的意思是对象的非标准属性`__proto__`,但这是一个完全不同的野兽...... (13认同)
  • 我认为这个答案有点误导. (5认同)
  • 这是一个很好的答案,但还有更多. (4认同)
  • 它还有很多......加上JavaScript不是基于类的语言 - 它通过原型处理继承,你需要更详细地介绍它们之间的差异! (3认同)

Cir*_*四事件 69

原型的七个Koans

随着Ciro San在深度冥想后降落火狐之火,他的思绪清晰而平和.

然而,他的手不安,并且自己抓起一把刷子并记下下面的笔记.


0)两种不同的东西可以称为"原型":

  • 原型属性,如 obj.prototype

  • 原型内部属性,[[Prototype]] 在ES5中表示.

    它可以通过ES5检索Object.getPrototypeOf().

    Firefox可以通过__proto__属性作为扩展访问它.ES6现在提到了一些可选要求__proto__.


1)存在回答这个问题的概念:

当我这样做时obj.property,JS在哪里寻找.property

直观地说,经典继承应该影响属性查找.


2)

  • __proto__用于点.属性查找,如obj.property.
  • .prototype用于直接查找,只是间接地因为它决定__proto__在对象创建与new.

查找顺序是:

  • objobj.p = ...或添加的属性Object.defineProperty(obj, ...)
  • 的性质 obj.__proto__
  • 属性obj.__proto__.__proto__等等
  • 如果一些__proto__就是null,返回undefined.

这就是所谓的原型链.

您可以避免.使用obj.hasOwnProperty('key')和查找Object.getOwnPropertyNames(f)


3)设置有两种主要方式obj.__proto__:


4)代码:

var F = function(i) { this.i = i }
var f = new F(1)
Run Code Online (Sandbox Code Playgroud)

对应下图:

(Function)       (  F  )                                      (f)----->(1)
 |  ^             | | ^                                        |   i    |
 |  |             | | |                                        |        |
 |  |             | | +-------------------------+              |        |
 |  |constructor  | |                           |              |        |
 |  |             | +--------------+            |              |        |
 |  |             |                |            |              |        |
 |  |             |                |            |              |        |
 |[[Prototype]]   |[[Prototype]]   |prototype   |constructor   |[[Prototype]]
 |  |             |                |            |              |        |
 |  |             |                |            |              |        |
 |  |             |                | +----------+              |        |
 |  |             |                | |                         |        |
 |  |             |                | | +-----------------------+        |
 |  |             |                | | |                                |
 v  |             v                v | v                                |
(Function.prototype)              (F.prototype)                         |
 |                                 |                                    |
 |                                 |                                    |
 |[[Prototype]]                    |[[Prototype]]          [[Prototype]]|
 |                                 |                                    |
 |                                 |                                    |
 | +-------------------------------+                                    |
 | |                                                                    |
 v v                                                                    v
(Object.prototype)                                       (Number.prototype)
 | | ^
 | | |
 | | +---------------------------+
 | |                             |
 | +--------------+              |
 |                |              |
 |                |              |
 |[[Prototype]]   |constructor   |prototype
 |                |              |
 |                |              |
 |                | -------------+
 |                | |
 v                v |
(null)           (Object)
Run Code Online (Sandbox Code Playgroud)

该图显示了许多语言的预定义对象节点:Number,null,Object,Object.prototypeFunction.我们的2行代码只创建了Function.prototype,1Number.prototype.


5) (1).__proto__通常来自f通过F查找:

var f = new F(1)
Run Code Online (Sandbox Code Playgroud)

当我们编写时F.prototype,JavaScript i会将查找视为:

  • f 不具有 F
  • thisnew,所以接受它

结果f直观正确,因为.constructor用于构造F.prototype,例如设置字段,就像在经典OOP语言中一样.


6)可以通过操纵原型链来实现经典继承语法.

ES6添加了.f.constructor关键字,它们只是以前可能的原型操作疯狂的语法糖.

f.constructor === F
!f.hasOwnProperty('constructor')
Object.getPrototypeOf(f) === F.prototype
F.prototype.hasOwnProperty('constructor')
F.prototype.constructor === f.constructor
Run Code Online (Sandbox Code Playgroud)
class C {
    constructor(i) {
        this.i = i
    }
    inc() {
        return this.i + 1
    }
}

class D extends C {
    constructor(i) {
        super(i)
    }
    inc2() {
        return this.i + 2
    }
}
Run Code Online (Sandbox Code Playgroud)
// Inheritance syntax works as expected.
c = new C(1)
c.inc() === 2
(new D(1)).inc() === 2
(new D(1)).inc2() === 3
Run Code Online (Sandbox Code Playgroud)
// "Classes" are just function objects.
C.constructor === Function
C.__proto__ === Function.prototype
D.constructor === Function
// D is a function "indirectly" through the chain.
D.__proto__ === C
D.__proto__.__proto__ === Function.prototype
Run Code Online (Sandbox Code Playgroud)
// "extends" sets up the prototype chain so that base class
// lookups will work as expected
var d = new D(1)
d.__proto__ === D.prototype
D.prototype.__proto__ === C.prototype
// This is what `d.inc` actually does.
d.__proto__.__proto__.inc === C.prototype.inc
Run Code Online (Sandbox Code Playgroud)

没有所有预定义对象的简化图:

// Class variables
// No ES6 syntax sugar apparently:
// http://stackoverflow.com/questions/22528967/es6-class-variable-alternatives
C.c = 1
C.c === 1
// Because `D.__proto__ === C`.
D.c === 1
// Nothing makes this work.
d.c === undefined
Run Code Online (Sandbox Code Playgroud)


roc*_*ock 64

读完这个帖子后,我觉得与JavaScript Prototype Chain混淆了,然后我找到了这些图表

http://iwiki.readthedocs.org/en/latest/javascript/js_core.html#inheritance *[[protytype]]*和函数对象的<code> prototype </ code>属性

它是一个清晰的图表,显示原型链的JavaScript继承

http://www.javascriptbank.com/javascript/article/JavaScript_Classical_Inheritance/

这个包含一个代码示例和几个漂亮的图表.

原型链最终回落到Object.prototype.

原型链可以在技术上扩展,只要你想要,每次通过设置子类的原型等于父类的对象.

希望它对您理解JavaScript原型链也很有帮助.

  • 你能解释一下`[[Prototype]]的含义吗? (3认同)
  • 一张图说千言万语.这太有用:) (2认同)

sam*_*sam 37

每个对象都有一个内部属性[[Prototype]],将它链接到另一个对象:

object [[Prototype]] -> anotherObject
Run Code Online (Sandbox Code Playgroud)

在传统的javascript中,链接对象是prototype函数的属性:

object [[Prototype]] -> aFunction.prototype
Run Code Online (Sandbox Code Playgroud)

某些环境将[[Prototype]]暴露为__proto__:

anObject.__proto__ === anotherObject
Run Code Online (Sandbox Code Playgroud)

您在创建对象时创建[[Prototype]]链接.

// (1) Object.create:
var object = Object.create(anotherObject)
// object.__proto__ = anotherObject

// (2) ES6 object initializer:
var object = { __proto__: anotherObject };
// object.__proto__ = anotherObject

// (3) Traditional JavaScript:
var object = new aFunction;
// object.__proto__ = aFunction.prototype
Run Code Online (Sandbox Code Playgroud)

所以这些陈述是等价的:

var object = Object.create(Object.prototype);
var object = { __proto__: Object.prototype }; // ES6 only
var object = new Object;
Run Code Online (Sandbox Code Playgroud)

new言不显示的链接目标(Object.prototype)本身; 相反,构造函数(Object)暗示了目标.

记得:

  • 每个对象都有一个链接[[Prototype]],有时会显示为__proto__.
  • 每个功能都有一个prototype属性.
  • 创建的对象new链接到prototype其构造函数的属性.
  • 如果函数从未用作构造函数,则其prototype属性将不使用.
  • 如果您不需要构造函数,请使用Object.create而不是new.

  • IMO 至少链接到 [`Object.create()` 文档](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create),@sam。链接到 [`__proto__`](https://developer.mozilla.org/cs/docs/Web/JavaScript/Reference/Global_Objects/Object/Proto) 和 [`Object.prototype`](https://developer.mozilla .org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype)将是很好的增强。我喜欢你关于原型如何与构造函数和 `Object.create()` 一起工作的例子,但它们可能是你想要摆脱的冗长且不太相关的部分。 (2认同)

Geo*_*lly 26

Javascript没有通常意义上的继承,但它有原型链.

原型链

如果在对象中找不到对象的成员,则它在原型链中查找它.该链由其他对象组成.可以使用__proto__变量访问给定实例的原型.每个对象都有一个,因为javascript中的类和实例之间没有区别.

向原型添加函数/变量的优点是它必须只在内存中一次,而不是每个实例.

它对继承也很有用,因为原型链可以包含许多其他对象.


Aru*_*ore 26

这篇文章很长.但我相信它会清除大多数关于JavaScript继承的"原型"性质的疑问.甚至更多.请阅读完整的文章.

JavaScript基本上有两种数据类型

  • 非物件
  • 对象

非物件

以下是非对象数据类型

  • 数字(包括NaN和Infinity)
  • 布尔值(true,false)
  • 未定义

使用typeof运算符时,这些数据类型将返回

typeof "string literal"(或包含字符串文字的变量)=== 'string'

typeof 5(或任何数字文字或包含数字文字或NaN或Infynity的变量)=== 'number'

typeof true(或false或包含truefalse的变量)=== 'boolean'

typeof undefined(或未定义的变量或包含undefined的变量)=== 'undefined'

字符串,号码布尔数据类型可以表示既作为对象非对象.当它们被表示为对象他们的typeof总是==="对象".一旦我们理解了对象数据类型,我们将回到这一点.

对象

对象数据类型可以进一步分为两种类型

  1. 函数类型对象
  2. 非函数类型对象

功能类型的对象是返回字符串的那些"功能"typeof运算符.所有用户定义的函数和所有可以通过使用new运算符创建新对象的对象内置的JavaScript都属于此类别.例如.

  • 宾语
  • 布尔
  • 排列
  • 键入的数组
  • 正则表达式
  • 功能
  • 所有其他内置对象都可以使用new运算符创建新对象
  • function UserDefinedFunction(){/*用户自定义代码*/}

所以, typeof(Object) === typeof(String) === typeof(Number) === typeof(Boolean) === typeof(Array) === typeof(RegExp) === typeof(Function) == = typeof(UserDefinedFunction) === 'function'

所有Function类型对象实际上都是内置JavaScript对象Function的实例(包括Function对象,即它是递归定义的).就好像以下列方式定义了这些对象

var Object= new Function ([native code for object Object])
var String= new Function ([native code for object String])
var Number= new Function ([native code for object Number])
var Boolean= new Function ([native code for object Boolean])
var Array= new Function ([native code for object Array])
var RegExp= new Function ([native code for object RegExp])
var Function= new Function ([native code  for object Function])
var UserDefinedFunction= new Function ("user defined code")
Run Code Online (Sandbox Code Playgroud)

如上所述,Function类型对象可以使用new运算符进一步创建新对象.例如,可以使用Object,String,Number,Boolean,Array,RegExpUserDefinedFunction类型的对象创建

var a=new Object() or var a=Object() or var a={} //Create object of type Object
var a=new String() //Create object of type String
var a=new Number() //Create object of type Number
var a=new Boolean() //Create object of type Boolean
var a=new Array() or var a=Array() or var a=[]  //Create object of type Array
var a=new RegExp() or var a=RegExp() //Create object of type RegExp
var a=new UserDefinedFunction() 
Run Code Online (Sandbox Code Playgroud)

这样创建的对象都是非函数类型对象,并返回其typeof === 'object'.在所有这些情况下,对象"a"不能使用operator new进一步创建对象.所以以下是错误的

var b=new a() //error. a is not typeof==='function'
Run Code Online (Sandbox Code Playgroud)

内置对象Mathtypeof === 'object'.因此,新运算符无法创建Math类型的新对象.

var b=new Math() //error. Math is not typeof==='function'
Run Code Online (Sandbox Code Playgroud)

另请注意,Object,ArrayRegExp函数可以创建新对象,甚至不使用operator new.然而,下面的人没有.

var a=String() // Create a new Non Object string. returns a typeof==='string' 
var a=Number() // Create a new Non Object Number. returns a typeof==='number'
var a=Boolean() //Create a new Non Object Boolean. returns a typeof==='boolean'
Run Code Online (Sandbox Code Playgroud)

用户定义的函数是特例.

var a=UserDefinedFunction() //may or may not create an object of type UserDefinedFunction() based on how it is defined.
Run Code Online (Sandbox Code Playgroud)

由于Function类型对象可以创建新对象,因此它们也称为Constructors.

自动定义时,每个构造函数/函数(无论是内置的还是用户定义的)都有一个名为"prototype"的属性,默认情况下它的值被设置为一个对象.该对象本身有一个名为"constructor"的属性,默认情况下会引用构造函数/函数.

例如,当我们定义一个函数时

function UserDefinedFunction()
{
}
Run Code Online (Sandbox Code Playgroud)

以下自动发生

UserDefinedFunction.prototype={constructor:UserDefinedFunction}
Run Code Online (Sandbox Code Playgroud)

"prototype"属性仅存在于Function类型对象中 (并且永远不会出现在Non Function类型对象中).

这是因为当创建一个新对象(使用new运算符)时,它从Constructor函数的当前原型对象继承所有属性和方法,即 在新创建的对象中创建内部引用 ,该对象引用Constructor函数的当前原型对象引用的对象.

这个"内部参考"是在对象创建用于参考继承属性是被称为对象的原型(引用由构造的引用的对象"原型"属性,但不同的是从它).对于任何对象(函数或非函数),可以使用Object.getPrototypeOf()方法检索它.使用此方法可以跟踪对象的原型链.

此外,创建的每个对象(函数类型或非函数类型)都具有"构造函数"属性,该属性继承自构造函数的prototype属性引用的对象.默认情况下,这个"构造"属性引用的构造函数创建它(如果构造函数的默认的"原型"没有变化).

对于所有Function类型对象,构造函数始终是 函数Function(){}

对于非函数类型对象(例如,Javascript Built in Math对象),构造函数是创建它的函数.对于Math对象,它是函数Object(){}.

如果没有任何支持代码,上面解释的所有概念都可能有点令人生畏.请逐行浏览以下代码以了解概念.尝试执行它以更好地理解.

function UserDefinedFunction()
{ 

} 

/* creating the above function automatically does the following as mentioned earlier

UserDefinedFunction.prototype={constructor:UserDefinedFunction}

*/


var newObj_1=new UserDefinedFunction()

alert(Object.getPrototypeOf(newObj_1)===UserDefinedFunction.prototype)  //Displays true

alert(newObj_1.constructor) //Displays function UserDefinedFunction

//Create a new property in UserDefinedFunction.prototype object

UserDefinedFunction.prototype.TestProperty="test"

alert(newObj_1.TestProperty) //Displays "test"

alert(Object.getPrototypeOf(newObj_1).TestProperty)// Displays "test"

//Create a new Object

var objA = {
        property1 : "Property1",
        constructor:Array

}


//assign a new object to UserDefinedFunction.prototype
UserDefinedFunction.prototype=objA

alert(Object.getPrototypeOf(newObj_1)===UserDefinedFunction.prototype)  //Displays false. The object referenced by UserDefinedFunction.prototype has changed

//The internal reference does not change
alert(newObj_1.constructor) // This shall still Display function UserDefinedFunction

alert(newObj_1.TestProperty) //This shall still Display "test" 

alert(Object.getPrototypeOf(newObj_1).TestProperty) //This shall still Display "test"


//Create another object of type UserDefinedFunction
var newObj_2= new UserDefinedFunction();

alert(Object.getPrototypeOf(newObj_2)===objA) //Displays true.

alert(newObj_2.constructor) //Displays function Array()

alert(newObj_2.property1) //Displays "Property1"

alert(Object.getPrototypeOf(newObj_2).property1) //Displays "Property1"

//Create a new property in objA
objA.property2="property2"

alert(objA.property2) //Displays "Property2"

alert(UserDefinedFunction.prototype.property2) //Displays "Property2"

alert(newObj_2.property2) // Displays Property2

alert(Object.getPrototypeOf(newObj_2).property2) //Displays  "Property2"
Run Code Online (Sandbox Code Playgroud)

每个对象的原型链最终追溯到Object.prototype(它本身没有任何原型对象).以下代码可用于跟踪对象的原型链

var o=Starting object;

do {
    alert(o + "\n" + Object.getOwnPropertyNames(o))

}while(o=Object.getPrototypeOf(o))
Run Code Online (Sandbox Code Playgroud)

各种对象的原型链如下所示.

  • 每个Function对象(包括内置的Function对象) - > Function.prototype - > Object.prototype - > null
  • 简单对象(由新的Object()或{}创建,包括内置的Math对象) - > Object.prototype - > null
  • 使用new或Object.create创建的对象 - >一个或多个原型链 - > Object.prototype - > null

要创建没有任何原型的对象,请使用以下命令:

var o=Object.create(null)
alert(Object.getPrototypeOf(o)) //Displays null
Run Code Online (Sandbox Code Playgroud)

有人可能会认为将Constructor的prototype属性设置为null将创建一个带有null原型的对象.但是在这种情况下,新创建的对象的原型设置为Object.prototype,其构造函数设置为Object.以下代码对此进行了演示

function UserDefinedFunction(){}
UserDefinedFunction.prototype=null// Can be set to any non object value (number,string,undefined etc.)

var o=new UserDefinedFunction()
alert(Object.getPrototypeOf(o)==Object.prototype)   //Displays true
alert(o.constructor)    //Displays Function Object
Run Code Online (Sandbox Code Playgroud)

以下是本文的摘要

  • 有两种类型的对象函数类型非函数类型
  • 只有Function类型对象可以使用operator new创建一个新对象.这样创建的对象是非函数类型对象.该非功能型对象使用不能再创建一个对象的operator new.

  • 默认情况下,所有函数类型对象都具有"原型"属性.此"prototype"属性引用具有"构造函数"属性的对象,该属性默认情况下引用Function类型对象本身.

  • 所有对象(函数类型非函数类型)都有一个"构造函数"属性,默认情况下引用创建它的函数类型对象/构造函数.

  • 在内部创建的每个对象都引用由创建它的构造函数的"prototype"属性引用的对象 .此对象称为创建对象的原型(它与它引用的Function类型对象"prototype"属性不同).这样,创建的对象可以直接访问构造函数的"prototype"属性(在创建对象时)引用的对象中定义的方法和属性.

  • 一个对象的原型(并且因此其继承的属性名称)可以使用被检索Object.getPrototypeOf() 方法.实际上,该方法可用于导航对象的整个原型链.

  • 每个对象的原型链最终都追溯到Object.prototype(除非使用Object.create(null)创建对象,在这种情况下对象没有原型).

  • typeof(new Array())==='object'是语言设计而非道格拉斯克罗克福德指出的错误

  • 将Constructor的prototype属性设置为null(或undefined,number,true,false,string)不应创建具有null原型的对象.在这种情况下,新创建的对象的原型设置为Object.prototype,其构造函数设置为Object.

希望这可以帮助.


Tha*_*var 22

prototypal对于许多开发人员来说,继承的概念是最复杂的概念之一.让我们试着理解问题的根源,以便prototypal inheritance更好地理解.让我们从一个plain函数开始.

在此输入图像描述

如果我们在上面使用new运算符Tree function,我们将其称为constructor函数.

在此输入图像描述

每个JavaScript功能都有prototype.当你登录时Tree.prototype,你得到......

在此输入图像描述

如果你查看上面的console.log()输出,你可以看到一个构造函数属性Tree.prototype和一个__proto__属性.的__proto__代表prototype,这function是基于关闭,因为这只是一个简单的JavaScript function没有inheritance建立的是,它指的是Object prototype这仅仅是内置于JavaScript的东西...

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype

这有类似的东西.toString, .toValue, .hasOwnProperty......

__proto__带来了我的mozilla已被弃用,并被Object.getPrototypeOf方法取代了object's prototype.

在此输入图像描述

Object.getPrototypeOf(Tree.prototype); // Object {} 
Run Code Online (Sandbox Code Playgroud)

让我们为我们添加一个方法Tree prototype.

在此输入图像描述

我们修改了Root并添加了一个function分支.

在此输入图像描述

这意味着,当你创建instanceTree,你可以调用它的branch方法.

在此输入图像描述

我们也可以添加primitives或添加objects到我们的Prototype.

在此输入图像描述

让我们添加一个child-tree到我们Tree.

在此输入图像描述

这里从Tree Child继承它prototype,我们在这里做的是使用Object.create()方法根据你传递的内容创建一个新对象,就在这里Tree.prototype.在这种情况下,我们正在做的是将Child的原型设置为一个看起来与Tree原型相同的新对象.接下来我们设置Child's constructor to Child,如果我们不这样做,它会指向Tree().

在此输入图像描述

Child现在有自己的prototype,它的__proto__要点TreeTree's prototype指向基础Object.

Child  
|
 \
  \
   Tree.prototype
   - branch
   |
   |
    \
     \
      Object.prototype
      -toString
      -valueOf
      -etc., etc.
Run Code Online (Sandbox Code Playgroud)

现在您创建一个最初可用instanceChild和调用.我们实际上没有定义我们的.但是,孩子继承自哪个.branchTreebranchChild prototypeRoot prototype

在此输入图像描述

在JS中,一切都不是一个对象,一切都可以像一个对象.

Javascript有原始人喜欢strings, number, booleans, undefined, null.他们不是object(i.e reference types),但肯定可以表现得像object.我们来看一个例子吧.

在此输入图像描述

在此列表的第一行中,primitive为name分配了一个字符串值.第二行将名称视为an,objectcharAt(0)使用点表示法调用.

这就是幕后发生的事情:// JavaScript引擎的作用

在此输入图像描述

String object只为它销毁前一个语句(这个过程被称为存在autoboxing).让我们再回到我们的prototypal inheritance.

  • Javascript支持delegation基于的 继承prototypes.
  • 每个Function都有一个prototype属性,它引用另一个对象.
  • properties/functions如果它不存在,则从它object自身或通过 prototype链看

一个prototype在JS是一个对象,yields你的另一父object.[即...授权] Delegation意味着如果你无法做某事,你会告诉别人为你做这件事.

在此输入图像描述

https://jsfiddle.net/say0tzpL/1/

如果你查看上面的小提琴,狗可以访问toString方法,但它不可用,但可通过代表到的原型链获得Object.prototype

在此输入图像描述

如果你看下面的一个,我们试图访问call每个可用的方法function.

在此输入图像描述

https://jsfiddle.net/rknffckc/

如果您查看上面的小提琴,ProfileFunction可以访问call方法,但它不可用,但可以通过代理到的原型链获得Function.prototype

在此输入图像描述

注意: prototype是函数构造函数的属性,而是__proto__从函数构造函数构造的对象的属性.每个函数都带有一个prototype值为空的属性object.当我们创建函数的实例时,我们得到一个内部属性[[Prototype]]或其__proto__引用是函数的原型constructor.

在此输入图像描述

上面的图看起来有点复杂,但是它展示了如何prototype chaining工作的全貌.让我们慢慢来看看:

有两个实例b1b2,它的构造是Bar和家长是Foo和具有从原型链两种方法identify,并speak通过BarFoo

在此输入图像描述

https://jsfiddle.net/kbp7jr7n/

如果你查看上面的代码,我们有Foo构造函数谁有方法identify()Bar构造函数有speak方法.我们创建了两个Bar实例b1b2其父类型Foo.现在,在调用speak方法时Bar,我们能够识别通过prototype链调用说话的人.

在此输入图像描述

Bar现在已经在Foo其中定义了所有方法prototype.让我们进一步深入理解Object.prototypeFunction.prototype以及它们是如何相关的.如果你查找构造函数Foo,Bar并且ObjectFunction constructor.

在此输入图像描述

prototypeBar就是Foo,prototypeFoo就是Object,如果你仔细看的prototypeFoo是有关Object.prototype.

在此输入图像描述

在我们关闭它之前,让我们在这里用一小段代码包装来总结上面的所有内容.我们在instanceof这里使用运算符来检查objectprototype链中的prototype属性是否在constructor下面总结了整个大图.

在此输入图像描述

我希望这可以添加一些信息,我知道这可能很难掌握......简单来说,它只是与对象相关的对象!


dir*_*tly 20

这个".prototype"属性的确切目的是什么?

标准类的接口变得可扩展.例如,您正在使用Array该类,还需要为所有数组对象添加自定义序列化程序.您是否会花时间编写子类,或使用合成或...原型属性通过让用户控制类可用的确切成员/方法集来解决这个问题.

将原型视为额外的vtable指针.当原始类中缺少某些成员时,将在运行时查找原型.


tri*_*cot 19

将原型链分类为两类可能会有所帮助.

考虑一下构造函数:

 function Person() {}
Run Code Online (Sandbox Code Playgroud)

Object.getPrototypeOf(Person)是一个函数.事实上,它是Function.prototype.由于Person是作为函数创建的,因此它共享所有函数具有的相同原型函数对象.它是相同的Person.__proto__,但不应该使用该属性.无论如何,随着Object.getPrototypeOf(Person)你有效地走上所谓的原型链的阶梯.

向上的链条看起来像这样:

    Person→交通Function.prototype→交通Object.prototype(终点)

重要的是,这个原型链与Person可以构造的对象几乎没有关系.那些构造的对象具有它们自己的原型链,并且该链可能与上面提到的链没有共同的祖先.

以此对象为例:

var p = new Person();
Run Code Online (Sandbox Code Playgroud)

pPerson没有直接的原型链关系.他们的关系是不同的.对象p有自己的原型链.使用Object.getPrototypeOf,你会发现链如下:

    p→交通Person.prototype→交通Object.prototype(终点)

这个链中没有功能对象(尽管可能是这样).

所以Person似乎与两种生活相关的链条有关.要从一个链"跳"到另一个链,您使用:

  1. .prototype:从构造函数链跳转到created-object的链.因此,仅为函数对象定义此属性(因为new只能在函数上使用).

  2. .constructor:从created对象的链跳转到构造函数的链.

以下是所涉及的两个原型链的可视化表示,表示为列:

在此输入图像描述

总结一下:

prototype属性不提供主题原型链的信息,也不提供主题创建的对象的信息.

毫不奇怪,该物业的名称prototype可能导致混乱.如果这个属性被命名prototypeOfConstructedInstances或沿着那条线的东西可能会更清楚.

您可以在两个原型链之间来回跳转:

Person.prototype.constructor === Person
Run Code Online (Sandbox Code Playgroud)

可以通过向prototype属性显式指定不同的对象来打破这种对称性(稍后将详细介绍).

创建一个函数,获取两个对象

Person.prototype是在创建函数的Person同时创建的对象.它具有Person构造函数,即使该构造函数实际上还没有执行.因此,同时创建了两个对象:

  1. 功能Person本身
  2. 在将函数作为构造函数调用时将充当原型的对象

两者都是对象,但它们具有不同的角色:函数对象构造,而另一个对象代表函数将构造的任何对象的原型.原型对象将成为其原型链中构造对象的父对象.

由于函数也是一个对象,它在自己的原型链中也有自己的父对象,但回想一下这两个链是不同的东西.

以下是一些可以帮助解决问题的平等 - 所有这些打印true:

function Person() {};

// This is prototype chain info for the constructor (the function object):
console.log(Object.getPrototypeOf(Person) === Function.prototype);
// Step further up in the same hierarchy:
console.log(Object.getPrototypeOf(Function.prototype) === Object.prototype);
console.log(Object.getPrototypeOf(Object.prototype) === null);
console.log(Person.__proto__ === Function.prototype);
// Here we swap lanes, and look at the constructor of the constructor
console.log(Person.constructor === Function);
console.log(Person instanceof Function);

// Person.prototype was created by Person (at the time of its creation)
// Here we swap lanes back and forth:
console.log(Person.prototype.constructor === Person);
// Although it is not an instance of it:
console.log(!(Person.prototype instanceof Person));
// Instances are objects created by the constructor:
var p = new Person();
// Similarly to what was shown for the constructor, here we have
// the same for the object created by the constructor:
console.log(Object.getPrototypeOf(p) === Person.prototype);
console.log(p.__proto__ === Person.prototype);
// Here we swap lanes, and look at the constructor
console.log(p.constructor === Person);
console.log(p instanceof Person);
Run Code Online (Sandbox Code Playgroud)

为原型链添加级别

虽然在创建构造函数时会创建原型对象,但您可以忽略该对象,并指定另一个应该用作该构造函数创建的后续实例的原型的对象.

例如:

function Thief() { }
var p = new Person();
Thief.prototype = p; // this determines the prototype for any new Thief objects:
var t = new Thief();
Run Code Online (Sandbox Code Playgroud)

现在t的原型链比p的长一步:

    t→交通p→交通Person.prototype→交通Object.prototype(终点)

另外原型链是不再:ThiefPerson是兄弟姐妹共享相同的父在他们的原型链:

    Person}
    Thief  }→交通Function.prototype→交通Object.prototype(终点)

然后可以将先前呈现的图形扩展到此(原始图像Thief.prototype被省略):

在此输入图像描述

蓝线代表原型链,其他彩色线代表其他关系:

  • 在一个对象和它的构造函数之间
  • 在构造函数和将用于构造对象的原型对象之间


Bad*_*Bad 16

面向对象JavaScript的权威指南 - 一个非常简洁明了的〜30分钟的问题视频解释(原型继承主题从5:45开始,虽然我宁愿听完整个视频).此视频的作者还制作了JavaScript对象可视化工具网站http://www.objectplayground.com/.在此输入图像描述 在此输入图像描述


B M*_*B M 14

我发现obj_n.prop_X在引用时将"原型链"解释为递归约定很有帮助:

如果obj_n.prop_X不存在,请检查obj_n+1.prop_X位置obj_n+1 = obj_n.[[prototype]]

如果prop_X最终在第k个原型对象中找到了那么

obj_1.prop_X = obj_1.[[prototype]].[[prototype]]..(k-times)..[[prototype]].prop_X

您可以在此处找到Javascript对象的属性关系图:

js对象图

http://jsobjects.org


小智 13

当构造函数创建对象时,该对象隐式引用构造函数的"prototype"属性以解析属性引用.构造函数的"prototype"属性可以由程序表达式constructor.prototype引用,添加到对象原型的属性通过继承由共享原型的所有对象共享.


Ara*_*ind 10

让我告诉你我对原型的理解.我不打算将这里的继承与其他语言进行比较.我希望人们不要再比较语言,只要把语言理解为自己.了解原型和原型继承非常简单,我将在下面向您展示.

Prototype就像一个模型,在此基础上您可以创建产品.要理解的关键点是,当您使用另一个对象创建一个对象作为原型时,原型和产品之间的链接将持续存在.例如:

var model = {x:2};
var product = Object.create(model);
model.y = 5;
product.y
=>5
Run Code Online (Sandbox Code Playgroud)

每个对象都包含一个名为[[prototype]]的内部属性,该属性可由Object.getPrototypeOf()函数访问.Object.create(model)创建一个新对象并将其[[prototype]]属性设置为对象模型.因此,当您这样做时Object.getPrototypeOf(product),您将获得对象模型.

产品中的属性按以下方式处理:

  • 当访问属性只读取它的值时,它会在范围链中查找.对变量的搜索从产品向上开始到它的原型.如果在搜索中找到这样的变量,则在那里停止搜索,并返回该值.如果在作用域链中找不到这样的变量,则返回undefined.
  • 写入(更改)属性时,该属性始终写在产品对象上.如果产品已经没有这样的属性,则会隐式创建和编写它.

使用prototype属性对象的这种链接称为原型继承.在那里,它是如此简单,同意?


Jim*_*ard 10

这里有两个不同但相关的实体需要解释:

  • .prototype功能的属性.
  • 所有对象的[[Prototype]][1]属性[2].

这是两件不同的事情.

[[Prototype]]属性:

这是所有[2]对象上都存在的属性.

这里存储的是另一个对象,作为一个对象本身,它有一个[[Prototype]]指向另一个对象的对象.那个其他对象有[[Prototype]]它自己的.这个故事一直持续到您提供的原型对象提供可在所有对象上访问的方法(如.toString).

[[Prototype]]物业是形成[[Prototype]]连锁店的一部分.这个[[Prototype]]对象链是例如在对象上执行[[Get]][[Set]]操作时检查的对象:

var obj = {}
obj.a         // [[Get]] consults prototype chain
obj.b = 20    // [[Set]] consults prototype chain
Run Code Online (Sandbox Code Playgroud)

.prototype属性:

这是一个只能在函数中找到的属性.使用一个非常简单的功能:

function Bar(){};
Run Code Online (Sandbox Code Playgroud)

.prototype属性包含一个b.[[Prototype]]在您执行时分配的对象var b = new Bar.你可以轻松地检查一下:

// Both assign Bar.prototype to b1/b2[[Prototype]]
var b = new Bar;
// Object.getPrototypeOf grabs the objects [[Prototype]]
console.log(Object.getPrototypeOf(b) === Bar.prototype) // true
Run Code Online (Sandbox Code Playgroud)

其中最重要的.prototype-是不是对的Object功能.这个原型保存了所有[[Prototype]]链包含的原型对象.在其上,定义了新对象的所有可用方法:

// Get properties that are defined on this object
console.log(Object.getOwnPropertyDescriptors(Object.prototype))
Run Code Online (Sandbox Code Playgroud)

现在,既然.prototype是一个对象,它就有一个[[Prototype]]属性.当你不做出任何任务Function.prototype时,.prototype[[Prototype]]指向原型对象(Object.prototype).每次创建新功能时都会自动执行此操作.

这样,无论new Bar;何时为您设置原型链,您都可以获得定义的所有内容Bar.prototype以及定义的所有内容Object.prototype:

var b = new Bar;
// Get all Bar.prototype properties
console.log(b.__proto__ === Bar.prototype)
// Get all Object.prototype properties
console.log(b.__proto__.__proto__ === Object.prototype)
Run Code Online (Sandbox Code Playgroud)

当你这样做使分配到Function.prototype所有你正在做的是延长了原型链包含另一个对象.这就像插入单链表中一样.

这基本上改变了[[Prototype]]链,允许Function.prototype由分配给的对象上定义的属性被函数创建的任何对象看到.


[1:这不会混淆任何人; 通过提供__proto__性能在许多实现.
[2]:除了null.


rus*_*us1 9

另一种尝试用更好的图片解释基于JavaScript原型的继承

简单的对象继承


Joh*_*ers 9

考虑以下keyValueStore对象:

var keyValueStore = (function() {
    var count = 0;
    var kvs = function() {
        count++;
        this.data = {};
        this.get = function(key) { return this.data[key]; };
        this.set = function(key, value) { this.data[key] = value; };
        this.delete = function(key) { delete this.data[key]; };
        this.getLength = function() {
            var l = 0;
            for (p in this.data) l++;
            return l;
        }
    };

    return  { // Singleton public properties
        'create' : function() { return new kvs(); },
        'count' : function() { return count; }
    };
})();
Run Code Online (Sandbox Code Playgroud)

我可以通过这样做来创建这个对象的新实例:

kvs = keyValueStore.create();
Run Code Online (Sandbox Code Playgroud)

该对象的每个实例都具有以下公共属性:

  • data
  • get
  • set
  • delete
  • getLength

现在,假设我们创建了此keyValueStore对象的100个实例.尽管get,set,delete,getLength会做同样的事情对每个100个实例,每个实例都有自己的这个函数的副本.

现在,想象一下,如果你可以有只是一个单一的get,set,deletegetLength复制,并且每个实例将引用相同的功能.这对性能更好,并且需要更少的内存.

这就是原型进入的地方.原型是属性的"蓝图",它是继承的,但不是由实例复制的.所以这意味着它只在内存中存在一个对象的所有实例,并由所有这些实例共享.

现在,keyValueStore再次考虑该对象.我可以像这样重写它:

var keyValueStore = (function() {
    var count = 0;
    var kvs = function() {
        count++;
        this.data = {};
    };

    kvs.prototype = {
        'get' : function(key) { return this.data[key]; },
        'set' : function(key, value) { this.data[key] = value; },
        'delete' : function(key) { delete this.data[key]; },
        'getLength' : function() {
            var l = 0;
            for (p in this.data) l++;
            return l;
        }
    };

    return  {
        'create' : function() { return new kvs(); },
        'count' : function() { return count; }
    };
})();
Run Code Online (Sandbox Code Playgroud)

这与keyValueStore对象的先前版本完全相同,只是它的所有方法现在都放在原型中.这意味着,现在所有100个实例都共享这四种方法,而不是每种方法都有自己的副本.


Lou*_*ore 7

在理解这类东西时,我总是喜欢类比.在我看来,与原型低音继承相比,"原型继承"相当令人困惑,尽管原型是更简单的范例.事实上,对于原型,实际上没有继承,因此名称本身就具有误导性,它更像是一种"委托".

想象一下......

你在高中,而且你在课堂上并且有一个今天到期的测验,但你没有笔来填写你的答案.卫生署!

你坐在你的朋友Finnius旁边,他可能有一支钢笔.你问,他看着他的桌子不成功,但他没有说"我没有笔",他是一个很好的朋友,如果他有笔,他会和他的另一个朋友Derp一起检查.Derp确实有一支备用笔并将其传递回Finnius,后者将其传递给您以完成您的测验.Derp已将笔委托给Finnius,后者已将笔委托给您使用.

这里最重要的是Derp不会给你笔,因为你与他没有直接关系.

这是原型如何工作的简化示例,其中搜索数据树以查找您正在寻找的事物.


Wil*_*een 6

摘要:

  • 函数是javascript中的对象,因此可以具有属性
  • (构造函数)函数始终具有原型属性
  • 当将函数用作new关键字的构造函数时,对象将获取原型。可以__proto__在新创建的对象的属性上找到对该原型的引用。
  • __proto__属性是指prototype构造函数的属性。

例:

function Person (name) {
  this.name = name;
}

let me = new Person('willem');

console.log(Person.prototype) // Person has a prototype property

console.log(Person.prototype === me.__proto__) // the __proto__ property of the instance refers to prototype property of the function.
Run Code Online (Sandbox Code Playgroud)

为什么这样有用:

当在对象上查找属性时,JavaScript具有一种称为“原型继承”的机制,基本上是这样做的:

  • 首先检查属性是否位于对象本身上。如果是这样,则返回此属性。
  • 如果该属性不在对象本身上,它将“爬升原型链”。它基本上看一下由proto属性引用的对象。在那里,它检查属性是否在proto所引用的对象上可用
  • 如果该属性不在原型对象上,它将沿着原型链一直爬到对象对象。
  • 如果无法在对象及其原型链的任何地方找到该属性,它将返回未定义。

例如:

function Person(name) {
  this.name = name;
}

let mySelf = new Person('Willem');

console.log(mySelf.__proto__ === Person.prototype);

console.log(mySelf.__proto__.__proto__ === Object.prototype);
Run Code Online (Sandbox Code Playgroud)

更新:

__proto__尽管已在大多数现代浏览器中实现了该属性,但已弃用该 属性,获取原型对象引用的一种更好的方法是:

Object.getPrototypeOf()