JavaScript .prototype 如何工作?

我不喜欢动态编程语言,但是我写了相当一部分 JavaScript 代码。我从来没有真正了解过这种基于原型的编程,有人知道它是如何工作的吗?

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

我记得很久以前与人们进行过多次讨论(我不确定自己在做什么),但是据我了解,这里没有一个课堂的概念。这只是一个对象,这些对象的实例是原始对象的副本,对吗?

但是,此 “.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

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

答案

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

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

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

尽管我们已经看到,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关键字的情况下做同样的事情

因此,在此步骤中, functionsobjectsthis关键字就是我们所有的东西。

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

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

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对象或Node.js GLOBAL 。这意味着我们将在浏览器中使用 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 ,该prototype可以在名称为__proto__任何 JavaScript 对象中找到,而我尝试在摘要部分进行一些解释。因此,我们可以通过执行以下操作获得类似的结果:

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

但是这里要注意的一点是,您可以访问person对象第一层上__proto__定义的所有属性(有关详细信息,请阅读摘要部分)。


正如你看到的任何使用这两种方式this会正是指向person对象。

C。 JavaScript 有另一种方式为用户提供功能this ,这是使用电话应用调用该函数。

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

在这一部分中,当您使用new关键字时,我将尝试采取 JavaScript 采取的所有步骤,而不使用new关键字和prototype 。因此,当我们执行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"

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

newObject.getName();

C。然后将该对象提供给构造函数,

我们可以使用我们的示例来做到这一点,例如:

Person.call(newObject, "George");

要么

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

然后构造函数可以执行任何所需的操作,因为构造函数内部的对象是刚刚创建的对象。

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


摘要:

基本上,当在函数上使用new关键字时,您正在调用该函数,并且该函数用作构造函数,因此当您说:

new FunctionName()

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

因此,当 JavaScript 在对象上查找属性时,它要做的第一件事就是在对象上查找属性。然后有一个秘密属性[[prototype]] ,通常像__proto__一样拥有它,而该属性就是 JavaScript 接下来要看的东西。当它遍历__proto__ ,它又是另一个 JavaScript 对象,它具有自己的__proto__属性,它会不断上升直到到达下一个__proto__为空的地步。该点是 JavaScript 中__proto__属性为 null 的Object.prototype对象是Object.prototype对象:

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

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

原型链

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

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,因此如果我错了,人们可以纠正我。

原型的七个 Koans

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

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


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

  • 原型属性,如obj.prototype

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

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

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


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

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

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


2)

  • __proto__用于点.属性查询,如obj.property
  • .prototype 直接用于查找,而是间接使用,因为它使用new决定对象创建时的__proto__

查找顺序为:

  • obj.p = ...Object.defineProperty(obj, ...)添加的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 (例如 set 字段),这与经典 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

如 “4)” 所述,第一行将ci设置为1

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

c.inc()
  • .inc是通过[[Prototype]]链找到的: c > C > C.prototype > inc
  • 当我们调用 JavaScript 作为一种功能XY() ,JavaScript 的自动设置this等于X里面Y()函数调用!

完全相同的逻辑也解释了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对每个功能都use strict要求的显式use strict来完成,这无疑是乏味的。

阅读此主题后,我对 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;

一条new语句不显示链接目标( Object.prototype )本身;相反,目标由构造函数( Object )隐含。

记得:

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

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

原型链

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

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

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

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

JavaScript 基本上有两种数据类型

  • 非物体
  • 对象

非物体

以下是非对象数据类型

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

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

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

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

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

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

字符串数字布尔数据类型都可以表示为对象非对象 。当它们表示为对象时,其 typeof 总是 ==='object'。一旦了解了对象数据类型,我们将回到这一点。

对象

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

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

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

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

因此, typeof(Object) === typeof(String) === typeof(Number) === typeof(Boolean) === typeof(Array) === typeof(RegExp) === typeof(Function) == = typeof(UserDefinedFunction) === '功能'

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

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

如前所述, 功能类型对象可以使用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

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

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

function UserDefinedFunction()
{
}

以下自动发生

UserDefinedFunction.prototype={constructor:UserDefinedFunction}

“原型” 属性仅在函数类型对象中存在 (而在非函数类型对象中则不存在 )。

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

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

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

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

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

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

function UserDefinedFunction()
{ 

} 

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

UserDefinedFunction.prototype={constructor:UserDefinedFunction}

*/


var newObj_1=new UserDefinedFunction()

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

alert(newObj_1.constructor) //Displays function UserDefinedFunction

//Create a new property in UserDefinedFunction.prototype object

UserDefinedFunction.prototype.TestProperty="test"

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

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

//Create a new Object

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

}


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

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

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

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

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


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

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

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

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

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

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

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

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

alert(newObj_2.property2) // Displays Property2

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

每个对象的原型链最终可以追溯到 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创建新对象。这样创建的对象是非功能类型的对象。 Non Function 类型的对象无法使用运算符 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函数开始。

在此处输入图片说明

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

在此处输入图片说明

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

在此处输入图片说明

如果查看上面的console.log()输出,则可以看到Tree.prototype上的构造函数属性和__proto__属性。 __proto__表示此function所基于的prototype ,由于这只是一个普通的JavaScript function ,尚未设置inheritance ,因此它引用的Object prototype是 JavaScript 内置的东西...

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

它具有.toString, .toValue, .hasOwnProperty等内容。

我的 mozilla 带来的__proto__已过时,并由Object.getPrototypeOf方法代替,以获取object's prototype

在此处输入图片说明

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

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

在此处输入图片说明

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

在此处输入图片说明

这意味着当您创建Treeinstance时,可以调用它的branch方法。

在此处输入图片说明

我们还可以向Prototype添加primitivesobjects

在此处输入图片说明

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

在此处输入图片说明

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

在此处输入图片说明

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

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

现在,您创建一个Child instance ,并调用Tree最初可用的branch 。我们实际上尚未在Child prototype上定义我们的branch 。但是,在 Child 继承的Root prototype中。

在此处输入图片说明

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

Javascript具有诸如strings, number, booleans, undefined, null.类的原语strings, number, booleans, undefined, null.它们不是object(ie reference types) ,但肯定可以像object 。让我们在这里看一个例子。

在此处输入图片说明

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

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

在此处输入图片说明

String object仅在销毁前一个语句中存在(此过程称为autoboxing )。让我们再次回到我们的prototypal inheritance

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

prototype在 JS 是一个对象,它yields你到另一个的父object[即委派] Delegation是指如果您无法做某事,您会告诉其他人为您做。

在此处输入图片说明

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

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

在此处输入图片说明

如果您看下面的内容,我们正在尝试访问每个function可用的call方法。

在此处输入图片说明

https://jsfiddle.net/rknffckc/

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

在此处输入图片说明

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

在此处输入图片说明

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

有两个实例b1b2 ,它们的构造函数是Bar ,其父对象是 Foo,并且有两个通过原型链identify和通过BarFoo speak方法

在此处输入图片说明

https://jsfiddle.net/kbp7jr7n/

如果您在上面的代码中查找,我们有Foo构造函数(其具有方法identify()Bar构造函数(其具有speak方法)。我们创建两个Bar实例b1b2 ,其父类型为Foo 。现在,在调用Bar speak方法时,我们可以通过prototype链识别谁在讲话。

在此处输入图片说明

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

在此处输入图片说明

prototypeBarFooprototypeFooObject ,如果你仔细看的prototypeFoo有关Object.prototype

在此处输入图片说明

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

在此处输入图片说明

我希望此添加了一些信息,我知道这可能会掌握很多... 用简单的话来说,它只是对象与对象的链接!