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:
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:
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:
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等对象的方法(你肯定没发现)。
但是随便定义的数组也能用这些方法
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)
嗯,我们验证了它就是空函数。不过不要忽略前半句。我们枚举出了它的所有的方法,所以所有的函数对象都能用,比如:
九. 复习一下
第八小节我们总结了:
所有函数对象的 __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!