Le prototype est un concept difficile à comprendre en JavaScript. Il existe de nombreux attributs liés au prototype. Les objets ont des attributs "[[prototype]]", les objets fonction ont des attributs "prototype" et les objets prototypes ont des attributs "constructeur".
Afin de comprendre le prototype et les relations d'attributs liées au prototype, cet article a été rédigé.
Je pense que vous serez en mesure de comprendre clairement le prototype grâce à cet article et de commencer le voyage du prototype maintenant.
Avant de commencer l'introduction des prototypes, comprenons d'abord ce qu'est un prototype ?
En JavaScript, le prototype est aussi un objet. L'héritage des attributs de l'objet peut être réalisé via le prototype Les objets JavaScript contiennent tous un attribut interne "[[Prototype]]". Cet attribut Celui correspondant est le prototype de l'objet.
"[[Prototype]]" est une propriété interne de l'objet et n'est pas accessible directement. Ainsi, afin de visualiser facilement le prototype d'un objet, Firefox et Chrome fournissent l'accesseur "__proto__" non standard (non pris en charge par tous les navigateurs) (ECMA a introduit l'accesseur de prototype d'objet standard "Object. getPrototype( objet)").
Examinons les concepts liés aux prototypes à travers un exemple :
function Person(name, age){ this.name = name; this.age = age; this.getInfo = function(){ console.log(this.name + " is " + this.age + " years old"); }; } var will = new Person("Will", 28);
Dans le code ci-dessus, un testament est créé via l'objet constructeur Personne . Utilisons l'objet volonté pour comprendre le prototype étape par étape.
Étape 1 : Visualiser le prototype de l'objet will
Grâce au code suivant, vous pouvez visualiser le prototype de l'objet will :
console.log(will.__proto__); console.log(will.constructor);
Analyse des résultats :
L'objet "Person {}" est le prototype de l'objet will. Vous pouvez voir en le développant dans Chrome que "Person {}", en tant que prototype. object, possède également l'attribut "__proto__" (correspondant au prototype du prototype).
Dans ce code, l'attribut "constructeur" est également utilisé. Dans l'objet prototype de JavaScript, il contient également un attribut "constructeur", qui correspond au constructeur qui crée toutes les instances pointant vers le prototype.
Grâce à l'attribut "constructeur", nous pouvons déterminer si un objet est de type tableau
function isArray(myArray) { return myArray.constructor.toString().indexOf("Array") > -1; }
Ici, L'objet testament lui-même n'a pas l'attribut « constructeur » , mais grâce à la recherche en chaîne de prototypes, l'attribut « constructeur » du prototype testamentaire (will.__proto__) a été trouvé et la fonction Personne a été obtenue.
Étape 2 : Visualiser le prototype de l'objet will (will.__proto__)
Puisque le prototype de will "Person {}" est aussi un objet, on peut également vérifier le prototype de "will's prototype (will.__proto__)".
Exécutez le code suivant :
console.log(will.__proto__ === Person.prototype); console.log(Person.prototype.__proto__); console.log(Person.prototype.constructor); console.log(Person.prototype.constructor === Person);
Analyse du résultat :
Premier coup d'oeil à "will.__proto__ === Person.prototype", En JavaScript, chaque fonction possède un attribut prototype. Lorsqu'une fonction est utilisée comme constructeur pour créer une instance, la valeur de l'attribut prototype de la fonction sera attribuée à toutes les instances d'objet en tant que prototype (c'est-à-dire en définissant la valeur de l'instance). _ _proto__ attribut), c'est-à-dire que les prototypes de toutes les instances font référence à l'attribut prototype de la fonction. Après avoir compris l'attribut prototype du constructeur, vous comprendrez certainement pourquoi la première phrase est vraie.
L'attribut prototype est unique aux objets fonction. S'il ne s'agit pas d'un objet fonction, il n'y aura pas un tel attribut.
Lorsque vous obtiendrez le prototype du prototype d'objet testamentaire via l'instruction "Person.prototype.__proto__", vous obtiendrez l'objet "Object {}", et vous obtiendrez voir tous les objets plus tard Le prototype sera retracé jusqu'à l'objet "Object {}".
Pour le "constructeur" de l'objet prototype "Person.prototype", selon l'introduction précédente, il correspondra à la fonction Personne elle-même.
Comme vous pouvez le voir ci-dessus, l'objet "Person.prototype" et l'objet fonction Personne se référencent mutuellement via les attributs "constructeur" et "prototype" (comme montré dans la figure plus loin) Cette relation de référence mutuelle) .
Étape 3 : Visualiser le prototype de l'objet Objet
Comme vous pouvez le voir dans la partie précédente, le prototype de Will le prototype est l'objet "Object {}". En fait, en JavaScript, le prototype de tous les objets remontera à l'objet "Object {}".
Regardez l'objet "Object {}" à travers un morceau de code :
console.log(Person.prototype.__proto__ === Object.prototype); console.log(typeof Object); console.log(Object); console.log(Object.prototype); console.log(Object.prototype.__proto__); console.log(Object.prototype.constructor);
Vous pouvez le voir à travers le code suivant :
Objet Objet Lui-même est un objet fonction.
Puisqu'il s'agit d'une fonction Objet, elle doit avoir un attribut prototype, vous pouvez donc voir que la valeur de "Object.prototype" est l'objet prototype "Object {}".
À votre tour, lorsque vous accédez à la propriété "constructeur" de l'objet "Object.prototype", vous obtenez la fonction Objet.
另外,当通过”Object.prototype.__proto__”获取Object原型的原型的时候,将会得到”null”,也就是说”Object {}”原型对象就是原型链的终点了。
Step 4: 查看对象Function的原型
在上面的例子中,Person是一个构造函数,在JavaScript中函数也是对象,所以,我们也可以通过”__proto__”属性来查找Person函数对象的原型。
console.log(Person.__proto__ === Function.prototype); console.log(Person.constructor === Function) console.log(typeof Function); console.log(Function); console.log(Function.prototype); console.log(Function.prototype.__proto__); console.log(Function.prototype.constructor);
结果分析 :
在JavaScript中有个Function对象(类似Object),这个对象本身是个函数;所有的函数(包括Function,Object)的原型(__proto__)都是”Function.prototype”。
Function对象作为一个函数,就会有prototype属性,该属性将对应”function () {}”对象。
Function对象作为一个对象,就有”__proto__”属性,该属性对应”Function.prototype”,也就是说,”Function.__proto__ === Function.prototype”
对于Function的原型对象”Function.prototype”,该原型对象的”__proto__”属性将对应”Object {}”
对于”prototype”和”__proto__”这两个属性有的时候可能会弄混,”Person.prototype”和”Person.__proto__”是完全不同的。
在这里对”prototype”和”__proto__”进行简单的介绍:
对于所有的对象,都有__proto__属性,这个属性对应该对象的原型
对于函数对象,除了__proto__属性之外,还有prototype属性,当一个函数被用作构造函数来创建实例时,该函数的prototype属性值将被作为原型赋值给所有对象实例(也就是设置实例的__proto__属性)
通过上面结合实例的分析,相信你一定了解了原型中的很多内容。
但是现在肯定对上面例子中的关系感觉很凌乱,一会儿原型,一会儿原型的原型,还有Function,Object,constructor,prototype等等关系。
现在就对上面的例子中分析得到的结果/关系进行图解,相信这张图可以让你豁然开朗。
对于上图的总结如下:
所有的对象都有”__proto__”属性,该属性对应该对象的原型
所有的函数对象都有”prototype”属性,该属性的值会被赋值给该函数创建的对象的”__proto__”属性
所有的原型对象都有”constructor”属性,该属性对应创建所有指向该原型的实例的构造函数
函数对象和原型对象通过”prototype”和”constructor”属性进行相互关联
在上面例子中,”getInfo”方法是构造函数Person的一个成员,当通过Person构造两个实例的时候,每个实例都会包含一个”getInfo”方法。
var will = new Person("Will", 28); var wilber = new Person("Wilber", 27);
前面了解到,原型就是为了方便实现属性的继承,所以可以将”getInfo”方法当作Person原型(Person.__proto__)的一个属性,这样所有的实例都可以通过原型继承的方式来使用”getInfo”这个方法了。
所以对例子进行如下修改:
function Person(name, age){ this.name = name; this.age = age; } Person.prototype.getInfo = function(){ console.log(this.name + " is " + this.age + " years old"); };
修改后的结果为:
因为每个对象和原型都有原型,对象的原型指向对象的父,而父的原型又指向父的父,这种原型层层连接起来的就构成了原型链。
在”理解JavaScript的作用域链“一文中,已经介绍了标识符和属性通过作用域链和原型链的查找。
这里就继续看一下基于原型链的属性查找。
当查找一个对象的属性时,JavaScript 会向上遍历原型链,直到找到给定名称的属性为止,到查找到达原型链的顶部(也就是 “Object.prototype”), 如果仍然没有找到指定的属性,就会返回 undefined。
看一个例子:
function Person(name, age){ this.name = name; this.age = age; } Person.prototype.MaxNumber = 9999; Person.__proto__.MinNumber = -9999; var will = new Person("Will", 28); console.log(will.MaxNumber); // 9999 console.log(will.MinNumber); // undefined
在这个例子中分别给”Person.prototype “和” Person.__proto__”这两个原型对象添加了”MaxNumber “和”MinNumber”属性,这里就需要弄清”prototype”和”__proto__”的区别了。
“Person.prototype “对应的就是Person构造出来所有实例的原型,也就是说”Person.prototype “属于这些实例原型链的一部分,所以当这些实例进行属性查找时候,就会引用到”Person.prototype “中的属性。
当通过原型链查找一个属性的时候,首先查找的是对象本身的属性,如果找不到才会继续按照原型链进行查找。
这样一来,如果想要覆盖原型链上的一些属性,我们就可以直接在对象中引入这些属性,达到属性隐藏的效果。
看一个简单的例子:
function Person(name, age){ this.name = name; this.age = age; } Person.prototype.getInfo = function(){ console.log(this.name + " is " + this.age + " years old"); }; var will = new Person("Will", 28); will.getInfo = function(){ console.log("getInfo method from will instead of prototype"); }; will.getInfo(); // getInfo method from will instead of prototype
会到本文开始的例子,will对象通过Person构造函数创建,所以will的原型(will.__proto__)就是”Person.prototype”。
同样,我们可以通过下面的方式创建一个对象:
var July = { name: "July", age: 28, getInfo: function(){ console.log(this.name + " is " + this.age + " years old"); }, } console.log(July.getInfo());
当使用这种方式创建一个对象的时候,原型链就变成下图了,July对象的原型是”Object.prototype”也就是说对象的构建方式会影响原型链的形式。
“hasOwnProperty”是”Object.prototype”的一个方法,该方法能判断一个对象是否包含自定义属性而不是原型链上的属性,因为”hasOwnProperty” 是 JavaScript 中唯一一个处理属性但是不查找原型链的函数。
相信你还记得文章最开始的例子中,通过will我们可以访问”constructor”这个属性,并得到will的构造函数Person。这里结合”hasOwnProperty”这个函数就可以看到,will对象并没有”constructor”这个属性。
从下面的输出可以看到,”constructor”是will的原型(will.__proto__)的属性,但是通过原型链的查找,will对象可以发现并使用”constructor”属性。
“hasOwnProperty”还有一个重要的使用场景,就是用来遍历对象的属性。
function Person(name, age){ this.name = name; this.age = age; } Person.prototype.getInfo = function(){ console.log(this.name + " is " + this.age + " years old"); }; var will = new Person("Will", 28); for(var attr in will){ console.log(attr); } // name // age // getInfo for(var attr in will){ if(will.hasOwnProperty(attr)){ console.log(attr); } } // name // age
本文介绍了JavaScript中原型相关的概念,对于原型可以归纳出下面一些点:
所有的对象都有”[[prototype]]”属性(通过__proto__访问),该属性对应对象的原型
所有的函数对象都有”prototype”属性,该属性的值会被赋值给该函数创建的对象的”__proto__”属性
所有的原型对象都有”constructor”属性,该属性对应创建所有指向该原型的实例的构造函数
函数对象和原型对象通过”prototype”和”constructor”属性进行相互关联
还有要强调的是文章开始的例子,以及通过例子得到的一张”普通对象”,”函数对象”和”原型对象”之间的关系图,当你对原型的关系迷惑的时候,就想想这张图(或者重画一张当前对象的关系图),就可以理清这里面的复杂关系了。
通过这些介绍,相信一定可以对原型有个清晰的认识。
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!