Heim  >  Artikel  >  Web-Frontend  >  Detaillierte Erläuterung des JS-Prototyps und der Prototypenkette (3)

Detaillierte Erläuterung des JS-Prototyps und der Prototypenkette (3)

零到壹度
零到壹度Original
2018-03-22 11:17:101197Durchsuche

Dieses Mal werfen wir im Anschluss an den vorherigen Artikel einen Blick auf den JS-Prototyp und die Prototypenkette sowie die Vorsichtsmaßnahmen. Das Folgende ist ein praktischer Fall, werfen wir einen Blick darauf.

7. Funktionsobjekte (überprüfen Sie die vorherigen Wissenspunkte)

Proto aller Funktionsobjekte Alle Zeigen Sie auf Function.prototype, eine leere Funktion (Leere Funktion).

Number.__proto__ === Function.prototype  // true
Number.constructor == Function //true
Boolean.__proto__ === Function.prototype // true
Boolean.constructor == Function //true
String.__proto__ === Function.prototype  // true
String.constructor == Function //true
// 所有的构造器都来自于Function.prototype,甚至包括根构造器Object及Function自身
Object.__proto__ === Function.prototype  // true
Object.constructor == Function // true
// 所有的构造器都来自于Function.prototype,甚至包括根构造器Object及Function自身
Function.__proto__ === Function.prototype // true
Function.constructor == Function //true
Array.__proto__ === Function.prototype   // true
Array.constructor == Function //true
RegExp.__proto__ === Function.prototype  // true
RegExp.constructor == Function //true
Error.__proto__ === Function.prototype   // true
Error.constructor == Function //true
Date.__proto__ === Function.prototype    // true
Date.constructor == Function //true

Es gibt insgesamt 12 integrierte (eingebaute) Konstruktoren/Objekte in JavaScript (JSON wurde in ES5 neu hinzugefügt ), hier werden 8 zugängliche Konstruktoren aufgelistet. Auf den Rest wie Global kann nicht direkt zugegriffen werden. Argumente werden nur von der JS-Engine erstellt, wenn die Funktion aufgerufen wird. Mathematik und JSON liegen in Form von Objekten vor, es sind keine neuen erforderlich. Ihr Prototyp ist Object.prototype. Wie folgt

Math.__proto__ === Object.prototype  // true
Math.construrctor == Object // true
JSON.__proto__ === Object.prototype  // true
JSON.construrctor == Object //true

Zu den oben genannten Funktionsobjekten gehören sicherlich benutzerdefinierte Objekte. Wie folgt

// 函数声明
function Person() {}
// 函数表达式
var Perosn = function() {}
console.log(Person.__proto__ === Function.prototype) // true
console.log(Man.__proto__ === Function.prototype)    // true

Was bedeutet das?

** Alle Konstruktoren stammen von Function.prototype, sogar der Root-Konstruktor Object und Function selbst. Alle Konstruktoren erben die Eigenschaften und Methoden von Function.prototype. Wie Länge, Anruf, Anwenden, Binden**

(Sie sollten den ersten Satz verstehen, wir werden im nächsten Abschnitt weiter über den zweiten Satz sprechen, zuerst ein Loch graben :))
Funktion .prototype ist auch der einzige Prototyp, dessen Typ XXX.prototype eine Funktion ist. Der Prototyp anderer Konstruktoren ist ein Objekt (der Grund wurde in Abschnitt 3 erläutert). Wie folgt (erneut überprüft):

console.log(typeof Function.prototype) // function
console.log(typeof Object.prototype)   // object
console.log(typeof Number.prototype)   // object
console.log(typeof Boolean.prototype)  // object
console.log(typeof String.prototype)   // object
console.log(typeof Array.prototype)    // object
console.log(typeof RegExp.prototype)   // object
console.log(typeof Error.prototype)    // object
console.log(typeof Date.prototype)     // object
console.log(typeof Object.prototype)   // object

Oh, oben wurde auch erwähnt, dass es sich um eine leere Funktion handelt, console.log(Function.prototype), schauen Sie sich das an (Hinweis, ich werde darüber sprechen im nächsten Abschnitt) Dies)

Wenn man weiß, dass der __proto__ aller Konstruktoren (einschließlich integrierter und benutzerdefinierter) Function.prototype ist, wer ist dann der __proto__ von Function.prototype?
Ich glaube, Sie haben alle gehört, dass Funktionen in JavaScript ebenfalls erstklassige Bürger sind. Wie können Sie das also zeigen? Wie folgt

console.log(Function.prototype.__proto__ === Object.prototype) // true

Dies zeigt, dass alle Konstruktoren auch gewöhnliche JS-Objekte sind und Sie dem Konstruktor Attribute hinzufügen/entfernen können. Gleichzeitig erbt es auch alle Methoden von Object.prototype: toString, valueOf, hasOwnProperty usw. (Sie sollten auch den ersten Satz verstehen. Über den zweiten Satz werden wir im nächsten Abschnitt weiter sprechen. Kein Grund, ein Loch zu graben, es ist immer noch dasselbe Loch;))

Abschließend, Wer ist der Prototyp von Object.prototype?
Object.prototype.__proto__ === null // true
hat die Spitze erreicht und ist null. (Lesen Sie es jetzt und schauen Sie sich Kapitel 5 noch einmal an. Verstehen Sie?)

8. Prototyp

Unter allen durch den ECMAScript-Kern definierten Attributen ist das am meisten Das Faszinierendste ist das Prototyp-Attribut. Für Referenztypen in ECMAScript ist der Prototyp der tatsächliche Ort, an dem alle ihre Instanzmethoden gespeichert werden. Mit anderen Worten: Methoden wie toString() und valueOf() werden tatsächlich unter dem Prototypnamen gespeichert, der Zugriff erfolgt jedoch über Instanzen der jeweiligen Objekte.

——„JavaScript Advanced Programming“ Dritte Ausgabe P116

Wir wissen, dass JS einige integrierte Methoden hat, die wir verwenden können, wie zum Beispiel:
Objekte können den Konstruktor/toString( verwenden) )/valueOf () und andere Methoden;
Arrays können map()/filter()/reducer() und andere Methoden verwenden;
Zahlen können parseInt()/parseFloat() und andere Methoden verwenden;
Warum? ? ?

Wenn wir eine Funktion erstellen:

var Person = new Object()
Person ist eine Instanz von Object, daher erbt Person das Prototypobjekt von Object Object.prototype Alle Methoden:

Detaillierte Erläuterung des JS-Prototyps und der Prototypenkette (3)

Object.prototype

Jede Instanz von Object verfügt über die oben genannten Eigenschaften und Methoden.
Damit ich Person.constructor oder Person.hasOwnProperty verwenden kann.

Wenn wir ein Array erstellen:

var num = new Array()
num ist eine Instanz von Array, daher erbt num das Prototypobjekt von Array Array.prototype Alle Methoden:

Detaillierte Erläuterung des JS-Prototyps und der Prototypenkette (3)

Array.prototype

Willst du mich veräppeln? Warum ist das ein leeres Array? ? ?

Wir können eine neue von ES5 bereitgestellte Methode verwenden: Object.getOwnPropertyNames
, um alle Eigenschaftsnamen (einschließlich nicht aufzählbarer Eigenschaften) mit Ausnahme von Eigenschaften in Prototy abzurufen und ein Array zurückzugeben:

Detaillierte Erläuterung des JS-Prototyps und der Prototypenkette (3)

var arrayAllKeys = Array.prototype; // [] 空数组// 只得到 arrayAllKeys 这个对象里所有的属性名(不会去找 arrayAllKeys.prototype 中的属性)console.log(Object.getOwnPropertyNames(arrayAllKeys));
/* 输出:
["length", "constructor", "toString", "toLocaleString", "join", "pop", "push",
"concat", "reverse", "shift", "unshift", "slice", "splice", "sort", "filter", "forEach",
"some", "every", "map", "indexOf", "lastIndexOf", "reduce", "reduceRight",
"entries", "keys", "copyWithin", "find", "findIndex", "fill"]
*/

这样你就明白了随便声明一个数组,它为啥能用那么多方法了。

细心的你肯定发现了Object.getOwnPropertyNames(arrayAllKeys) 输出的数组里并没有 constructor/hasOwnPrototype等对象的方法(你肯定没发现)。
但是随便定义的数组也能用这些方法

Detaillierte Erläuterung des JS-Prototyps und der Prototypenkette (3)

var num = [1];console.log(num.hasOwnPrototype()) // false (输出布尔值而不是报错)

Why ???

因为Array.prototype 虽然没这些方法,但是它有原型对象(__proto__):

// 上面我们说了 Object.prototype 就是一个普通对象。
Array.prototype.__proto__ == Object.prototype

所以 Array.prototype 继承了对象的所有方法,当你用num.hasOwnPrototype()时,JS 会先查一下它的构造函数 (Array) 的原型对象 Array.prototype 有没有有hasOwnPrototype()方法,没查到的话继续查一下 Array.prototype 的原型对象 Array.prototype.__proto__有没有这个方法。

当我们创建一个函数时:

var f = new Function("x","return x*x;");
//当然你也可以这么创建 f = function(x){ return x*x }
console.log(f.arguments) // arguments 方法从哪里来的?
console.log(f.call(window)) // call 方法从哪里来的?
console.log(Function.prototype) // function() {} (一个空的函数)
console.log(Object.getOwnPropertyNames(Function.prototype)); 
/* 输出
["length", "name", "arguments", "caller", "constructor", "bind", "toString", "call", "apply"]
*/

我们再复习第八小节这句话:

所有函数对象proto都指向 Function.prototype,它是一个空函数(Empty function)

嗯,我们验证了它就是空函数。不过不要忽略前半句。我们枚举出了它的所有的方法,所以所有的函数对象都能用,比如:

Detaillierte Erläuterung des JS-Prototyps und der Prototypenkette (3)

九. 复习一下

第八小节我们总结了:

所有函数对象的 __proto__ 都指向 Function.prototype,它是一个空函数(Empty function)

但是你可别忘了在第三小节我们总结的:

所有对象的 __proto__ 都指向其构造器的 prototype

我们下面再复习下这句话。

先看看 JS 内置构造器:

var obj = {name: 'jack'}
var arr = [1,2,3]
var reg = /hello/g
var date = new Date
var err = new Error('exception') 
console.log(obj.__proto__ === Object.prototype) // true
console.log(arr.__proto__ === Array.prototype)  // true
console.log(reg.__proto__ === RegExp.prototype) // true
console.log(date.__proto__ === Date.prototype)  // true
console.log(err.__proto__ === Error.prototype)  // true

再看看自定义的构造器,这里定义了一个 Person:

function Person(name) { 
 this.name = name;
}
var p = new Person('jack')
console.log(p.__proto__ === Person.prototype) // true

p 是 Person 的实例对象,p 的内部原型总是指向其构造器 Person 的原型对象 prototype。

每个对象都有一个 constructor 属性,可以获取它的构造器,因此以下打印结果也是恒等的:

function Person(name) {
    this.name = name
}
var p = new Person('jack')
console.log(p.__proto__ === p.constructor.prototype) // true

上面的Person没有给其原型添加属性或方法,这里给其原型添加一个getName方法:

function Person(name) { 
   this.name = name
}
// 修改原型
Person.prototype.getName = function() {}
var p = new Person('jack')
console.log(p.__proto__ === Person.prototype) // true
console.log(p.__proto__ === p.constructor.prototype) // true

可以看到p.__proto__与Person.prototype,p.constructor.prototype都是恒等的,即都指向同一个对象。

如果换一种方式设置原型,结果就有些不同了:

function Person(name) {  
  this.name = name
}
// 重写原型Person.prototype = {  
  getName: function() {}
}
var p = new Person('jack')
console.log(p.__proto__ === Person.prototype) // true
console.log(p.__proto__ === p.constructor.prototype) // false

这里直接重写了 Person.prototype(注意:上一个示例是修改原型)。输出结果可以看出p.__proto__仍然指向的是Person.prototype,而不是p.constructor.prototype。

这也很好理解,给Person.prototype赋值的是一个对象直接量{getName: function(){}},使用对象直接量方式定义的对象其构造器(constructor)指向的是根构造器Object,Object.prototype是一个空对象{},{}自然与{getName: function(){}}不等。如下:

var p = {}
console.log(Object.prototype) // 为一个空的对象{}
console.log(p.constructor === Object) // 对象直接量方式定义的对象其constructor为Objectconsole.log(p.constructor.prototype === Object.prototype) // 为true,
不解释(๑ˇ3ˇ๑)

十. 原型链(再复习一下:)

下面这个例子你应该能明白了!

function Person(){
}var person1 = new Person();
console.log(person1.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype) //true
console.log(Object.prototype.__proto__) //null
Person.__proto__ == Function.prototype; //true
console.log(Function.prototype)// function(){} (空函数)
var num = new Array()console.log(num.__proto__ == Array.prototype) // true
console.log( Array.prototype.__proto__ == Object.prototype) // true
console.log(Array.prototype) // [] (空数组)
console.log(Object.prototype.__proto__) //null
console.log(Array.__proto__ == Function.prototype)// true

疑点解惑:

Object.__proto__ === Function.prototype // true

Object 是函数对象,是通过new Function()创建的,所以Object.__proto__指向Function.prototype。(参照第八小节:「所有函数对象的__proto__都指向Function.prototype」)

Function.__proto__ === Function.prototype // true

Function 也是对象函数,也是通过new Function()创建,所以Function.__proto__指向Function.prototype。

自己是由自己创建的,好像不符合逻辑,但仔细想想,现实世界也有些类似,你是怎么来的,你妈生的,你妈怎么来的,你姥姥生的,……类人猿进化来的,那类人猿从哪来,一直追溯下去……,就是无,(NULL生万物)
正如《道德经》里所说“无,名天地之始”。

Function.prototype.__proto__ === Object.prototype //true

其实这一点我也有点困惑,不过也可以试着解释一下。
Function.prototype是个函数对象,理论上他的__proto__应该指向 Function.prototype,就是他自己,自己指向自己,没有意义。
JS一直强调万物皆对象,函数对象也是对象,给他认个祖宗,指向Object.prototype。Object.prototype.__proto__ === null,保证原型链能够正常结束。

十一 总结

原型和原型链是JS实现继承的一种模型。

原型链的形成是真正是靠__proto__ 而非prototype

要深入理解这句话,我们再举个例子,看看前面你真的理解了吗?

var animal = function(){}; var dog = function(){};
 animal.price = 2000;
 dog.prototype = animal; var tidy = new dog(); console.log(dog.price) //undefined
 console.log(tidy.price) // 2000

这里解释一下:

var dog = function(){};
 dog.prototype.price = 2000; var tidy = new dog(); console.log(tidy.price); // 2000
 console.log(dog.price); //undefined
 var dog = function(){}; var tidy = new dog();
 tidy.price = 2000; console.log(dog.price); //undefined

这个明白吧?想一想我们上面说过这句话:

实例(tidy)和 原型对象(dog.prototype)存在一个连接。不过,要明确的真正重要的一点就是,这个连接存在于实例(tidy)与构造函数的原型对象(dog.prototype)之间,而不是存在于实例(tidy)与构造函数(dog)之间。

聪明的你肯定想通了吧 :)

Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung des JS-Prototyps und der Prototypenkette (3). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn