プロトタイプは JavaScript で理解するのが難しい概念です。オブジェクトには「[[プロトタイプ]]」属性があり、関数オブジェクトには「プロトタイプ」属性があり、プロトタイプ オブジェクトには「コンストラクター」属性があります。
この記事は、プロトタイプとそれに関連する属性関係を明確にするために書かれました。
この記事を通じてプロトタイプを明確に理解し、今すぐプロトタイプの旅を始めることができると思います。
プロトタイプの紹介を始める前に、まずプロトタイプとは何かを理解しましょう。
JavaScript では、 プロトタイプもオブジェクトです。プロトタイプを通じて、オブジェクトのプロパティの継承を実現できます。 JavaScript オブジェクトにはすべて、「[[Prototype]]」という内部属性が含まれます。オブジェクト。 。
「[[Prototype]]」はオブジェクトの内部プロパティであり、直接アクセスすることはできません。したがって、オブジェクトのプロトタイプを便利に表示するために、Firefox と Chrome は 非標準 (すべてのブラウザーでサポートされているわけではありません) 「__proto__」アクセサーを提供します (ECMA は、標準オブジェクト プロトタイプ アクセサー「Object.getPrototype(object)」を導入しました) 。
例を通してプロトタイプ関連の概念を見てみましょう:
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);
上記のコードでは、will オブジェクトが Person コンストラクターを通じて作成されます。オブジェクトの意志を通じて、プロトタイプについて段階的に学習しましょう。
ステップ 1: オブジェクト will のプロトタイプを表示する
次のコードを通じてオブジェクト will のプロトタイプを表示できます:
console.log(will.__proto__); console.log(will.constructor);
結果分析:
「person {}」オブジェクトはプロトタイプですご覧のとおり、「人 {}」はプロトタイプ オブジェクトとして、「__proto__」属性(プロトタイプのプロトタイプに対応)も持っています。
このコードでは、「constructor」属性も使用されています。 JavaScript の プロトタイプ オブジェクト には、プロトタイプを指すすべてのインスタンスを作成するコンストラクター に対応する「constructor」属性も含まれています。
function isArray(myArray) { return myArray.constructor.toString().indexOf("Array") > -1; }
willオブジェクト自体には「constructor」属性がありませんが、プロトタイプチェーン検索を通じてを実行すると、ウィル プロトタイプ (will.__proto__) の「コンストラクター」属性を取得し、パーソン関数を取得できます。
ステップ 2: オブジェクト will のプロトタイプを表示する (will.__proto__)
will のプロトタイプ「person {}」もオブジェクトであるため、「」のプロトタイプを表示することもできます。 will「プロトタイプのプロトタイプ(will.__proto__)」。 次のコードを実行します:console.log(will.__proto__ === Person.prototype); console.log(Person.prototype.__proto__); console.log(Person.prototype.constructor); console.log(Person.prototype.constructor === Person);
JavaScriptでは、関数が使用されるとき、各関数にはプロトタイプ属性があります。コンストラクターを使用してインスタンスを作成すると、関数のプロトタイプ属性値がプロトタイプとしてすべてのオブジェクト インスタンスに割り当てられます (つまり、インスタンスの __proto__ 属性が設定されます)。つまり、すべてのインスタンスのプロトタイプが参照されます。関数のプロトタイプ属性 。コンストラクターのプロトタイプ属性を理解すると、最初の文の結果が true になる理由が確実に理解できるでしょう。
"person.prototype" オブジェクトと Person 関数オブジェクトは、"constructor" 属性と "prototype" 属性を通じて相互参照を実現しています (この相互参照を示す図は後で説明します)関係)。
ステップ 3: オブジェクト Object のプロトタイプを表示する
前のパートからわかるように、will のプロトタイプのプロトタイプは「Object {}」オブジェクトです。実際、JavaScript では、すべてのオブジェクトのプロトタイプは「Object {}」オブジェクトにまで遡ります。 コードを通して「Object {}」オブジェクトを見てみましょう: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);
另外,当通过”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”属性进行相互关联
还有要强调的是文章开始的例子,以及通过例子得到的一张”普通对象”,”函数对象”和”原型对象”之间的关系图,当你对原型的关系迷惑的时候,就想想这张图(或者重画一张当前对象的关系图),就可以理清这里面的复杂关系了。
通过这些介绍,相信一定可以对原型有个清晰的认识。
以上がJavaScript プロトタイプの概念を深く理解するコード例 (写真)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。