Javascript .prototype如何工作?

How does JavaScript .prototype work?

我对动态编程语言不太了解,但我已经编写了相当多的javascript代码。我从来没有真正了解过这个基于原型的编程,有人知道它是如何工作的吗?

1
2
3
4
var obj = new Object();
obj.prototype.test = function() { alert('Hello?'); };
var obj2 = new obj();
obj2.test();

我记得有一段时间我和别人讨论了很多(我不太确定自己在做什么),但据我所知,没有课堂的概念。它只是一个对象,这些对象的实例是原始对象的克隆,对吗?

但javascript中这个".prototype"属性的确切目的是什么?它如何与实例化对象相关?

更新:正确方式

1
2
3
4
5
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

而且这些幻灯片真的帮助了很多。


在一个实现Java、C、C++等经典继承的语言中,您首先创建一个类——一个对象的蓝图——然后您可以从该类中创建新对象,或者可以扩展类,定义一个新的类来增加原始类。

在JavaScript中,您首先创建一个对象(没有类的概念),然后您可以扩充自己的对象或从中创建新的对象。这并不难,但有点异国情调,很难为习惯于古典方式的人进行新陈代谢。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//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();

直到现在,我一直在扩展基本对象,现在我创建了另一个对象,然后从个人继承。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//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());

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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());

如前所述,我不能对一个人调用setAmountDue()、getAmountDue()。

1
2
//The following statement generates an error.
john.setAmountDue(1000);


每个javascript对象都有一个名为[[原型]]的内部属性。如果您通过obj.propNameobj['propName']查找属性,而对象没有这样的属性(可以通过obj.hasOwnProperty('propName')检查),则运行时将查找由[[原型]]引用的对象中的属性。如果原型对象也没有这样的属性,则依次检查其原型,从而遍历原始对象的原型链,直到找到匹配项或达到其结尾。

一些JavaScript实现允许直接访问[[原型]]属性,例如通过名为__proto__的非标准属性。一般来说,只有在创建对象的过程中才能设置对象的原型:如果通过new Func()创建一个新对象,则对象的[[原型]]属性将设置为Func.prototype引用的对象。

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

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


我扮演着一个javascript老师的角色,当我讲课时,原型概念一直是一个有争议的话题。我花了一段时间才想出一个很好的方法来澄清这个概念,现在在本文中,我将尝试解释javascript.prototype是如何工作的。好的。

这是一个非常简单的基于原型的对象模型,在解释过程中会被视为一个样本,目前还没有评论:好的。

1
2
3
4
5
6
7
function Person(name){
    this.name = name;
}
Person.prototype.getName = function(){
    console.log(this.name);
}
var person = new Person("George");

在讨论原型概念之前,我们必须考虑一些关键点。好的。1-javascript函数的实际工作方式:

为了进行第一步,我们必须弄清楚,javascript函数实际上是如何工作的,作为一个类函数,在其中使用this关键字,或者作为一个带有参数的常规函数,它做什么,返回什么。好的。

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

所以在这个步骤中,functionsobjectsthis关键字是我们所拥有的。好的。

第一个问题是,不使用new关键字,this关键字如何有用。好的。

为了回答这个问题,假设我们有一个空对象,有两个函数,比如:好的。

1
2
3
4
5
6
var person = {};
function Person(name){  this.name = name;  }

function getName(){
    console.log(this.name);
}

现在不使用new关键字,我们如何使用这些函数。因此,javascript有三种不同的方法:好的。a.第一种方法是将函数作为常规函数调用:

1
2
Person("George");
getName();//would print the"George" in the console

在这种情况下,这将是当前上下文对象,通常是浏览器中的全局window对象或Node.js中的GLOBAL对象。这意味着我们将在浏览器中使用window.name,在node.js中使用global.name,其值为"george"。好的。B.我们可以将它们作为对象的属性附加到对象上。

-最简单的方法是修改空的Person对象,例如:好的。

1
2
person.Person = Person;
person.getName = getName;

我们可以这样称呼他们:好的。

1
2
person.Person("George");
person.getName();// -->"George"

现在,Person的目标是:好的。

1
Object {Person: function, getName: function, name:"George"}

-另一种将属性附加到对象的方法是使用该对象的prototype,该对象可以在任何名称为__proto__的javascript对象中找到,我在摘要部分尝试对其进行一些解释。因此,我们可以通过这样做得到类似的结果:好的。

1
2
person.__proto__.Person = Person;
person.__proto__.getName = getName;

但是我们实际做的就是修改Object.prototype,因为每当我们使用文本({ ... }创建一个javascript对象时,它都是基于Object.prototype创建的,这意味着它作为一个名为__proto__的属性附加到新创建的对象上,所以如果我们改变它,就像我们以前对代码sn所做的那样ippet,所有的javascript对象都会改变,这不是一个好的实践。那么现在最好的做法是:好的。

1
2
3
4
person.__proto__ = {
    Person: Person,
    getName: getName
};

现在其他的物体都是和平的,但这似乎仍然不是一个好的实践。所以我们还有一个解决方案,但是要使用这个解决方案,我们应该回到创建了person对象(var person = {};)的代码行,然后像这样更改它:好的。

1
2
3
4
5
var propertiesObject = {
    Person: Person,
    getName: getName
};
var person = Object.create(propertiesObject);

它所做的是创建一个新的javascript Object并将propertiesObject附加到__proto__属性。为了确保你能做到:好的。

1
console.log(person.__proto__===propertiesObject); //true

但这里的难点在于,您可以访问person对象第一级的__proto__中定义的所有属性(有关详细信息,请阅读摘要部分)。好的。

如您所见,使用这两种方法中的任何一种,this都将精确地指向person对象。好的。c.javascript有另一种方法向函数提供this,它使用call或apply来调用函数。

The apply() method calls a function with a given this value and
arguments provided as an array (or an array-like object).

Ok.

和好的。

The call() method calls a function with a given this value and
arguments provided individually.

Ok.

这种方式是我最喜欢的,我们可以很容易地调用我们的函数,比如:好的。

1
Person.call(person,"George");

或好的。

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

getName.call(person);  
getName.apply(person);

这三种方法是确定.prototype功能的重要初始步骤。好的。2-new关键字是如何工作的?

这是了解.prototype功能的第二步。这是我用来模拟过程的步骤:好的。

1
2
function Person(name){  this.name = name;  }
my_person_prototype = { getName: function(){ console.log(this.name); } };

在这一部分中,我将尝试在不使用new关键字和prototype的情况下,在使用new关键字时,采取javascript所采取的所有步骤。所以当我们执行new Person("George")时,person函数作为一个构造器,这就是javascript所做的,一个接一个:好的。首先,它生成一个空对象,基本上是一个空散列,比如:

1
var newObject = {};

b.javascript的下一步是将所有原型对象附加到新创建的对象上。

我们这里的my_person_prototype与原型对象类似。好的。

1
2
3
for(var key in my_person_prototype){
    newObject[key] = my_person_prototype[key];
}

这不是JavaScript实际附加在原型中定义的属性的方式。实际的方法与原型链的概念有关。好的。A.&B.通过执行以下操作,您可以获得完全相同的结果,而不是这两个步骤:

1
2
3
4
5
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"

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

1
newObject.getName();

c.然后它将该对象赋给构造函数,

我们可以用我们的样本来实现这一点,比如:好的。

1
Person.call(newObject,"George");

或好的。

1
Person.apply(newObject, ["George"]);

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

现在,模拟其他步骤之前的最终结果是:对象名称:"乔治"好的。总结:

基本上,当你对一个函数使用新的关键字时,你是在调用它,而这个函数是一个构造函数,所以当你说:好的。

1
new FunctionName()

javascript在内部生成一个对象,一个空散列,然后将该对象提供给构造函数,然后构造函数可以做它想要做的任何事情,因为该构造函数内部是刚刚创建的对象,如果您没有在函数中使用RETURN语句,或者如果您已经放置了一个OCx1〔0〕在你的功能体的末端。好的。

所以当JavaScript查找对象的属性时,它首先要做的就是在该对象上查找它。还有一个秘密属性[[prototype]],我们通常把它和__proto__一样,这个属性就是javascript接下来看到的。当它浏览__proto__时,就又是另一个javascript对象而言,它有自己的__proto__属性,它不断向上直到到达下一个__proto__为空的点。该点是javascript中其__proto__属性为空的唯一对象是Object.prototype对象:好的。

1
console.log(Object.prototype.__proto__===null);//true

这就是继承在JavaScript中的工作原理。好的。

The prototype chain好的。

换言之,当您在函数上有一个原型属性,并且对此调用了一个新的属性时,在javascript完成对新创建的属性对象的查看之后,它将查看函数的.prototype,并且该对象可能有自己的内部原型。等等。好的。好啊。


prototype允许您创建类。如果不使用prototype,则会变成静态的。

下面是一个简短的例子。

1
2
var obj = new Object();
obj.test = function() { alert('Hello?'); };

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

如以下代码所示

1
2
3
4
5
6
7
function obj()
{
}

obj.prototype.test = function() { alert('Hello?'); };
var obj2 = new obj();
obj2.test();

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

以上是我的理解。我正在把它变成一个社区维基,这样人们可以在我错的时候纠正我。


原型的七个Koans

当慈三经过深思熟虑,下到火狐山时,他的头脑清醒而平静。

然而,他的手却不安,他自己抓着一把刷子,匆匆记下了下面的笔记。

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

  • 原型属性,如obj.prototype中所示。

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

    可通过ES5 Object.getPrototypeOf()检索。

    火狐使它可以作为扩展通过__proto__属性进行访问。ES6现在提到了__proto__的一些可选要求。

1)这些概念用于回答问题:

When I do obj.property, where does JS look for .property?

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

2)

  • __proto__用于点.属性查找,如obj.property中所示。
  • .prototype不直接用于查找,只是间接用于确定__proto__在使用new创建对象时的查找。

查找顺序为:

  • 添加obj.p = ...Object.defineProperty(obj, ...)obj属性
  • obj.__proto__的性质
  • obj.__proto__.__proto__的性质等
  • 如果有的__proto__null,返回undefined

这就是所谓的原型链。

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

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

  • new

    1
    2
    var F = function() {}
    var f = new F()

    那么,new设置了:

    1
    f.__proto__ === F.prototype

    这就是使用.prototype的地方。

  • Object.create

    1
     f = Object.create(proto)

    集合:

    1
    f.__proto__ === proto

4)代码:

1
2
var F = function() {}
var f = new F()

对应下图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
(Function)       (  F  )                                      (f)
 |  ^             | | ^                                        |
 |  |             | | |                                        |
 |  |             | | +-------------------------+              |
 |  |constructor  | |                           |              |
 |  |             | +--------------+            |              |
 |  |             |                |            |              |
 |  |             |                |            |              |
 |[[Prototype]]   |[[Prototype]]   |prototype   |constructor   |[[Prototype]]
 |  |             |                |            |              |
 |  |             |                |            |              |
 |  |             |                | +----------+              |
 |  |             |                | |                         |
 |  |             |                | | +-----------------------+
 |  |             |                | | |
 v  |             v                v | v
(Function.prototype)              (F.prototype)
 |                                 |
 |                                 |
 |[[Prototype]]                    |[[Prototype]]
 |                                 |
 |                                 |
 | +-------------------------------+
 | |
 v v
(Object.prototype)
 | | ^
 | | |
 | | +---------------------------+
 | |                             |
 | +--------------+              |
 |                |              |
 |                |              |
 |[[Prototype]]   |constructor   |prototype
 |                |              |
 |                |              |
 |                | -------------+
 |                | |
 v                v |
(null)           (Object)

此图显示了许多语言预定义的对象节点:nullObjectObject.prototypeFunctionFunction.prototype。我们的两行代码只创建了ffF.prototype

5).constructor通常来自F.prototype通过.查找:

1
2
3
4
5
f.constructor === F
!f.hasOwnProperty('constructor')
Object.getPrototypeOf(f) === F.prototype
F.prototype.hasOwnProperty('constructor')
F.prototype.constructor === f.constructor

当我们编写f.constructor时,javascript将.查找为:

  • f没有.constructor
  • f.__proto__ === F.prototype.constructor === F所以拿着吧

结果f.constructor == F是直观正确的,因为f用于构造f,例如set字段,与经典OOP语言非常相似。

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

ES6添加了classextends关键字,这些关键字只是简单的语法糖,用于先前可能的原型操作疯狂。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
    }
}
1
2
3
4
// Inheritance syntax works as expected.
(new C(1)).inc() === 2
(new D(1)).inc() === 2
(new D(1)).inc2() === 3
1
2
3
4
5
6
7
//"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
1
2
3
4
5
6
7
//"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
1
2
3
4
5
6
7
8
9
// 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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
      __proto__
(C)<---------------(D)         (d)
| |                |           |
| |                |           |
| |prototype       |prototype  |__proto__
| |                |           |
| |                |           |
| |                | +---------+
| |                | |
| |                | |
| |                v v
|__proto__        (D.prototype)
| |                |
| |                |
| |                |__proto__
| |                |
| |                |
| | +--------------+
| | |
| | |
| v v
| (C.prototype)--->(inc)
|
v
Function.prototype


在阅读了这个线程之后,我对javascript原型链感到困惑,然后我找到了这些图表

http://iwiki.readthedocs.org/en/latest/javascript/js-core.html继承*[[protytype]]* and EDOCX1〔0〕 property of function objects

通过原型链显示javascript继承是一个清晰的图表

http://www.javascriptbank.com/javascript/article/javascript_古典_继承/

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

prototype chain ultimately falls back to Object.prototype.

prototype chain can be technically extended as long as you want, each time by setting the prototype of the subclass equal to an object of the parent class.

希望这对您理解JavaScript原型链也有帮助。


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

1
object [[Prototype]] -> anotherObject

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

1
object [[Prototype]] -> aFunction.prototype

有些环境将[[原型]]暴露为__proto__

1
anObject.__proto__ === anotherObject

创建对象时创建[[原型]]链接。

1
2
3
4
5
6
7
8
9
10
11
// (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

所以这些陈述是等价的:

1
2
3
var object = Object.create(Object.prototype);
var object = { __proto__: Object.prototype }; // ES6 only
var object = new Object;

new语句不显示链接目标(Object.prototype本身);而是由构造函数(Object隐含目标。

记得:

  • 每个对象都有一个链接,[[原型]],有时作为__proto__公开。
  • 每个函数都有一个prototype属性。
  • 使用new创建的对象链接到其构造函数的prototype属性。
  • 如果函数从未用作构造函数,则其prototype属性将不使用。
  • 如果不需要构造函数,请使用object.create而不是new


这篇文章很长。但我相信它能帮你解决大部分问题关于JavaScript继承的"原型"性质。甚至更多。请阅读全文。好的。

javascript基本上有两种数据类型好的。

  • 非对象
  • 物体

非对象好的。

以下是非对象数据类型好的。

  • 一串
  • 数字(包括NaN和Infinity)
  • 布尔值(真、假)
  • 未定义

当使用typeof运算符时,这些数据类型返回如下好的。

"string literal"的类型(或包含string literal的变量)=="string"好的。

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

typeof true(或false,或包含true或false的变量)=='布尔值'好的。

未定义的类型(或未定义的变量或包含未定义的变量)=='未定义'好的。

字符串、数字和布尔数据类型既可以表示为对象,也可以表示为非对象。当它们表示为对象时,它们的typeof始终为=='对象'。一旦我们了解了对象数据类型,我们将回到这一点。好的。

物体好的。

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

  • 函数类型对象
  • 非函数类型对象
  • 函数类型对象是使用typeof运算符返回字符串"function"的对象。所有用户定义的函数和所有可以使用new操作符创建新对象的javascript内置对象都属于此类。例如好的。

    • 对象
    • 布尔
    • 数组
    • 类型化数组
    • 正则表达式
    • 功能
    • 可以使用new运算符创建新对象的所有其他内置对象
    • 函数userDefinedFunction()/*用户定义的代码*/

    所以,typeof(object)==类型of(string)==类型of(number)==类型of(boolean)===类型of(array)===类型of(regexp)===类型of(function)===类型of(userdefinedfunction)==='函数'好的。

    所有的函数类型对象实际上都是内置的javascript对象函数的实例(包括函数对象,即它是递归定义的)。就好像这些对象是按以下方式定义的好的。

    1
    2
    3
    4
    5
    6
    7
    8
    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")

    如前所述,函数类型对象可以使用new操作符进一步创建新对象。例如,可以使用好的。

    1
    2
    3
    4
    5
    6
    7
    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()

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

    1
    var b=new a() //error. a is not typeof==='function'

    内置对象数学类型为typeof=='object'。因此,new运算符无法创建类型为math的新对象。好的。

    1
    var b=new Math() //error. Math is not typeof==='function'

    还要注意,对象、数组和regexp函数可以创建一个新对象,而不必使用operator new。但是下面的没有。好的。

    1
    2
    3
    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'

    用户定义的函数是特殊情况。好的。

    1
    var a=UserDefinedFunction() //may or may not create an object of type UserDefinedFunction() based on how it is defined.

    由于函数类型对象可以创建新对象,因此它们也被称为构造函数。好的。

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

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

    1
    2
    3
    function UserDefinedFunction()
    {
    }

    跟踪自动发生好的。

    1
    UserDefinedFunction.prototype={constructor:UserDefinedFunction}

    此"原型"属性仅存在于函数类型对象中(并且从不在非函数类型对象中)。好的。

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

    在对象中创建的用于引用继承属性的"内部引用"称为对象的原型(它引用了构造函数的"原型"属性引用的对象,但与之不同)。对于任何对象(函数或非函数),都可以使用object.getprototypeof()方法来检索。使用这种方法,我们可以跟踪对象的原型链。好的。

    此外,创建的每个对象(函数类型或非函数类型)都有一个"构造函数"属性,该属性继承自构造函数函数的原型属性引用的对象。默认情况下,此"构造函数"属性引用创建它的构造函数函数(如果构造函数函数的默认"原型"未更改)。好的。

    对于所有函数类型对象,构造函数函数始终是函数函数()好的。

    对于非函数类型的对象(例如javascript内置数学对象),构造函数函数是创建它的函数。对于数学对象,它是函数对象()。好的。

    在没有任何支持代码的情况下,上面解释的所有概念都有点让人望而生畏。请逐行阅读下面的代码以理解这个概念。试着执行它以获得更好的理解。好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    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"

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

    1
    2
    3
    4
    5
    6
    7
    var o=Starting object;

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

    }while(o=Object.getPrototypeOf(o))

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

    • 每一个函数对象(包括内置函数对象)。 object.prototype function.prototype > >。
    • 简单对象(创造了新的城市(或对象){ }包括内置的数学对象object.prototype)> >。
    • 对象的创新与新的或object.create >一个或多个原型系统object.prototype > >。

    用于创建对象的原型是"没有任何使用下面的:

    好。

    1
    2
    var o=Object.create(null)
    alert(Object.getPrototypeOf(o)) //Displays null

    一位可能会认为"原型性质的constructor两个空应创建的对象和一个空的原型。然而,在这些用例中创造了新的原型对象的两个object.prototype冰上和冰上的两个constructor功能对象。这是demonstrated市下面的代码

    好。

    1
    2
    3
    4
    5
    6
    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

    下面的文章总结本

    好。

    • 有两类类型对象的功能和非功能型
    • 功能型只读对象可以创建一个新的对象,使用新的运营商。这样的目标是创造了非功能型对象。《非功能型对象不能继续创造新的对象进行操作。

      好。

    • 全功能型城市的默认对象有一个"原型"的性质。这个原型性质的证明人"这是一个对象"constructor"性质,证明人的城市功能类型的默认对象本身。

      好。

    • 所有对象(非功能型和功能型)有一个"constructor"性质,违约的证明人的城市功能类型的对象/ constructor,创造了它。

      好。

    • 这是我们创造了每一个对象引用的对象internally证明人的城市 "原型"性质的constructor,创造了它。这冰的已知对象作为对象的原型创造了(这是从不同的功能类型对象的"原型"的性质,它的证明人)。这种方式创造了可以直接访问对象的方法和属性定义的对象引用的constructor的"原型"("性能对象的创建时间)。

      好。

    • 的对象的原型(鸭hence继承其财产的名称可以使用"retrieved object.getprototypeof()方法。事实上这个方法 可以用于navigating的全部对象的原型链。

      好。

    • 在每个对象的原型链ultimately痕迹后object.prototype(这两个对象的使用创造了《冰object.create(null),在这种情况下有没有对象的原型)。

      好。

    • {T(New Array())=对象是城市设计的语言,而不是一个错误AA Douglas Crockford芒城

      好。

    • Setting"原型性质的constructor两个空(或undefined号、威胁、褶皱、字符串)将不创建对象有一个空的原型。在这些用例中创造了新的原型对象的两个object.prototype冰上和冰上的两个constructor功能对象。

      好。

    希望这helps。

    好。 好的。


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

    原型链

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

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

    它对于继承也很有用,因为原型链可以由许多其他对象组成。


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

    enter image description here好的。

    如果我们在Tree function上使用new操作符,我们称它为constructor函数。好的。

    enter image description here好的。

    每个JavaScript函数都有一个prototype函数。当你登录Tree.prototype时,你会得到…好的。

    enter image description here好的。

    如果您查看上面的console.log()输出,您也可以看到Tree.prototype上的构造函数属性和__proto__属性。__proto__代表这个function是以prototype为基础的,由于这只是一个普通的JavaScript function没有设置inheritance,所以它指的是Object prototype这是一个内置在javascript中的东西……好的。

    https://developer.mozilla.org/en-us/docs/web/javascript/reference/global_objects/object/prototype好的。

    这有点像.toString, .toValue, .hasOwnProperty等…好的。

    我的Mozilla带来的__proto__被否决,取而代之的是Object.getPrototypeOf方法来获得object's prototype。好的。

    enter image description here好的。

    1
    Object.getPrototypeOf(Tree.prototype); // Object {}

    让我们给我们的Treeprototype添加一个方法。好的。

    enter image description here好的。

    我们修改了Root并在其中添加了function分支。好的。

    enter image description here好的。

    这意味着当你创建一个Treeinstance时,你可以称之为branch方法。好的。

    enter image description here好的。

    我们也可以将primitivesobjects添加到我们的prototype中。好的。

    enter image description here好的。

    让我们把一个child-tree添加到我们的Tree中。好的。

    enter image description here好的。

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

    enter image description here好的。

    Child现在有自己的prototype__proto__指向TreeTree's prototype指向Object。好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    Child  
    |
     \
      \
       Tree.prototype
       - branch
       |
       |
        \
         \
          Object.prototype
          -toString
          -valueOf
          -etc., etc.

    现在您创建一个Childinstance,并调用branch,它最初在Tree中可用。我们还没有在Child prototype上定义我们的branch。但是,在孩子所继承的《以东记》中(53)。好的。

    enter image description here好的。

    在JS中,一切都不是一个对象,一切都可以像一个对象一样工作。好的。

    JavaScriptstrings, number, booleans, undefined, null.这样的原语,它们不是object(i.e reference types)而是可以充当Object的原语。我们来看一个例子。好的。

    enter image description here好的。

    在这个列表的第一行中,一个primitive字符串值被分配给name。第二行将名称视为Object,并使用点表示法调用charAt(0)。好的。

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

    enter image description here好的。

    String object只存在于一个语句中,然后被销毁(一个称为autoboxing的过程)。让我们再回到我们的prototypalinheritance。好的。

    • Javascript支持通过delegation继承,基于prototypes
    • 每个Function有一个prototype属性,它指的是另一个对象。
    • object本身或通过prototype链,如果不存在

    JS中的prototypeyields你对另一个object的父母的对象。[I.。授权]delegation意味着如果你不能做什么,你会告诉别人帮你做。好的。

    enter image description here好的。

    https://jsfiddle.net/say0tzpl/1/好的。

    如果您查看上面的小提琴,dog可以访问toString方法,但它在其中不可用,但可以通过委托给Object.prototype的原型链使用。好的。

    enter image description here好的。

    如果你看下面的一个,我们试图访问每个Function中可用的call方法。好的。

    enter image description here好的。

    https://jsfiddle.net/rknffckck/好的。

    如果您查看上面的小提琴,Profile函数可以访问call方法,但它在其中不可用,而是通过委托给Function.prototype的原型链可用。好的。

    enter image description here好的。

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

    enter image description here好的。

    上面的图表看起来有点复杂,但给出了prototype chaining的工作原理。让我们慢慢来:好的。

    有两个实例b1b2,其构造函数为Bar,父级为foo,有原型链identifyspeak通过BarFoo的两种方法。好的。

    enter image description here好的。

    https://jsfiddle.net/kbp7jr7n/好的。

    如果您查找上面的代码,我们有Foo构造函数,它有identify()方法,Bar构造函数,它有speak方法。我们创建了两个Bar实例b1b2,其父类型为Foo。现在,在调用Barspeak方法的同时,我们可以通过prototype链识别谁在呼叫该语音。好的。

    enter image description here好的。

    Bar现在拥有Foo的所有方法,这些方法在其prototype中定义。让我们更深入地了解Object.prototypeFunction.prototype以及它们之间的关系。如果您查找Foo的构造函数,BarobjectFunction constructor的构造函数。好的。

    enter image description here好的。

    BarprototypeFooFooprototypeobject,如果仔细观察,FooprototypeObject.prototype有关。好的。

    enter image description here好的。

    在结束之前,我们先用一小段代码来总结上面的所有内容。我们在这里使用instanceof操作符来检查object是否在其prototype链中包含constructorprototype属性,下面总结了整个大图。好的。

    enter image description here好的。

    我希望这个添加是一些信息,我知道这可能是一个很大的把握…简单来说,它只是链接到对象的对象!!!!!好的。好啊。


    what is the exact purpose of this".prototype" property?

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

    把原型想象成一个额外的vtable指针。当原始类中缺少某些成员时,将在运行时查找原型。


    它可能有助于将原型链分为两类。好的。

    考虑建造师:好的。

    1
     function Person() {}

    Object.getPrototypeOf(Person)的值是一个函数。实际上,它是Function.prototype。由于Person被创建为一个函数,所以它与所有函数具有相同的原型函数对象。它与Person.__proto__相同,但不应使用该属性。不管怎样,有了Object.getPrototypeOf(Person),你实际上走上了所谓的原型链的阶梯。好的。

    向上的链条如下:好的。

    &PersonFunction.prototypeObject.prototype(终点)好的。

    重要的是,这个原型链与Person可以构建的对象几乎没有关系。这些被构造的对象有自己的原型链,并且这个链可能没有与上面提到的那个相似的祖先。好的。

    以该对象为例:好的。

    1
    var p = new Person();

    P与人没有直接的原型链关系。他们的关系不同。对象P有自己的原型链。使用Object.getPrototypeOf可以发现链如下:好的。

    &pPerson.prototypeObject.prototype(终点)好的。

    这个链中没有函数对象(尽管可能是这样)。好的。

    因此,Person似乎与两种生活在自己生活中的锁链有关。要从一条链"跳"到另一条链,请使用:好的。

  • .prototype:从构造器链跳到创建的对象链。因此,仅为函数对象定义此属性(因为new只能用于函数)。好的。

  • .constructor:从创建的对象链跳转到构造函数链。好的。

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

    enter image description here好的。

    总结:好的。

    The prototype property gives no information of the subject's prototype chain, but of objects created by the subject.

    Ok.

    不足为奇,prototype房地产的名称会导致混淆。如果这片地产被命名为prototypeOfConstructedInstances或类似的名字,可能会更清楚。好的。

    您可以在两个原型链之间来回跳跃:好的。

    1
    Person.prototype.constructor === Person

    这种对称性可以通过向prototype属性显式分配不同的对象来破坏(稍后将详细介绍)。好的。创建一个函数,获取两个对象

    Person.prototype是在创建函数Person的同时创建的对象。它使用EDOCX1[2]作为构造函数,即使该构造函数尚未实际执行。因此,同时创建两个对象:好的。

  • Person函数的研究
  • 面向这法案将为prototype当函数是被称为作为一个constructor
  • 都是虚构的,但他们有不同的角色:面向建筑的功能,而其他的面向,代表的prototype of any面向那将函数的构造。在prototype面向将成为《亲子法的对象在其prototype链。

    /好的。

    从一个函数是也安的面向,它也有其自身的父母在其自身的prototype链,但回忆说,这些双链是什么不同的事情。

    /好的。

    这里是一些equalities那手术grasp的问题……所有这些true打印:

    /好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    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);

    /好的。 综合水平的prototype链

    尽管从一prototype创建面向的是当你创建一个constructor函数,你可以忽视,面向assign,和另一个面向,这应该是用来作为prototype任何subsequent instances创造通过这constructor。。。。。。。

    /好的。

    设计实例:

    /好的。

    1
    2
    3
    4
    function Thief() { }
    var p = new Person();
    Thief.prototype = p; // this determines the prototype for any new Thief objects:
    var t = new Thief();

    现在的prototype链T是自然的一步比,P:

    /好的。

    nbsp nbsp &;&;& nbsp;nbsp;tpPerson.prototype→→→Object.prototype(端点)

    /好的。

    其他prototype链是不自然:ThiefPerson是siblings共享同样的父母在他们的prototype链:

    /好的。

    nbsp nbsp &;&;& nbsp;nbsp Person;} nbsp nbsp &;&;& nbsp nbsp &;&;Thiefnbsp;}→→Function.prototypeObject.prototype(端点)

    /好的。

    在presented简慢了图形然后可以被扩展到这个(原Thief.prototype是左二):

    /好的。

    enter image description here

    /好的。

    蓝色的线代表prototype链,其他色系代表其他的关系:

    /好的。

    • 之间的关系的对象和其constructor
    • 之间的一个constructor和prototype面向那将被用于constructing对象

    好的。


    面向对象的javascript的权威指南-一个非常简洁清晰的30分钟视频解释问题(原型继承主题从5:45开始,尽管我宁愿听整个视频)。本视频作者还制作了javascript对象可视化工具网站http://www.objectplayplay.com/.enter image description hereenter image description here


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

    如果不存在obj_n.prop_X,则检查obj_n+1.prop_X,其中obj_n+1 = obj_n.[[prototype]]

    如果最终在k-th原型对象中找到了prop_X,那么

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

    您可以通过以下属性找到javascript对象关系图:

    js objects graph

    http://jsobts.org网站


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


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

    原型就像一个模型,基于这个模型你可以创建一个产品。要理解的关键点是,当您使用另一个对象作为原型创建一个对象时,原型和产品之间的链接将一直持续。例如:

    1
    2
    3
    4
    5
    var model = {x:2};
    var product = Object.create(model);
    model.y = 5;
    product.y
    =>5

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

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

    • 当一个属性被访问来读取它的值时,它会在作用域链中查找。对变量的搜索从产品开始,一直到它的原型。如果在搜索中找到这样一个变量,搜索将在该变量处停止,并返回该值。如果在作用域链中找不到这样的变量,则返回UNDEFINED。
    • 当一个属性被写入(更改)时,该属性总是被写入到产品对象上。如果产品还没有这样的属性,则隐式地创建和写入它。

    这种使用原型属性的对象链接称为原型继承。很简单,同意吗?


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

    • 函数的.prototype属性。
    • 所有对象的[[Prototype]]属性[2]。

    这是两件不同的事情。

    [[Prototype]]财产:

    这是存在于所有[2]对象上的属性。

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

    [[Prototype]]属性是构成[[Prototype]]链的一部分。例如,当对一个对象执行[[Get]][[Set]]操作时,将检查该[[Prototype]]对象链:

    1
    2
    3
    var obj = {}
    obj.a         // [[Get]] consults prototype chain
    obj.b = 20    // [[Set]] consults prototype chain

    .prototype财产:

    这是一个仅在函数上找到的属性。使用非常简单的函数:

    1
    function Bar(){};

    .prototype属性拥有一个对象,当您执行var b = new Bar操作时,该对象将被分配给b.[[Prototype]]。您可以轻松检查:

    1
    2
    3
    4
    // 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

    其中一个最重要的.prototypes是Object函数。这个原型保存了所有[[Prototype]]链包含的原型对象。在此基础上,定义了新对象的所有可用方法:

    1
    2
    // Get properties that are defined on this object
    console.log(Object.getOwnPropertyDescriptors(Object.prototype))

    现在,由于.prototype是一个对象,所以它具有[[Prototype]]属性。当你不给Function.prototype赋值时,.prototype[[Prototype]]指向原型对象(Object.prototype)。这将在您创建新函数时自动执行。

    这样,只要你做new Bar;的时候,原型链就为你建立起来了,你就可以得到Bar.prototype上定义的一切,以及Object.prototype上定义的一切:

    1
    2
    3
    4
    5
    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)

    当您确实对Function.prototype进行赋值时,您所要做的就是扩展原型链以包含另一个对象。这就像是在一个单独链接的列表中插入。

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

    [1:这不会混淆任何人;在许多实现中通过__proto__属性提供。【2】:除null外,其余均为。


    考虑以下keyValueStore对象:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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; }
        };
    })();

    我可以通过执行以下操作创建此对象的新实例:

    1
    kvs = keyValueStore.create();

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

    • data
    • get
    • set
    • delete
    • getLength

    现在,假设我们创建这个keyValueStore对象的100个实例。尽管getsetdeletegetLength对这100个实例中的每一个都会做完全相同的事情,但每个实例都有它自己的函数副本。

    现在,想象一下,如果您只有一个getsetdeletegetLength拷贝,那么每个实例都引用相同的函数。这将更好地提高性能,并且需要更少的内存。

    原型就是这样出现的。原型是继承但不被实例复制的属性的"蓝图"。这意味着对于一个对象的所有实例,它在内存中只存在一次,并且由所有这些实例共享。

    现在,再考虑一下keyValueStore对象。我可以这样重写:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    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; }
        };
    })();

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


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

    Simple objects inheritanse


    我总是喜欢用类比来理解这类事情。"在我看来,与类低音继承相比,"原型继承"是相当混乱的,尽管原型是非常简单的范例。事实上,对于原型,实际上没有继承,因此名称本身及其本身具有误导性,它更像是一种"委托"。

    想象一下……

    你在上高中,你在上课,今天要做一个测验,但是你没有一支笔来填写答案。呸!

    你坐在你的朋友芬尼乌斯旁边,他可能有一支笔。你问他,他环顾办公桌,但没有说"我没有笔",他是一个很好的朋友,如果他有笔,他会和他的另一个朋友德普核对。德普确实有一支备用的钢笔,把它传给芬尼乌斯,芬尼乌斯把它传给你,让你完成测验。德普已将笔委托给芬尼乌斯,芬尼乌斯已将笔委托给你使用。

    重要的是,德普不会把笔给你,因为你和他没有直接的关系。

    这是一个原型如何工作的简化示例,其中搜索数据树以查找您要查找的内容。


    总结:

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

    例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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.

    为什么这有用:

    Javascript在查找名为"原型继承"的对象的属性时有一种机制,其基本功能如下:

    • 首先检查属性是否位于对象本身。如果是,则返回此属性。
    • 如果属性不在对象本身上,它将"向上爬升原链"。它主要查看proto属性所引用的对象。在这里,它检查proto引用的对象上的属性是否可用。
    • 如果属性不在proto对象上,它将沿着proto链一直向上爬到对象对象上。
    • 如果在对象及其原型链上找不到该属性,它将返回未定义的。

    例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    function Person(name) {
      this.name = name;
    }

    let mySelf = new Person('Willem');

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

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

    更新:

    __proto__属性已被弃用,尽管它在大多数现代浏览器中实现,获得原型对象引用的更好方法是:

    Object.getPrototypeOf()


    另一个显示原型、原型和构造函数关系的方案:enter image description here


    只是您已经有了一个使用Object.new的对象,但是在使用构造函数语法时,您仍然没有对象。


    原型通过克隆现有对象来创建新对象。所以说真的,当我们考虑原型的时候,我们真的可以考虑克隆或者复制某个东西,而不是把它编造出来。


    了解对象的原型(可通过Object.getPrototypeOf(obj)或已弃用的obj.__proto__属性获得)与构造函数函数的原型属性之间的区别是很重要的。前者是每个实例上的属性,后者是构造函数上的属性。也就是说,Object.getPrototypeOf(new Foobar())指的是与Foobar.prototype相同的对象。

    参考:https://developer.mozilla.org/en-us/docs/learn/javascript/objects/object_原型