Javascript est un langage de programmation basé sur un prototype, qui est très différent de notre programmation habituelle basée sur les classes. Je vais énumérer les points importants comme suit :
1. La fonction est un objet de première classe, ce qui signifie que la fonction et l'objet ont le même statut de langage
2. Il n'y a pas de classes, seulement des objets
3. Une fonction est aussi une sorte d'objet, ce qu'on appelle l'objet fonction
4. Les objets sont passés par référence
Alors, comment ce langage de programmation basé sur un prototype implémente-t-il l'héritage (un élément de base de OO), qui est à l'origine du prototype.
Regardez l'extrait de code ci-dessous :
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);
Pour le code ci-dessus, après l'avoir exécuté dans un navigateur, vous trouverez :
1.length : Fournit le nombre de paramètres de la fonction
2.prototype : est un objet
3. Les trois autres sont des fonctions
Pour la déclaration de n'importe quelle fonction, elle aura les 5 propriétés (méthodes ou attributs) mentionnées ci-dessus.
Jetons un coup d'œil au prototype.
// 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); }
À partir de l'exemple ci-dessus, nous pouvons constater que nous pouvons ajouter dynamiquement des méthodes ou des attributs du prototype, et que les objets créés par celui-ci hériteront automatiquement des méthodes et des attributs pertinents.
De plus, chaque objet possède un attribut constructeur, qui est utilisé pour pointer vers l'objet fonction qui l'a créé. Par exemple, le fun.constructor dans l'exemple ci-dessus pointe vers Personne.
Alors une question se pose naturellement, quelle est la différence entre les méthodes et attributs déclarés par l'objet fonction et l'objet déclaré par le prototype ?
Il existe plusieurs différences :
1. Les méthodes et attributs déclarés par vous-même sont statiques, ce qui signifie que si vous essayez d'ajouter de nouvelles méthodes ou de modifier des méthodes existantes après les avoir déclarées, cela n'affectera pas les objets créés par eux, ni les objets créés. par eux. Autrement dit, l'héritage a échoué
2. Le prototype peut ajouter dynamiquement de nouvelles méthodes ou modifier les méthodes existantes, il est donc dynamique une fois que l'objet fonction parent déclare les attributs de prototype pertinents, l'objet créé par celui-ci héritera automatiquement de ces attributs de prototype.
En continuant avec l'exemple ci-dessus :
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());//已经继承了改变, 即所谓的 动态 }
Puisqu'il existe des attributs de l'objet fonction lui-même et des attributs du prototype, comment l'objet créé par celui-ci recherche-t-il les attributs correspondants ?
Suivez essentiellement le processus et la séquence ci-dessous.
1. Recherchez d'abord les propriétés de l'objet fonction lui-même et exécutez-le immédiatement s'il est trouvé
2. Si 1 n'est pas trouvé, il recherchera l'attribut prototype. Il y aura deux résultats. S'il est trouvé, il sera exécuté directement, sinon il continuera à rechercher le prototype de l'objet parent jusqu'à ce qu'il soit trouvé. est trouvé ou atteint la fin de la chaîne du prototype (la fin sera l'objet Object)
Ce qui précède répond également à la solution si les propriétés de l'objet fonction lui-même sont les mêmes que les propriétés du prototype (noms en double), l'objet de la fonction elle-même est prioritaire.
Exemple typique de prototype
Les amis qui ont utilisé des bibliothèques jQuery ou Prototype savent peut-être que ces bibliothèques ont généralement la méthode trim.
Exemple :
String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g, ''); };
utilisation des garnitures :
' foo bar '.trim(); // 'foo bar'
Mais il y a un autre inconvénient à faire cela, car le moteur JavaScript dans les versions plus récentes du navigateur lui-même fournit la méthode trim dans l'objet String, donc le trim que nous définissons écrasera son propre trim. En fait, avant de définir la méthode de trim, nous pouvons faire un test simple pour voir si nous devons ajouter cette méthode nous-mêmes :
if(!String.prototype.trim) { String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g, ''); }; }
Chaîne prototype
Lorsqu'un objet est défini ou instancié en JavaScript, il sera ajouté à un attribut caché appelé __proto__, et la chaîne de prototypes s'appuie sur cet attribut pour se former. Mais n'accédez jamais directement à l'attribut __proto__, car certains navigateurs ne prennent pas en charge l'accès direct à celui-ci. De plus, __proto__ et la propriété prototype d'un objet ne sont pas la même chose. Ils ont chacun leurs propres objectifs.
Comment comprendre ? En fait, lorsque nous créons la fonction myObject, nous créons en fait un objet de type Function :
console.log(typeof myObject); // fonction
Il convient de noter ici que Function est un objet prédéfini en JavaScript, il possède donc également ses propres attributs prédéfinis (tels que la longueur et les arguments) et ses propres méthodes (telles que call et apply). Bien sûr, il a également __proto__ pour). mettre en œuvre la chaîne prototype. En d’autres termes, il peut y avoir des extraits de code similaires aux suivants dans le moteur JavaScript :
Function.prototype = { arguments: null, length: 0, call: function() { // secret code }, apply: function(){ // secret code }, ... };
En fait, le code du moteur JavaScript ne peut pas être aussi simple. Voici juste une description du fonctionnement de la chaîne de prototypes.
Nous définissons une fonction myObject, qui a également un nom de paramètre, mais ne lui donne aucune autre propriété, comme la longueur ou d'autres méthodes, comme call. Alors pourquoi le code suivant s’exécute-t-il normalement ?
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, 搜索到的是自己上二层的属性 }
上面这个示例的对象关系如下: