JavaScript .prototype如何工作?

2020/09/15 05:02 · javascript ·  · 0评论

我不喜欢动态编程语言,但是我写了相当一部分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

这些幻灯片也确实起到了很大作用。

每个JavaScript对象都有一个内部“ slot”,[[Prototype]]其值称为nullobject您可以将插槽视为JavaScript引擎内部对象的属性,该属性对您编写的代码隐藏。方括号[[Prototype]]是有意的,并且是ECMAScript规范约定,用于表示内部插槽。

[[Prototype]]对象的指向的值俗称“该对象的原型”。

如果您通过点(obj.propName)或方括号(obj['propName'])表示法访问属性,而该对象没有直接具有这样的属性(即,自己的属性,可通过进行检查obj.hasOwnProperty('propName')),则运行时将在引用的对象上查找具有该名称的属性由[[Prototype]]代替。如果[[Prototype]] 没有这样的属性,[[Prototype]]则依次检查,依此类推。这样,原始对象的原型链就会遍历,直到找到匹配项或到达末尾为止。null价值是原型链的顶部

现代JavaScript实现允许[[Prototype]]通过以下方式对进行读和/或写访问

  1. new操作者(设定从一个构造函数返回的默认对象上的原型链),
  2. extends关键字(使用类语法时配置原型链),
  3. Object.create将提供的参数设置为[[Prototype]]结果对象的,
  4. Object.getPrototypeOfObject.setPrototypeOf[[Prototype]] 创建对象获取/设置),以及
  5. 命名的标准化访问器(即getter / setter)属性__proto__(类似于4)。

Object.getPrototypeOf并且比之更Object.setPrototypeOf受推荐__proto__,部分原因o.__proto__ 当对象的原型为时,行为异常null

[[Prototype]]在创建对象时首先设置对象。

如果您通过创建新对象new Func(),则[[Prototype]]默认情况下,该对象的名称将设置为所引用的对象Func.prototype

因此请注意,所有类以及可与该new运算符一起使用的所有函数.prototype除具有自己的[[Prototype]]内部插槽,还具有一个命名的属性“原型”一词的这种双重使用是该语言的新手之间无休止的混淆的根源。

new与构造函数一起使用可以让我们模拟JavaScript中的经典继承。尽管我们已经看到,JavaScript的继承系统是原型的,而不是基于类的。

在将类语法引入JavaScript之前,构造函数是模拟类的唯一方法。我们可以将构造函数的.prototype属性所引用的对象的属性视为共享成员。即。每个实例相同的成员。在基于类的系统中,对每个实例都以相同的方式实现方法,因此在概念上将方法添加到.prototype属性中。但是,对象的字段是特定于实例的,因此在构造过程中会添加到对象本身。

没有类语法,开发人员就必须手动配置原型链,以实现与经典继承类似的功能。这导致了许多实现此目的的不同方法。

这是一种方法:

function Child() {}
function Parent() {}
Parent.prototype.inheritedMethod = function () { return 'this is inherited' }

function inherit(child, parent) {
  child.prototype = Object.create(parent.prototype)
  child.prototype.constructor = child
  return child;
}

Child = inherit(Child, Parent)
const o = new Child
console.log(o.inheritedMethod()) // 'this is inherited'

...这是另一种方式:

function Child() {}
function Parent() {}
Parent.prototype.inheritedMethod = function () { return 'this is inherited' }

function inherit(child, parent) {
    function tmp() {}
    tmp.prototype = parent.prototype
    const proto = new tmp()
    proto.constructor = child
    child.prototype = proto
    return child
}

Child = inherit(Child, Parent)
const o = new Child
console.log(o.inheritedMethod()) // 'this is inherited'

ES2015中引入的类语法通过提供extends“一种真正的方式”来配置原型链以模拟JavaScript中的经典继承,从而简化了事情

因此,类似于上面的代码,如果您使用类语法创建新对象,如下所示:

class Parent { inheritedMethod() { return 'this is inherited' } }
class Child extends Parent {}

const o = new Child
console.log(o.inheritedMethod()) // 'this is inherited'

...结果对象[[Prototype]]将被设置为的实例Parent,其实例[[Prototype]]Parent.prototype

最后,如果您通过创建了一个新对象Object.create(foo),则将结果对象[[Prototype]]设置为foo

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

如前所述,我无法在Person上调用setAmountDue()和getAmountDue()。

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

这是一个非常简单的基于原型的对象模型,在解释过程中将其视为示例,但尚无注释:

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

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

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

第一步,我们必须弄清楚JavaScript函数实际上是如何工作的(作为类,类似于使用this关键字的函数),或者作为带有参数,其作用和返回结果的常规函数​​。

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

因此,在此步骤中functionsobjectsthis关键字就是全部。

第一个问题是,如果this不使用newkeyword,关键词如何有用

因此,要回答这个问题,假设我们有一个空对象,并且有两个函数,例如:

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,其值为“乔治”。

b。我们可以它们附加到对象上,作为其属性

- 最简单的方法是修改空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来调用该函数。

apply()方法调用具有给定值的函数,并以数组(或类似数组的对象)形式提供参数。

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

这3种方法是弄清.prototype功能的重要初始步骤。


2- new关键字如何运作?

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

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

在这一部分中,我将尝试采取JavaScript所采取的所有步骤,而在使用new关键字prototype不使用关键字and new因此,当我们执行时new Person("George")Person函数充当了构造函数,这就是JavaScript所要做的,一个接一个:

一个。首先,它生成一个空对象,基本上是一个空哈希,例如:

var newObject = {};

b。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关键字时,您正在调用该函数,并且该函数用作构造函数,因此当您说:

new FunctionName()

JavaScript在内部创建一个对象,一个空的哈希,然后将其提供给构造函数,然后构造函数可以执行其所需的任何操作,因为构造函数内部的对象是刚刚创建的对象,然后为您提供了该对象如果您尚未在函数中使用return语句,或者return undefined;在函数主体的末尾放置了a

因此,当JavaScript在对象上查找属性时,它要做的第一件事就是在对象上查找属性。然后有一个[[prototype]]通常我们喜欢的秘密属性__proto__而该属性就是JavaScript接下来要看的东西。并且,当它遍历时__proto__,直到它再次是另一个JavaScript对象,它都具有自己的__proto__属性,它不断上升直到到达下一个__proto__为null 的地步该点是JavaScript中__proto__属性为null 的唯一对象Object.prototypeobject:

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

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

原型链

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

原型的七个Koans

经过深思熟虑,西罗·桑(Ciro San)降落在火狐山(Mount Fire Fox)上时,他的头脑清晰而平静。

然而,他的手却焦躁不安,他自己握住了刷子,写下了以下笔记。


0)可以将两种不同的事物称为“原型”:

  • 原型属性,如 obj.prototype

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

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

    Firefox使该__proto__属性可以作为扩展访问。ES6现在提到的一些可选要求__proto__


1)存在这些概念可以回答以下问题:

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

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


2)

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

查找顺序为:

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

这就是所谓的原型链

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


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

  • 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(i) { this.i = i }
var f = new F(1)

对应下图(Number省略了一些内容):

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

此图显示了许多语言预定义的对象节点:

  • null
  • Object
  • Object.prototype
  • Function
  • Function.prototype
  • 1
  • Number.prototype(可以找到(1).__proto__,必须使用圆括号来满足语法)

我们的两行代码仅创建了以下新对象:

  • f
  • F
  • F.prototype

i现在的属性是f因为当您执行以下操作:

var f = new F(1)

它的计算结果Fthis被该值new然后把它分配给要回来,f


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添加了classextends关键字,它们主要是语法糖,用于解决以前可能发生的原型操作混乱问题。

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.
c = new C(1)
c.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

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

(c)----->(1)
 |   i
 |
 |
 |[[Prototype]]
 |
 |
 v    __proto__
(C)<--------------(D)         (d)
| |                |           |
| |                |           |
| |prototype       |prototype  |[[Prototype]] 
| |                |           |
| |                |           |
| |                | +---------+
| |                | |
| |                | |
| |                v v
|[[Prototype]]    (D.prototype)--------> (inc2 function object)
| |                |             inc2
| |                |
| |                |[[Prototype]]
| |                |
| |                |
| | +--------------+
| | |
| | |
| v v
| (C.prototype)------->(inc function object)
|                inc
v
Function.prototype

让我们花点时间研究以下内容如何工作:

c = new C(1)
c.inc() === 2

第一行设置c.i,以1如在解释“4)”。

在第二行,当我们这样做时:

c.inc()
  • .inc通过[[Prototype]]找到c-> C-> C.prototype->inc
  • 当我们在Javascript中以调用函数时X.Y(),JavaScript 函数调用中会自动设置this为相等XY()

完全相同的逻辑也可以解释d.incd.inc2

本文https://javascript.info/class#not-just-a-syntax-sugar提到了class值得了解的其他影响如果没有class关键字(TODO检查哪个),则其中一些可能无法实现

  • [[FunctionKind]]:"classConstructor",这会强制使用new调用构造函数:为什么不能将ES6类构造函数称为普通函数?
  • 类方法是不可枚举的。可以完成Object.defineProperty
  • 总是上课use strict可以使用use strict每个函数的显式来完成,这无疑是乏味的。

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功能。

以上是我的理解。我将其设为社区Wiki,因此如果我错了,人们可以纠正我。

阅读此主题后,我对JavaScript原型链感到困惑,然后我发现了这些图表

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

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

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

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

原型链最终归结为Object.prototype。

通过将子类的原型设置为与父类的对象相等,每次都可以在技术上根据需要扩展原型链。

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

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

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;

您实际上无法Object.prototype语句中看到链接目标(相反,构造函数(Object隐含了目标

记得:

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

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

原型链

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

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

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

这篇文章很长。但是我确信它将清除您有关JavaScript继承的“原型”性质的大多数查询。甚至更多。请阅读全文。

JavaScript基本上有两种数据类型

  • 非物体
  • 对象

非物体

以下是非对象数据类型

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

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

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

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

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

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

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

对象

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

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

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

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

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

All the Function type objects are actually instances of the built in JavaScript object Function (including the Function object i.e it is recursively defined). It is as if the these objects have been defined in the following way

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运算符进一步创建新对象例如可以使用以下方法创建类型为ObjectStringNumberBooleanArrayRegExpUserDefinedFunction的对象

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

这样创建的对象都是Non Function类型的对象,并返回其typeof === 'object'在所有这些情况下,对象“ a”无法使用运算符new进一步创建对象。所以以下是错误的

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

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

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

还要注意,ObjectArrayRegExp函数无需使用运算符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.

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

Every Constructor/Function (whether built in or user defined) when defined automatically has a property called "prototype" whose value by default is set as an object. This object itself has a property called "constructor" which by default references back the Constructor/Function .

For example when we define a function

function UserDefinedFunction()
{
}

following automatically happens

UserDefinedFunction.prototype={constructor:UserDefinedFunction}

This "prototype" property is only present in the Function type objects
(and never in Non Function type objects).

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

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

同样,每个创建的对象函数类型或非函数类型)都具有“构造函数”属性,该属性是从构造函数的prototype属性所引用的对象继承的。默认情况下,这个“构造”属性引用的构造函数创建它(如果构造函数的默认的“原型”没有变化)。

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

对于非功能类型的对象(例如,内置Javascript的Java对象),构造函数是创建它的函数。对于Math对象,它是函数Object(){}

没有任何支持代码,上面解释的所有概念可能有些令人生畏。请逐行阅读以下代码以了解其概念。尝试执行它以具有更好的理解。

function UserDefinedFunction()
{ 

} 

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

UserDefinedFunction.prototype={constructor:UserDefinedFunction}

*/


var newObj_1=new UserDefinedFunction()

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

alert(newObj_1.constructor) //Displays function UserDefinedFunction

//Create a new property in UserDefinedFunction.prototype object

UserDefinedFunction.prototype.TestProperty="test"

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

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

//Create a new Object

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

}


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

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

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

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

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


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

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

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

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

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

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

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

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

alert(newObj_2.property2) // Displays Property2

alert(Object.getPrototypeOf(newObj_2).property2) //Displays  "Property2"

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

var o=Starting object;

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

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

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

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

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

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

可能有人认为,将构造函数的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创建新对象这样创建的对象是非功能类型的对象。非功能型对象不能再创建使用对象new运算符

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

  • 所有对象(“ 函数类型”和“ 非函数类型”)都具有“构造函数”属性,默认情况下会引用创建它函数类型对象 / 构造函数

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

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

  • 每个对象的原型链最终都可以追溯到Object.prototype(除非使用Object.create(null)创建对象,否则对象将没有原型)。

  • typeof(new Array())==='object'是语言设计的,并非Douglas Crockford指出的错误

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

希望这可以帮助。

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

在此处输入图片说明

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

在此处输入图片说明

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

在此处输入图片说明

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

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype

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

__proto__我的mozilla带来了不推荐使用的Object.getPrototypeOf方法,该方法已替换为要获取的方法object's prototype

在此处输入图片说明

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

让我们在中添加一个方法Tree prototype

在此处输入图片说明

我们已经修改了,Rootfunction为其添加了一个分支。

在此处输入图片说明

也就是说,当您创建的instanceTree,可以调用它的branch方法。

在此处输入图片说明

我们还可以添加primitivesobjects到我们Prototype

在此处输入图片说明

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

在此处输入图片说明

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

在此处输入图片说明

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

Child  
|
 \
  \
   Tree.prototype
   - branch
   |
   |
    \
     \
      Object.prototype
      -toString
      -valueOf
      -etc., etc.

现在你创建instanceChild和呼叫branch使本来可用Tree我们实际上尚未在上定义我们branchChild prototype但是,Root prototypeChild从中继承。

在此处输入图片说明

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

Javascript具有像strings, number, booleans, undefined, null.他们不是这样的原语object(i.e reference types),但当然可以像普通话一样起作用object让我们在这里看一个例子。

在此处输入图片说明

在此清单的第一行中,将一个primitive字符串值分配给name。第二行将名称视为,object然后charAt(0)使用点表示法进行调用

这是幕后发生的事情:// JavaScript引擎做什么

在此处输入图片说明

String object之前它的破坏(这个过程被称为只存在一个语句autoboxing)。让我们再次回到我们的prototypal inheritance

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

prototypeJS中的A 是一个对象,yields是另一个对象的父对象object[即委派] Delegation表示如果您无法做某事,您会告诉其他人为您做。

在此处输入图片说明

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

如果您查看上面的小提琴,则dog可以访问toStringmethod,但是其中不可用,但是可以通过委托给它的原型链使用Object.prototype

在此处输入图片说明

如果您看下面的一个,我们正在尝试访问callevery中可用方法function

在此处输入图片说明

https://jsfiddle.net/rknffckc/

如果您查看上面的小提琴,则ProfileFunction可以访问callmethod,但是其中不可用,但是可以通过委托给它的原型链使用Function.prototype

在此处输入图片说明

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

在此处输入图片说明

上面的图看起来有点复杂,但是展示了整个prototype chaining工作原理。让我们慢慢地看一下:

有两个实例b1b2,其构造函数为Bar,父对象为Foo,并且具有原型链identifyspeakvia Bar和中的两个方法。Foo

在此处输入图片说明

https://jsfiddle.net/kbp7jr7n/

如果你看看上面的代码,我们有Foo构造谁的方法identify()Bar具有构造speak方法。我们创建了两个Bar实例b1b2其父类型Foo现在,在调用的speak方法时Bar,我们可以识别通过prototype链环进行通话的人

在此处输入图片说明

Bar现在已在Foo中定义了所有方法prototype让我们进一步深入理解Object.prototypeFunction.prototype和它们之间的关系。如果您查找的构造函数Foo则为BarObjectFunction constructor

在此处输入图片说明

prototypeBarFooprototypeFoo就是Object,如果你仔细观察的prototypeFoo是有关Object.prototype

在此处输入图片说明

在结束之前,让我们在这里包装一小段代码来总结上面的所有内容我们在instanceof这里使用运算符来检查an 链中是否object具有a prototypeprototype属性constructor,下面总结了整个大图。

在此处输入图片说明

我希望此添加了一些信息,我知道这可能有点难理解...用简单的话来说,它只是对象与对象的链接!!!

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

考虑构造函数:

 function Person() {}

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

向上的链看起来像这样:

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

重要的是该原型链与Person可以构造的对象无关这些构造的对象具有自己的原型链,并且该链可能与上述的链没有共同的祖先。

以这个对象为例:

var p = new Person();

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

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

该链中没有函数对象(尽管可以)。

因此Person似乎与两种链条有关,它们各自生活。要从一个链“跳”到另一个链,请使用:

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

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

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

在此处输入图片说明

总结一下:

prototype属性不提供对象的原型链的任何信息,而提供对象创建的对象的信息。

物业名称prototype会引起混乱也就不足为奇了如果已命名此属性prototypeOfConstructedInstances或沿此名称命名,可能会更加清楚

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

Person.prototype.constructor === Person

通过为prototype属性显式分配一个不同的对象,可以打破这种对称性(稍后会详细介绍)。

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

Person.prototype是在创建函数的Person同时创建的对象。它具有Person构造函数,即使该构造函数尚未真正执行。因此,同时创建了两个对象:

  1. 函数Person本身
  2. 将函数称为构造函数时将充当原型的对象

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

由于函数也是对象,因此它在自己的原型链中也有自己的父项,但请记住,这两个链是关于不同的事物的。

这里有一些平等之处可以帮助您解决问题-所有这些印刷品true

function Person() {};

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

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

将水平添加到原型链

尽管在创建构造函数时创建了原型对象,但是您可以忽略该对象,并为该构造函数创建的任何后续实例分配另一个应用作原型的对象。

例如:

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

现在,t的原型链p的原型链长了一个步骤

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

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

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

然后可以将先前显示的图形扩展为该图形(原始图形Thief.prototype被忽略):

在此处输入图片说明

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

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

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

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

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

《面向对象的JavaScript权威指南》 -简短而清晰的约30分钟的视频解释所提出的问题(原型继承主题从5:45开始,尽管我更喜欢听整个视频)。该视频的作者还创建了JavaScript对象可视化器网站http://www.objectplayground.com/在此处输入图片说明
在此处输入图片说明

我发现obj_n.prop_X在引用“原型链”作为递归约定时会有所帮助

如果obj_n.prop_X不存在,检查obj_n+1.prop_X哪里obj_n+1 = obj_n.[[prototype]]

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

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

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

js对象图

http://jsobjects.org

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

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

  • .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-是不是对的Object功能该原型包含所有[[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:那不会混淆任何人;通过提供__proto__性能在许多实现。
[2]:除以外的所有内容null

让我告诉您我对原型的理解。我不会在这里将继承与其他语言进行比较。我希望人们不再比较语言,而只是理解语言本身。理解原型和原型继承非常简单,下面我将向您展示。

原型就像模型一样,您可以基于该模型创建产品。要理解的关键点是,当您使用另一个对象作为原型创建对象时,原型与产品之间的联系是永恒的。例如:

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

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

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

  • 当访问属性以读取其值时,将在范围链中查找该属性。搜索变量的过程从产品开始一直到其原型。如果在搜索中找到了这样的变量,则搜索将在此处停止,并返回值。如果在范围链中找不到此类变量,则返回undefined。
  • 写入(更改)属性后,该属性总是写在产品对象上。如果产品尚不具有此类属性,则将隐式创建和编写该产品。

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

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

简单对象继承

考虑以下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复制,并且每个实例将引用相同的功能。这样可以提高性能,并减少内存需求。

这就是原型的来源。原型是继承但不被实例复制的属性的“蓝图”。因此,这意味着它在对象的所有实例中仅在内存中存在一次,并由所有这些实例共享。

现在,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个实例共享这四个方法,而不是每个实例都有自己的副本。

摘要:

  • 函数是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所引用的对象上可用
  • 如果该属性不在原型对象上,它将沿着原型链一直攀升到对象对象。
  • 如果无法在对象及其原型链的任何地方找到该属性,它将返回未定义。

例如:

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

在理解这类内容时,我总是喜欢类比。在我看来,与原型低音继承相比,“原型继承”相当混乱,尽管原型是更简单的范例。实际上,对于原型而言,确实没有继承,因此名称本身就具有误导性,它更像是一种“委托”。

想象一下....

您正在读高中,正在上课,今天有一个测验,但是您没有笔来填写答案。h!

您正坐在可能有笔的朋友Finnius旁边。您问,他环顾四周办公桌没有成功,但他没有说“我没有笔”,而是一个好朋友,他与其他朋友Derp核对了是否有笔。Derp确实有一支备用笔,并将其传递回Finnius,后者将其交给您以完成测验。Derp已将钢笔委托给Finnius,后者将钢笔委托给您使用。

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

这是原型工作方式的简化示例,其中在数据树中搜索您要寻找的东西。

另一个显示__proto__原型构造函数关系的方案:
在此处输入图片说明

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

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

参考:https : //developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Objects/Object_prototypes

原型创建新的对象通过克隆现有对象因此,实际上,当我们考虑原型时,可以真正地考虑克隆或复制某些内容而不是对其进行组合。

本文地址:http://javascript.askforanswer.com/javascript-prototyperuhegongzuo.html
文章标签: ,   ,  
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!