Javascript ist eine prototypbasierte Programmiersprache, die sich stark von unserer üblichen klassenbasierten Programmierung unterscheidet. Ich werde die wichtigen Punkte wie folgt auflisten:
1. Funktion ist ein erstklassiges Objekt, was bedeutet, dass Funktion und Objekt den gleichen Sprachstatus haben
2. Es gibt keine Klassen, nur Objekte
3. Eine Funktion ist auch eine Art Objekt, das sogenannte Funktionsobjekt
4. Objekte werden als Referenz
übergeben
Wie implementiert diese prototypbasierte Programmiersprache also die Vererbung (ein Grundelement von OO), die den Ursprung des Prototyps darstellt?
Sehen Sie sich den folgenden Codeausschnitt an:
function foo(a, b, c) { return a*b*c; } alert(foo.length); alert(typeof foo.constructor); alert(typeof foo.call); alert(typeof foo.apply); alert(typeof foo.prototype);
Für den obigen Code finden Sie nach der Ausführung in einem Browser Folgendes:
1.Länge: Gibt die Anzahl der Parameter der Funktion an
2.Prototyp: ist ein Objekt
3. Die anderen drei sind Funktionen
Für die Deklaration einer Funktion verfügt diese über die oben genannten 5 Eigenschaften (Methoden oder Attribute).
Werfen wir einen Blick auf den Prototyp.
// prototype function Person(name, gender) { this.name = name; this.gender = gender; this.whoAreYou = function(){//这个也是所谓的closure, 内部函数可以访问外部函数的变量 var res = "I'm " + this.name + " and I'm a " + this.gender +"."; return res; }; } // 那么在由Person创建的对象便具有了下面的几个属性 Person.prototype.age = 24; Person.prototype.getAge = function(){ return this.age; }; flag = true; if (flag) { var fun = new Person("Tower", "male"); alert(fun.name); alert(fun.gender); alert(fun.whoAreYou()); alert(fun.getAge()); } Person.prototype.salary = 10000; Person.prototype.getSalary = function(){ return this.name + " can earn about " + this.salary + "RMB each month." ; }; // 下面就是最神奇的地方, 我们改变了Person的prototype,而这个改变是在创建fun之后 // 而这个改变使得fun也具有了相同的属性和方法 // 继承的意味即此 if (flag) { alert(fun.getSalary()); alert(fun.constructor.prototype.age);//而这个相当于你直接调用 Person.prototype.age alert(Person.prototype.age); }
Aus dem obigen Beispiel können wir ersehen, dass wir Methoden oder Attribute des Prototyps dynamisch hinzufügen können und die von ihm erstellten Objekte automatisch die relevanten Methoden und Attribute erben.
Darüber hinaus verfügt jedes Objekt über ein Konstruktorattribut, das verwendet wird, um auf das Funktionsobjekt zu verweisen, das es erstellt hat. Beispielsweise zeigt der fun.constructor im obigen Beispiel auf Person.
Dann stellt sich natürlich die Frage: Was ist der Unterschied zwischen den vom Funktionsobjekt deklarierten Methoden und Attributen und dem vom Prototyp deklarierten Objekt?
Es gibt mehrere Unterschiede:
1. Die von Ihnen deklarierten Methoden und Attribute sind statisch. Wenn Sie also versuchen, nach der Deklaration neue Methoden hinzuzufügen oder vorhandene Methoden zu ändern, hat dies keine Auswirkungen auf die von ihnen erstellten Objekte und auch nicht auf die erstellten Objekte von ihnen. Das heißt, die Vererbung ist fehlgeschlagen
2. Der Prototyp kann dynamisch neue Methoden hinzufügen oder vorhandene Methoden ändern, sodass er dynamisch ist. Sobald das übergeordnete Funktionsobjekt die relevanten Prototypattribute deklariert, erbt das von ihm erstellte Objekt diese Prototypattribute automatisch.
Fahren Sie mit dem obigen Beispiel fort:
flag = true; // 函数内部声明的方法是静态的,无法传递的 Person.school = "ISCAS"; Person.whoAreYou = function(){ return "zhutao"; };//动态更改声明期的方法,并不会影响由其创建的对象的方法, 即所谓的 静态 if (flag) { alert(Person.school); alert(fun.school);//输出的是 "undefined" alert(Person.whoAreYou()); //输出 zhutao alert(fun.whoAreYou()); // I'm Tower and I'm a male. } Person.prototype.getSalary = function(){ return "I can earn 1000000 USD"; }; if (flag) { alert(fun.getSalary());//已经继承了改变, 即所谓的 动态 }
Da es Attribute des Funktionsobjekts selbst und Attribute des Prototyps gibt, wie sucht das von ihm erstellte Objekt nach den entsprechenden Attributen?
Befolgen Sie im Wesentlichen den unten aufgeführten Prozess und die folgende Reihenfolge.
1. Suchen Sie zunächst nach den Eigenschaften des Funktionsobjekts selbst und führen Sie es sofort aus, wenn es gefunden wird
2. Wenn 1 nicht gefunden wird, wird nach dem Prototypattribut gesucht. Es gibt zwei Ergebnisse. Wenn es gefunden wird, wird die Suche nach dem Prototyp des übergeordneten Objekts fortgesetzt gefunden wird oder das Ende der Prototypenkette erreicht (das Ende wird das Objektobjekt sein)
Das Obige beantwortet auch die Lösung, wenn die Eigenschaften des Funktionsobjekts selbst mit den Prototypeigenschaften (doppelte Namen) übereinstimmen, hat das Objekt der Funktion selbst Vorrang.
Typisches Beispiel eines Prototyps
Freunde, die jQuery- oder Prototype-Bibliotheken verwendet haben, wissen möglicherweise, dass diese Bibliotheken normalerweise über die Trim-Methode verfügen.
Beispiel:
String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g, ''); };
Zuschnittverwendung:
' foo bar '.trim(); // 'foo bar'
Dies hat jedoch noch einen weiteren Nachteil, da die JavaScript-Engine in neueren Versionen des Browsers selbst die Trim-Methode im String-Objekt bereitstellt, sodass der von uns definierte Trim seinen eigenen Trim überschreibt. Bevor wir die Trimmmethode definieren, können wir tatsächlich einen einfachen Test durchführen, um zu sehen, ob wir diese Methode selbst hinzufügen müssen:
if(!String.prototype.trim) { String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g, ''); }; }
Prototypenkette
Wenn ein Objekt in JavaScript definiert oder instanziiert wird, wird ihm ein verstecktes Attribut namens __proto__ angehängt, und die Prototypenkette stützt sich bei der Bildung auf dieses Attribut. Greifen Sie jedoch niemals direkt auf das Attribut __proto__ zu, da einige Browser den direkten Zugriff darauf nicht unterstützen. Darüber hinaus sind __proto__ und die Prototypeigenschaft eines Objekts nicht dasselbe. Sie haben jeweils ihren eigenen Zweck.
Wie ist das zu verstehen? Tatsächlich erstellen wir beim Erstellen der myObject-Funktion tatsächlich ein Objekt vom Typ Function:
console.log(typeof myObject); // function
Hier ist zu beachten, dass Function ein vordefiniertes Objekt in JavaScript ist und daher auch über eigene vordefinierte Attribute (wie Länge und Argumente) und Methoden (wie call und apply) verfügt Implementieren Sie die Prototypenkette. Mit anderen Worten, es kann innerhalb der JavaScript-Engine Codeausschnitte geben, die den folgenden ähneln:
Function.prototype = { arguments: null, length: 0, call: function() { // secret code }, apply: function(){ // secret code }, ... };
Tatsächlich kann der JavaScript-Engine-Code nicht so einfach sein. Hier ist nur eine Beschreibung, wie die Prototypenkette funktioniert.
Wir definieren eine Funktion myObject, die ebenfalls einen Parameternamen hat, ihr aber keine weiteren Eigenschaften, wie Länge oder andere Methoden, wie Call, gibt. Warum wird der folgende Code normal ausgeführt?
console.log(myObject.length); // 结果:1,是参数的个数
这是因为我们定义 myObject 时,同时也给它定义了一个 __proto__ 属性,并赋值为 Function.prototype(参考前面的代码片段),所以我们能够像访问其它属性一样访问 myObject.length,即使我们并没有定义这个属性,因为它会顺着 __proto__ 原型链往上去找 length,最终在 Function 里面找到了。
那为什么找到的 length 属性的值是 1,而不是 0 呢,是什么时候给它赋值的呢?由于 myObject 是 Function 的一个实例:
console.log(myObject instanceof Function); // true console.log(myObject === Function); // false
当实例化一个对象的时候,对象的 __proto__ 属性会被赋值为其构造者的原型对象,在本示例中就是 Function,此时构造器回去计算参数的个数,改变 length 的值。
console.log(myObject.__proto__ === Function.prototype); // true
而当我们用 new 关键字创建一个新的实例时,新对象的 __proto__ 将会被赋值为 myObject.prototype,因为现在的构造函数为 myObject,而非 Function。
var myInstance = new myObject('foo'); console.log(myInstance.__proto__ === myObject.prototype); // true
新对象除了能访问 Function.prototype 中继承下来的 call 和 apply 外,还能访问从 myObject 中继承下来的 getName 方法:
console.log(myInstance.getName()); // foo var mySecondInstance = new myObject('bar'); console.log(mySecondInstance.getName()); // bar console.log(myInstance.getName()); // foo
其实这相当于把原型对象当做一个蓝本,然后可以根据这个蓝本创建 N 个新的对象。
再看一个多重prototype链的例子:
// 多重prototype链的例子 function Employee(name) { this.name = ""; this.dept = "general"; this.gender = "unknown"; } function WorkerBee() { this.projects = []; this.hasCar = false; } WorkerBee.prototype = new Employee; // 第一层prototype链 function Engineer() { this.dept = "engineer"; //覆盖了 "父对象" this.language = "javascript"; } Engineer.prototype = new WorkerBee; // 第二层prototype链 var jay = new Engineer("Jay"); if (flag) { alert(jay.dept); //engineer, 找到的是自己的属性 alert(jay.hasCar); // false, 搜索到的是自己上一层的属性 alert(jay.gender); // unknown, 搜索到的是自己上二层的属性 }
上面这个示例的对象关系如下: