JavaScript .prototype如何工作?

时间:2009-02-21 12:31:18

标签: javascript dynamic-languages prototype-oriented

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

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

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

但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

这些slides确实帮了很多忙。

26 个答案:

答案 0 :(得分:1780)

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

答案 1 :(得分:965)

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

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

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

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

答案 2 :(得分:169)

我扮演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对象模型。但是在这一步中,我将尝试在不使用prototypenew关键字的情况下做同样的事情。

因此,在此步骤 functions objects 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,该对象可以在名称为__proto__的任何JavaScript对象中找到,我试图在摘要部分解释一下。所以我们可以通过这样做得到类似的结果:

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

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

__proto__

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

var person = {};

它的作用是创建新的JavaScript var propertiesObject = { Person: Person, getName: getName }; var person = Object.create(propertiesObject); 并将Object附加到propertiesObject属性。所以要确保你能做到:

__proto__

但这里棘手的一点是你可以访问console.log(person.__proto__===propertiesObject); //true 对象第一层__proto__中定义的所有属性(阅读摘要部分以获取更多细节)。


如您所见,使用这两种方式中的任何一种person都会指向this对象。

℃。 JavaScript提供了另一种方法来提供person函数,该函数使用callapply来调用函数。

  

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

  

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

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

this

Person.call(person, "George");

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


2- //apply is more useful when params count is not fixed Person.apply(person, ["George"]); getName.call(person); getName.apply(person); 关键字的工作原理是什么?

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

.prototype

在本部分中,当您使用function Person(name){ this.name = name; } my_person_prototype = { getName: function(){ console.log(this.name); } }; 关键字时,我将尝试执行JavaScript所采取的所有步骤,而不使用new关键字和prototype。因此,当我们执行new时,new Person("George")函数充当构造函数,这些是JavaScript所做的,逐个:

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

Person

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

我们这里的var newObject = {}; 类似于原型对象。

my_person_prototype

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


一个。 &安培;湾通过执行以下操作,您可以获得完全相同的结果,而不是这两个步骤:

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

现在我们可以调用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();

Person.call(newObject, "George");

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

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


要点:

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

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

JavaScript内部创建一个对象,一个空哈希,然后它将该对象提供给构造函数,然后构造函数可以做任何想做的事情,因为该构造函数内部的 this 就是对象如果您没有在函数中使用return语句,或者如果您在函数体的末尾添加了new FunctionName() ,那么它就会为您提供该对象。

因此,当JavaScript在一个对象上查找一个属性时,它所做的第一件事就是它在该对象上查找它。然后有一个秘密属性 return undefined; ,我们通常会将其视为 [[prototype]] ,该属性就是JavaScript下一步所看到的。当它查看 __proto__ 时,只要它再次是另一个JavaScript对象,它就有自己的 __proto__ 属性,它会上升直到它到达下一个 __proto__ 为空的点。关键是JavaScript中唯一的 __proto__ 属性为null的对象是__proto__对象:

Object.prototype

以及继承如何在JavaScript中运行。

The prototype chain

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

答案 3 :(得分:74)

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函数。

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

答案 4 :(得分:67)

原型的七个Koans

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

然而,他的手却焦躁不安,自己抓起一把刷子,记下下面的笔记。


0)可以调用两种不同的东西"原型":

  • 原型属性,如obj.prototype

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

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

    Firefox可以通过__proto__属性作为扩展程序访问它。 __proto__ obj.property的一些可选要求。


1)这些概念的存在是为了回答这个问题:

  

当我.property时,JS在哪里寻找__proto__

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


<强> 2)

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

查询顺序是:

  • obj.p = ...已添加Object.defineProperty(obj, ...)obj.__proto__
  • 的媒体资源
  • obj.__proto__.__proto__
  • 的属性
  • __proto__的属性,依此类推
  • 如果某些nullundefined,请返回.

这就是所谓的原型链

您可以使用obj.hasOwnProperty('key')Object.getOwnPropertyNames(f)

来避免obj.__proto__查询

3)设置new有两种主要方法:

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

    new

    然后f.__proto__ === F.prototype 设置了:

    .prototype

    是使用Object.create的地方。

  • f = Object.create(proto)

    f.__proto__ === proto
    

    设定:

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

4)代码:

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

对应下图:

null

此图显示了许多语言预定义对象节点:ObjectObject.prototypeFunctionFunction.prototypef。我们的2行代码仅创建了FF.prototype.constructor


5) 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 === Ff.constructor == F,所以请将其

结果F直观正确,因为f用于构建class,例如设置字段,就像经典的OOP语言一样。


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

ES6添加了extendsclass 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

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

{{1}}

答案 5 :(得分:65)

阅读完这篇帖子后,我觉得与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原型链也很有帮助。

答案 6 :(得分:37)

每个对象都有一个内部属性[[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

答案 7 :(得分:26)

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

JavaScript基本上有两种数据类型

  • 非对象
  • 物件

非对象

以下是 非对象 数据类型

  • 字符串
  • 号码(包括NaN和Infinity)
  • 布尔值(true,false)
  • 未定义

当您使用 typeof 运算符

时,这些数据类型会返回

typeof &#34;字符串文字&#34; (或包含字符串文字的变量)=== &#39; string&#39; < /强>

typeof 5 (或任何数字文字或包含数字文字的变量或 NaN或Infynity )= == &#39;号码&#39;

typeof true (或 false 或包含 true false 的变量)=== &#39; boolean&#39;

typeof undefined (或未定义的变量或包含 undefined 的变量)=== &#39; undefined&#39

字符串数字布尔数据类型可以表示为对象非对象。当它们被表示为对象时,它们的typeof总是===&#39; object&#39;。一旦我们理解了对象数据类型,我们就会回到这里。

<强> 物件

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

  1. 功能类型对象
  2. 非功能类型对象
  3. 功能类型对象是使用 typeof 运算符返回字符串&#39;功能&#39; 的功能。 所有用户定义的函数和所有可以通过使用new运算符创建新对象的对象内置的JavaScript都属于此类别。例如。

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

    所以, typeof(对象) === typeof(字符串) === typeof(数字) === typeof(布尔)< / strong> === typeof(数组) === typeof(RegExp) === typeof(功能) === typeof(UserDefinedFunction) === &#39;功能&#39;

    所有 功能类型对象 实际上是内置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")
    

    如上所述, 功能类型对象 可以使用 new运算符进一步创建新对象。例如,对象类型为对象字符串数字布尔数组 RegExp 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() 
    

    这样创建的对象都是 非函数类型对象 并返回 typeof === &#39; object&# 39; 即可。在所有这些情况下,对象&#34; a&#34;无法进一步创造 使用operator new的对象。所以以下是错误的

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

    内置对象数学 typeof === &#39; object&#39; 。因此,新运算符无法创建Math类型的新对象。

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

    另请注意,对象数组 RegExp 功能可以创建新对象,甚至无需使用 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'
    

    用户定义的函数是特例。

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

    由于 功能类型对象 可以创建新对象,因此它们也称为 构造函数

    自动定义时,每个构造函数/函数(无论是内置函数还是用户定义)都有一个名为&#34; prototype&#34; 的属性,其值默认设置为作为一个对象。该对象本身有一个名为&#34; constructor&#34; 的属性,默认情况下引用构造函数/函数

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

    function UserDefinedFunction()
    {
    }
    

    自动发生

    UserDefinedFunction.prototype={constructor:UserDefinedFunction}
    

    &#34;原型&#34;属性仅出现在函数类型对象中 (绝不会在非函数类型对象中)。

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

    在对象中创建的用于引用继承属性的&#34;内部引用&#34; 称为对象的原型(引用构造函数&#34;原型&#34; 属性引用的对象,但与之不同)。对于任何对象(函数或非函数),可以使用 Object.getPrototypeOf()方法检索它。使用此方法可以跟踪对象的原型链。

    此外,创建的每个对象功能类型非功能类型)都有&#34;构造函数&# 34; 属性,它继承自构造函数的prototype属性所引用的对象。默认情况下,&#34;构造函数&#34; 属性引用创建它的构造函数(如果构造函数&#39; 默认值&#34;原型&#34;没有改变)。

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

    对于 非函数类型对象 (例如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对象) - &gt; Function.prototype - &gt; Object.prototype - &gt; null
    • 简单对象(由新的Object()或{}创建,包括内置的Math对象) - &gt; Object.prototype - &gt;空
    • 使用new或Object.create创建的对象 - &gt;一个或多个原型链 - &gt; Object.prototype - &gt;空

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

    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
    

    以下是本文的摘要

    • 有两种类型的对象功能类型非功能类型
    • 只有功能类型对象可以使用运算符new 创建新对象。这样创建的对象是非函数类型对象。 非功能类型对象无法使用 operator new 进一步创建对象。

    • 默认情况下,所有功能类型对象都有&#34;原型&#34; 属性。此&#34;原型&#34; 属性引用一个对象,该对象具有&#34;构造函数&#34; 属性,默认情况下引用函数类型对象本身。

    • 所有对象(函数类型非函数类型)都有一个&#34;构造函数&#34;默认情况下引用创建它的函数类型对象 / 构造函数的属性。

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

    • 可以使用 Object.getPrototypeOf()方法检索对象的原型(以及其继承的属性名称)。实际上这种方法 可用于导航对象的整个原型链。

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

    • typeof(new Array())===&#39; object&#39; 是按语言设计的,而不是Douglas Crockford指出的错误

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

    希望这有帮助。

答案 8 :(得分:25)

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

原型链

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

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

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

答案 9 :(得分:22)

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

enter image description here

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

enter image description here

每个JavaScript函数都有一个prototype。当您记录Tree.prototype时,您会得到......

enter image description here

如果查看上面的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

enter image description here

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

让我们为Tree prototype添加一种方法。

enter image description here

我们修改了Root并为其添加了function分支。

enter image description here

这意味着当您创建instance Tree时,您可以将其称为branch方法。

enter image description here

我们还可以将primitivesobjects添加到Prototype

enter image description here

我们为child-tree添加Tree

enter image description here

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

enter image description here

Tree()现在有自己的Childprototype指向__proto__Tree指向基地Tree's prototype

Object

现在,您创建Child | \ \ Tree.prototype - branch | | \ \ Object.prototype -toString -valueOf -etc., etc. instance并致电Child,该branch最初位于Tree。我们尚未在branch上实际定义Child prototype。但是,在Child继承的Root prototype中。

enter image description here

在JS中,一切都不是对象,一切都可以像对象一样。

Javascript包含strings, 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的过程)。让我们再次回到prototypal inheritance

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

JS中的prototype是一个对象,yields您是另一个object的父级。 [即......代表团] Delegation表示如果您无法做某事,您会告诉其他人为您做这件事。

enter image description here

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

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

enter image description here

如果您查看以下内容,我们会尝试访问每个call中提供的function方法。

enter image description here

https://jsfiddle.net/rknffckc/

如果你查看上面的小提琴,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。现在,在调用speak的{​​{1}}方法时,我们可以通过Bar链识别谁正在调用发言。

enter image description here

prototype现在拥有Bar中定义的Foo的所有方法。让我们进一步深入了解prototypeObject.prototype以及它们之间的关系。如果您查找Function.prototype的构造函数,FooBarObject

enter image description here

Function constructor的{​​{1}}为prototypeBar的{​​{1}}为Foo,如果您密切关注prototype FooObject相关。

enter image description here

在我们关闭之前,让我们在这里用一小段代码包装总结上面的所有内容。我们在此处使用prototype运算符来检查Foo Object.prototype链中instanceof的{​​{1}}属性是否object,以下是对整个大图的总结。

enter image description here

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

答案 10 :(得分:20)

  

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

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

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

答案 11 :(得分:19)

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

考虑构造函数:

 function Person() {}

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

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

PersonFunction.prototypeObject.prototype(终点)

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

以此对象为例:

var p = new Person();

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

pPerson.prototypeObject.prototype(终点)

此链中没有任何功能对象(尽管可能是这样)。

所以Person似乎与两种生活相关的链条有关。要从一个链“跳”到另一个链,请使用:

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

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

  3. 以下是所涉及的两个原型链的直观呈现,表示为列:

    enter image description here

    总结:

      

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

    属性prototype的名称可能导致混淆,这并不奇怪。如果此属性已被命名为prototypeOfConstructedInstances或该行的某些内容,则可能更清楚。

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

    Person.prototype.constructor === Person
    

    可以通过向prototype属性明确指定不同的对象来解决这种对称性(稍后会详细介绍)。

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

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

    1. 函数Person本身
    2. 当函数作为构造函数调用时将充当原型的对象
    3. 两者都是对象,但它们具有不同的角色:函数对象构造,而另一个对象表示函数将构造的任何对象的原型。原型对象将成为其原型链中构造对象的父对象。

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

      以下是一些可以帮助解决问题的平等 - 所有这些都打印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);

      向原型链添加级别

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

      例如:

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

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

      tpPerson.prototypeObject.prototype(终点)

      另一个原型链不再是:ThiefPerson是在原型链中共享同一个父级的兄弟姐妹:

      Person}
      Thief}→Function.prototypeObject.prototype(终点)

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

      enter image description here

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

      • 在对象及其构造函数之间
      • 在构造函数和将用于构造对象的原型对象之间

答案 12 :(得分:16)

The Definitive Guide to Object-Oriented JavaScript - 一个非常简洁明了〜30分钟的问题视频解释(原型继承主题从5:45开始,尽管我宁愿听完整个视频)。此视频的作者还制作了JavaScript对象可视化工具网站http://www.objectplayground.com/enter image description here enter image description here

答案 13 :(得分:14)

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

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

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

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

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

js objects graph

http://jsobjects.org

答案 14 :(得分:13)

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

答案 15 :(得分:10)

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

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

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

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

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

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

使用prototype属性进行对象的这种链接称为原型继承。在那里,它很简单,同意吗?

答案 16 :(得分:10)

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

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

这是两件不同的事情。

[[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

.prototype属性:

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

function Bar(){};

.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

最重要的.prototype之一就是of the Object function。该原型包含所有[[Prototype]]链包含的原型对象。在其上,定义了新对象的所有可用方法:

// 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上定义所有内容:

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:这不会让任何人感到困惑;在许多实现中通过the __proto__ property提供。
[2]:除null以外的所有。

答案 17 :(得分: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; }
    };
})();

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

kvs = keyValueStore.create();

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

  • data
  • get
  • set
  • delete
  • getLength

现在,假设我们创建了此keyValueStore个对象的100个实例。即使getsetdeletegetLength将对这100个实例中的每个实例执行完全相同的操作,但每个实例都有自己的此函数副本。

现在,想象一下,如果您只有一个getsetdeletegetLength副本,并且每个实例都会引用相同的功能。这样可以提高性能并减少内存。

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

现在,再次考虑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; }
    };
})();

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

答案 18 :(得分:8)

用更好的照片解释JavaScript prototype-based inheritance的另一种尝试

Simple objects inheritanse

答案 19 :(得分:7)

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

想象一下......

你在高中时,你在课堂上进行了一个今天到期的测验,但是你没有笔来填写你的答案。卫生署!

你坐在你的朋友Finnius旁边,他可能有一支钢笔。你问,他环顾他的办公桌不成功,但没有说&#34;我没有笔#34,他是一个很好的朋友,他与他的另一个朋友Derp检查,如果他有一个钢笔。 Derp确实有一支备用笔并将其传递回Finnius,后者将其传递给您以完成您的测验。 Derp已将笔委托给Finnius,后者已将笔委托给您使用。

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

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

答案 20 :(得分:4)

摘要:

  • 函数是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.

这为什么有用:

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

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

例如:

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

答案 21 :(得分:3)

另一个显示 __ proto __ prototype constructor 关系的方案: enter image description here

答案 22 :(得分:2)

只是你已经有一个byte的对象,但在使用构造函数语法时你仍然没有对象。

答案 23 :(得分:2)

原型通过克隆现有的对象来创建新对象。因此,当我们考虑原型时,真的可以考虑克隆或制作副本而不是制作副本。

答案 24 :(得分:0)

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

参考:https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes

答案 25 :(得分:0)

如果您想从基础上理解原型和基于原型的继承的概念,请查看官方MDN文档,他们对此进行了很好的解释。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain

在继承方面,JavaScript只有一种构造: 对象。每个对象都有一个私有属性,其中包含指向 另一个对象称为其原型。该原型对象有一个 自己的原型,依此类推,直到到达null为止 作为其原型。根据定义,null没有原型,并充当 该原型链的最终链接。

此外,这是另一个很好的资源,它使用简单的示例进行了解释-https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes