题 JavaScript .prototype如何工作?


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

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

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

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


编辑

这些 幻灯片 真的帮助了解了这个话题。


1858
2018-02-21 12:31


起源


John Resig有一些关于函数原型的幻灯片,这些幻灯片在查看主题时对我有帮助(您还可以对代码进行更改,看看会发生什么......) http://ejohn.org/apps/learn/#64 - John Foster
伟大的参考资料,为了保持这个问题的信息,可能会将John的网站上的一些评论放在你的答案上,以防他的网站发生变化,导致链接不再可用。无论哪种方式+1,帮助了我。 - Chris
+1为您的链接 John Resig的JavaScript Ninja幻灯片#64。从那里开始真的很有帮助,我觉得我正确理解原型。 - a paid nerd
我们真的需要一个功能对象来应用原型吗?如果是,为什么? - Anshul
这可能对您有所帮助: webdeveasy.com/javascript-prototype - Naor


答案:


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

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

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

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


927
2018-02-21 13:33



那么,我是通过在我的短片段中的prototype属性上定义新属性来做错事的? - John Leidegren
我认为这就是将函数对象作为一等公民的意义。 - John Leidegren
我讨厌非标准的东西,特别是在编程语言中,为什么还有一个 原 什么时候显然不需要? - John Leidegren
@ H1D:如果 Class.method 找不到,运行时查找 Class.__proto__.method (更确切地说 Object.getPrototypeOf(Class).method 符合ES5)和 不  Class.prototype.method - Christoph
注意使用 [[原型]] 故意 - ECMA-262用双方括号括起内部属性的名称 - Christoph


在实现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();

到目前为止,我一直在扩展基础对象,现在我创建了另一个对象,然后从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());

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());

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

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

1755
2018-01-24 03:42



我认为stackoverflow上的答案不仅对原始海报有趣,而且对潜伏或来自搜索的其他人的大社区也很有意义。我一直是其中之一,我从老帖子中受益。我想我可以为添加一些代码示例的其他答案做出贡献。关于你的问题:如果你遗漏了新的,它就不起作用了。当我调用myCustomer.sayMyName()时,它返回“myCustomer.sayMyName不是函数”。最简单的方法是尝试使用firebug,看看会发生什么。 - stivlo
据我所知var Person = function(name){...};定义了一个能够构建Person Objects的构造函数。所以还没有Object,只有匿名构造函数被赋给Person。这是一个非常好的解释: helephant.com/2008/08/how-javascript-objects-work - stivlo
警告:此答案忽略了不基于每个实例调用父类构造函数的事实。它起作用的唯一原因是因为他在子构造函数和父构造函数中完成了相同的操作(设置名称)。有关在JavaScript中尝试继承时遇到的常见错误(以及最终解决方案)的更深入解释,请参阅: 这个堆栈溢出帖子 - Aaren Cordova
我注意到这个答案也没有提到通过使用“new Person()”作为原型,你实际上是将“Person”的“name”实例属性设置为“Customer”的静态属性(所以所有客户实例将具有相同的属性)。虽然这是一个很好的基本例子,但请不要这样做。 :)通过将其原型设置为“Person.prototype”,创建一个新的匿名函数作为“桥梁”,然后从中创建一个实例,并将“Customer.prototype”设置为该匿名实例。 - James Wilkins
有关 Customer.prototype = new Person(); 在线,MDN显示了一个使用示例 Customer.prototype = Object.create(Person.prototype)并说明 '这里常见的错误是使用“new Person()”'。 资源 - Rafael Eyng


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


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

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

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

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

要迈出第一步,我们必须弄清楚,JavaScript函数实际上是如何工作的,就像类似函数一样使用 this 它中的关键字或仅作为常规函数及其参数,它的作用和返回的内容。

假设我们要创建一个 Person 对象模型。但是在这一步中我会努力 不使用就做同样的事情 prototype 和 new 关键词

所以在这一步 functionsobjects 和 this 关键字,我们都有。

第一个问题是 怎么样 this 关键字可以使用而不使用 new 关键词

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

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

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

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

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

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

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

湾我们可以 连接 它们是一个对象,作为它的属性

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

person.Person = Person;
person.getName = getName;

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

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

而现在 person 对象就像:

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

- 附加财产的另一种方式 一个对象正在使用 prototype 可以在名称为的任何JavaScript对象中找到的该对象的 __proto__,我试图在摘要部分解释一下。所以我们可以通过这样做得到类似的结果:

person.__proto__.Person = Person;
person.__proto__.getName = getName;

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

person.__proto__ = {
    Person: Person,
    getName: getName
};

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

var propertiesObject = {
    Person: Person,
    getName: getName
};
var person = Object.create(propertiesObject);

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

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

但这里棘手的一点是你可以访问定义的所有属性 __proto__ 在第一级 person 对象(阅读摘要部分以获取更多详细信息)。


如你所见,使用这两种方式中的任何一种 this 会完全指向 person 目的。

C。 JavaScript提供了另一种提供函数的方法 this,正在使用 呼叫 要么 应用 调用该函数。

apply()方法调用具有给定this值的函数   作为数组(或类数组对象)提供的参数。

call()方法调用一个给定此值的函数   论据单独提供。

这种方式是我最喜欢的,我们可以很容易地调用我们的功能:

Person.call(person, "George");

要么

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

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

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


2-如何 new 关键字有效?

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

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

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

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

var newObject = {};

湾JavaScript采取的下一步是 连接 所有原型对象都是新创建的对象

我们有 my_person_prototype 这里类似于原型对象。

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

这不是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"

现在我们可以打电话了 getName 在我们的功能 my_person_prototype

newObject.getName();

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

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

Person.call(newObject, "George");

要么

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

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

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


概要:

基本上,当你使用的时候  函数上的关键字,你正在调用它,该函数作为构造函数,所以当你说:

new FunctionName()

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

因此,当JavaScript在一个对象上查找一个属性时,它所做的第一件事就是它在该对象上查找它。然后有一个秘密财产 [[prototype]] 我们通常喜欢它 __proto__ 这个属性是JavaScript接下来要看的。当它看起来通过 __proto__,就它再次是另一个JavaScript对象而言,它有自己的 __proto__属性,它上升和上升,直到它到达下一个点 __proto__ 一片空白。这一点是JavaScript中唯一的对象 __proto__ attribute为null是 Object.prototype 目的:

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

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

The prototype chain

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


163
2018-02-13 19:32



a)请不要通过复制属性来解释原型b)设置内部 [[原型]] 在构造函数应用于实例之前发生,请更改该顺序c)jQuery在这个问题中完全是offtopic - Bergi
@Bergi:谢谢你的指出,如果你能告诉我现在好吗,我将不胜感激。 - Mehran Hatami
你能说点简单吗?你在所有方面都是正确的,但阅读这些解释的学生可能会第一次感到困惑。拿起任何更简单的例子,让代码解释自己或添加一堆注释来澄清你的意思。 - P.M
@ P.M:感谢您的反馈。我试图让它变得尽可能简单,但我认为你是对的它还有一些模糊的观点。所以我会尝试修改它,也更具描述性。 :) - Mehran Hatami
在“书”结尾处的插图+1 - sargas


prototype 允许你上课。如果你不使用 prototype 然后它变成了静态。

这是一个简短的例子。

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

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

如下面的代码所示

function obj()
{
}

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

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

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


66
2017-11-07 09:48



-1: prototype 是构造函数的属性,而不是实例,即您的代码是错误的!也许你的意思是非标准财产 __proto__ 对象,但那是一个完全不同的野兽...... - Christoph
@Christoph - 感谢您指出。我已经更新了示例代码。 - Ramesh
这是一个很好的答案,但还有更多。 - John Leidegren
还有更多内容......加上JavaScript不是基于类的语言 - 它通过原型处理继承,你需要更详细地介绍差异! - James
我认为这个答案有点误导。 - Armin Cifuentes


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

http://iwiki.readthedocs.org/en/latest/javascript/js_core.html#inheritance *[[protytype]]* and <code>prototype</code> property of function objects

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

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

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

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

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

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


59
2018-05-26 20:40



如果你能提供链接内容的简短摘要,那将是很好的,所以这个答案在链接失效时仍然有用。 - Nicktar
@Nicktar,感谢您的建议,我通过这些链接添加了简单的描述。 - rockXrock
那是一张清晰的图表吗? :) - Nuno_147
一张图说千言万语。这太有用了:) - Mark Robson
你能解释一下吗? [[Prototype]] 手段? - CodyBugstein


原型的七个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

查找顺序是:

  • obj 添加的属性 obj.p = ... 要么 Object.defineProperty(obj, ...)
  • 的性质 obj.__proto__
  • 的性质 obj.__proto__.__proto__, 等等
  • 如果有的话 __proto__ 是 null,回来 undefined

这就是所谓的 原型链

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


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

  • new

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

    然后 new 已设立:

    f.__proto__ === F.prototype
    

    这个 在哪 .prototype 得到了用。

  • Object.create

     f = Object.create(proto)
    

    集:

    f.__proto__ === proto
    

4) 代码:

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

对应下图:

(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.prototypeFunction 和 Function.prototype。我们只创建了2行代码 fF 和 F.prototype


5)  .constructor 通常来自 F.prototype 通过 . 抬头:

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,例如设置字段,就像经典的OOP语言一样。


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

ES6增加了 class 和 extends 关键字,它只是以前可能的原型操纵疯狂的语法糖。

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
    }
}
// Inheritance syntax works as expected.
(new C(1)).inc() === 2
(new D(1)).inc() === 2
(new D(1)).inc2() === 3
// "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
// "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
// 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

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

      __proto__
(C)<---------------(D)         (d)
| |                |           |
| |                |           |
| |prototype       |prototype  |__proto__
| |                |           |
| |                |           |
| |                | +---------+
| |                | |
| |                | |
| |                v v
|__proto__        (D.prototype)
| |                |
| |                |
| |                |__proto__
| |                |
| |                |
| | +--------------+
| | |
| | |
| v v
| (C.prototype)--->(inc)
|
v
Function.prototype

57
2018-06-18 19:48



我不知道你在哪里,但这是最明确的答案! - tomasb
@tomasb谢谢! “我不知道你在哪里得到这个”:在我看到一些动态语言后,我注意到他们班级系统最重要的是如何 . 查找工作(以及制作了多少份数据)。所以我开始明白这一点。其余的是谷歌+博客文章+手头的Js口译员。 :) - Ciro Santilli 新疆改造中心 六四事件 法轮功
这个答案表明了对这个概念的真实和深刻的理解。做得好! - Nir Smadar
我仍然不明白为什么g.constructor === Object因为你说“4)当你做f = new F时,new也会设置f.constructor = F”。你能解释一下吗?无论如何,这是我正在寻找的最佳答案。非常感谢! - nguyenngoc101
@现在一切都很清楚,我投了你的答案。感谢你们对我的帮助。 - nguyenngoc101


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

object [[Prototype]] -> anotherObject

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

object [[Prototype]] -> aFunction.prototype

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

anObject.__proto__ === anotherObject

您在创建对象时创建[[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

所以这些陈述是等价的:

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

一个 new 声明不显示链接目标(Object.prototype)本身;相反,目标是由构造函数隐含的(Object)。

记得:

  • 每个对象都有一个链接[[Prototype]],有时会显示为 __proto__
  • 每个功能都有 prototype 属性。
  • 使用创建的对象 new 与...有联系 prototype 他们的建设者的财产。
  • 如果函数永远不会用作构造函数,那么它就是 prototype 财产将被闲置。
  • 如果您不需要构造函数,请使用 的Object.create 代替 new

33
2018-02-21 12:41



+1用于突出显示Object.create() - Jakob Sternberg
修订版5删除了一些有用的信息,包括Object.create()的信息。看到 修订版4。 - Palec
@Palec应该加回什么? - sam
IMO至少要链接到 Object.create() 文档,@ sam。链接到 __proto__ 和 Object.prototype 将是很好的增强功能。我喜欢你的原型如何与构造函数一起工作的例子 Object.create(),但它们可能是你想要摆脱的长而不太相关的部分。 - Palec
从我所得到的所有讨论(来自经典继承)如果我创建构造函数并尝试使用new运算符创建它的实例我只会获得附加到proto对象的方法和属性,因此必须附加所有方法和proto对象的属性,如果我们想继承,mi吧? - blackHawk


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

原型链

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

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

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


23
2017-11-04 14:08



FF和Chrome支持 原,但不是IE或Opera。 - some
乔治,请澄清一个菜鸟 - “javascript中的类和实例之间没有区别。” - 你能详细说说吗?这个怎么用? - Hamish Grubijan
从我所得到的所有讨论(来自经典继承)如果我创建构造函数并尝试使用new运算符创建它的实例我只会获得附加到proto对象的方法和属性,因此必须附加所有方法和proto对象的属性,如果我们想继承,mi吧? - blackHawk


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

JavaScript基本上有两种数据类型

  • 非物件 
  • 对象

非物件

以下是 非对象 数据类型

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

使用时,这些数据类型会返回 类型 操作者

类型  “字符串字面量” (或包含字符串文字的变量)=== '串'

类型   (或任何数字文字或包含数字文字或变量的变量 NaN或Infynity)=== '数'

类型  真正 (要么  或包含的变量 真正 要么 )=== “布尔”

类型  未定义 (或未定义的变量或包含的变量 未定义)=== “未定义”

 和 布尔 数据类型可以表示为 对象 和 非物件当它们被表示为对象时,它们的typeof始终是==='object'。一旦我们理解了对象数据类型,我们将回到这一点。

对象

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

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

函数类型对象 是返回字符串的那些 '功能' 同 类型 运营商。 所有用户定义的函数以及可以使用new运算符创建新对象的所有内置JavaScript都归入此类别。例如。

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

所以, typeof运算(对象) === typeof运算(字符串) === typeof运算(号码) === typeof运算(布尔) === typeof运算(阵列)  === typeof运算(正则表达式) === typeof运算(功能)  === typeof运算(UserDefinedFunction) === '功能'

一切 函数类型对象 实际上是内置JavaScript对象的实例 功能 (包括 功能 对象,即递归定义)。就好像这些对象已经按照以下方式定义

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")

如上所述, 函数类型对象 可以使用the进一步创建新对象 新运营商。例如,类型的对象 目的布尔排列正则表达式  要么 UserDefinedFunction 可以使用创建

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() 

这样创建的对象都是 非函数类型对象 并返回他们的 类型==='目的'。在所有这些情况下,对象“a”无法进一步创建 使用operator new的对象。所以以下是错误的

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

内置对象 数学 是 类型==='目的'。因此,新运算符无法创建Math类型的新对象。

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

还要注意 目的排列 和 正则表达式 函数可以创建一个新对象,甚至不使用 操作员新。然而,下面的人没有。

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'

用户定义的函数是特例。

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

自从 函数类型对象 可以创建他们也被称为的新对象 构造函数

一切 构造函数/功能 (无论是内置还是用户定义)自动定义时都有一个名为的属性 “原型” 默认情况下,其值设置为对象。该对象本身具有一个名为的属性 “构造” 默认情况下引用它 构造函数/功能 。

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

function UserDefinedFunction()
{
}

以下自动发生

UserDefinedFunction.prototype={constructor:UserDefinedFunction}

这个 “原型”财产 只出现在 函数类型对象  (从来没有 非函数类型对象)。

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

这个 “内部参考” 在对象中创建的用于引用继承属性的内容称为 对象的原型 (引用构造函数引用的对象 “原型” 财产,但与之不同)。对于任何对象(函数或非函数),可以使用它来检索 Object.getPrototypeOf() 方法。使用此方法可以跟踪对象的原型链。

也, 创建的每个对象 (功能类型 要么 非功能类型) 有一个 “构造” 从构造函数的prototype属性引用的对象继承的属性。默认情况下这个 “构造” 属性引用 构造函数 它创造了它(如果 构造函数 默认“原型”未更改)。

对全部 函数类型对象 构造函数总是如此 function Function(){}

对于 非函数类型对象 (例如Javascript Built in Math对象)构造函数是创建它的函数。 对于 数学 它是对象 function 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"

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

var o=Starting object;

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

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

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

  • 每个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

有人可能会认为将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

以下是本文的摘要

  • 有两种类型的对象 功能类型 和 非功能类型
  • 只要 函数类型对象 可以使用。创建一个新对象 操作员新。这样创建的对象是 非功能类型 对象。该 非函数类型对象 无法进一步使用创建对象 操作员新

  • 所有 函数类型对象 默认情况下有一个 “原型” 属性。这个 “原型” 属性引用具有的对象 “构造” 默认情况下引用的属性 功能类型对象 本身。

  • 所有物品(功能类型 和 非功能类型)有一个“构造函数”属性,默认情况下引用该属性 功能类型对象/构造函数 创造了它。

  • 在内部创建的每个对象都引用引用的对象 “原型” 创建它的构造函数的属性。此对象称为已创建 对象的原型 (它与它引用的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。

希望这可以帮助。


23
2017-07-03 21:44