内容:
オブジェクト作成のいくつかのモードと作成プロセス
プロトタイプチェーンプロトタイプ、prototype
と __proto__
([ [プロトタイプ]]
) 関係prototype
与 __proto__
([[Prototype]]
)的关系
继承的几种实现
a.构造函数创建
function Test() {// }
流程
创建函数的时候会默认为Test创建一个prototype属性,Test.prototype
包含一个指针指向的是Object.prototype
prototype默认会有一个constructor,且Test.prototype.constructor = Test
prototype里的其它方法都是从Object继承而来
// 调用构造函数创建实例 var instance = new Test()
此处的instance包含了一个指针指向构造函数的原型,(此处的指针在chrome里叫__proto__
,也等于[[Prototype]]
)
b.原型模式
由上我们可以知道,默认创建的prototype属性只拥有constructor和继承至Object的属性,原型模式就是为prototype添加属性和方法
Test.prototype.getName = ()=> { alert('name') }
此时的instance实例就拥有了getName方法,因为实例的指针是指向Test.prototype的
instance.__proto__ === Test.prototype
如下图所示
这里我们可得知:实例instance与构造函数之间是通过原型prototype
来相关联的。
c.组合模式
这种模式我们用的最多,其实也是原型模式的另一种写法,只不过有一点小区别而已
function Test() {} Test.prototype = { getName() { alert('name') } }
我们经常会这么直接重写prototype方法,由上我们可知,prototype会默认自带constructor属性指向构造函数本身,那么重写以后呢?
Test.prototype.constructor === Object // 而并不等于Test了// 因为重写以后相当于利用字面量方式创建一个实例对象,这个实例的构造函数是指向Object本身的
当然我们也可以手动赋值constructor
Test.prototype = { constructor: Test, getName() { alert('name') } }
那么又会有疑问了constructor
要不要有何意义?我觉得constructor意义仅仅是为了来鉴别原型所属的构造函数吧。
当需要获取某个属性的时候,会先从实例中查找,没有就根据指针所指向的原型去查找,依次向上,直到实例的指针__proto__
指向为null时停止查找,例如:
// 1 读取nameinstance.name // 2 instance.__proto__ === Test.prototypeTest.prototype.name // 3 Test.prototype.__proto__ === Object.prototypeObject.prototype.name // 4Object.prototype.__proto__ === null
当找到了这个属性就会直接返回,而不会继续查找,即使这个属性值为null,想要继续查找,我们可以通过delete
操作符来实现。
由这里我们自然可以想到Array, Date, Function, String
,都是一个构造函数,他们的原型的指针都是指向Object.prototype
,它们就像我这里定义的Test
一样,只不过是原生自带而已
d.几个有用的方法
Object.getPrototypeOf()
获取某个实例的指针所指向的原型
Object.getPrototypeOf(instance) === Test.prototype
hasOwnProperty
判断一个属性是存在于实例中还是存在于原型中,如图所示:
in
操作符,无论该属性是否可枚举
'name' in instance // true 'getName' in instance // true
无论属性是在实例中,还是在原型中都返回true,所以当我们需要判断一个属性存在与实例中,还是原型中有2种办法
// 一种就是使用hasOwnProperty判断在实例中 // 另一种判断在原型中 instance.hasOwnProperty('getName') === false && 'getName' in instance === true
for ... in
Object.keys(instance)// ["name"]
Test.prototype
に Object.prototype
を指すポインターが含まれるプロトタイプ属性が作成されます。 🎜🎜🎜プロトタイプにはデフォルトでコンストラクターがあり、Test.prototype.constructor = Test
🎜🎜🎜🎜プロトタイプ内の他のメソッドは Object から継承されます🎜🎜// 定义父类function Parent() {}Parent.prototype.getName = ()=> { console.log('parent') }// 实例化父类let parent = new Parent()// 定义子类function Child() {} Child.prototype = parent // 实例化子类let child = new Child() child.getName() // parent// 此时 child.constructor === parent.constructor === Parent
__proto__
と呼ばれます。これは [[Prototype]]
とも同じです)🎜🎜🎜🎜例🎜🎜🎜b. プロトタイプモード🎜 上記から、prototype 属性が作成されたことがわかります。デフォルトではObjectから継承したコンストラクターとプロパティのみを持ちます。プロトタイプモードはプロトタイプにプロパティとメソッドを追加することです🎜function Parent(name) {this.name = namethis.colors = ['red'] } Parent.prototype.getName = function() {console.log(this.name) }// 实例化父类let parent = new Parent()function Child(age, name) { Parent.call(this, name)this.age = age } Child.prototype = parent // 实例化子类let child = new Child(1, 'aaa') child.getName() // parent
class Parent { constructor(name) {this.name = namethis.colors = ['red'] } getName() { console.log(this.name) } }class Child extends Parent { constructor(age, name) {super(name) } } let child = new Child(1, 'aaa') child.getName() // parent
prototype
を通じて関連付けられていることがわかります。 🎜🎜c. 組み合わせモード🎜 これは実際にはプロトタイプ モードを記述する別の方法ですが、わずかな違いがあります🎜rrreee🎜 上記のことから、次のことがわかります。プロトタイプには、デフォルトでコンストラクター自体を指すコンストラクター プロパティが含まれますが、書き換えられた後はどうなるでしょうか? 🎜rrreee🎜もちろん、コンストラクターを手動で割り当てることもできます🎜rrreee🎜それでは、constructor
の意味は何でしょうか?という質問がまた出てきます。コンストラクターの意味は、プロトタイプが属するコンストラクターを識別することだけだと思います。 🎜🎜特定の属性を取得する必要がある場合、まずインスタンスから検索します。取得できない場合は、ポインタが指すプロトタイプに従って検索し、インスタンス ポインタ __proto__</ まで進みます。 code> は null を指し、検索を停止します。例: 🎜rrreee🎜 この属性が見つかった場合は、検索を続行せずに直接返されます。属性値が null であっても、検索を続行したい場合は、を使用できます。 <code>delete
演算子。 🎜🎜 ここから、Array、Date、Function、String
はすべてコンストラクターであり、それらのプロトタイプ ポインターは Object.prototype
を指しており、それらは に似ていると自然に考えることができます。 >Test
ここで定義したものは単なるネイティブです 🎜🎜d。インスタンス ポインタが指す特定のプロトタイプを取得するためのいくつかの便利なメソッド 🎜🎜🎜🎜 rrreee🎜🎜🎜hasOwnProperty
は、次の図に示すように、プロパティがインスタンスに存在するかプロトタイプに存在するかを判断します。 🎜🎜🎜🎜🎜NY~N}CNR`}8W%4QA$M8LFE4.png🎜🎜🎜🎜🎜 in
演算子、プロパティが列挙可能かどうか 🎜rrreee🎜 はプロパティがインスタンスにあるかプロトタイプにあるかに関係なく true を返すため、プロパティがインスタンスに存在するかプロトタイプに存在するかを判断する必要がある場合 🎜rrreee🎜🎜 には 2 つのメソッドがあります🎜for...in
演算子は同じですが、列挙可能なプロパティのみがリストされます。IE8 バージョンのバグは、プロパティが列挙可能かどうかです。たとえば、リストされます🎜🎜🎜。 🎜🎜D(%S__GN8404{H9X6PW$DVK.png🎜🎜
name是在实例中定义的,getName是在原型中定义的
Object.keys()
则不一样,它返回一个对象上所有可枚举的属性,仅仅是该实例中的
Object.keys(instance)// ["name"]
e.总结
以上讨论了构造函数,原型和实例的关系:
每个构造函数都有原型对象
每个原型对象都有一个constructor
指针指向构造函数
每个实例都有一个__proto__
指针指向原型
其实是一个道理,这里我们不难想到,将Child.prototype指向parent实例,就是利用原型实现的继承,而为了每个实例都拥有各自的colors和name,也就是基础属性,在Child的构造函数中call调用了Parent的构造函数,相当于每次实例化的时候都初始化一遍colors和name,而不是所有实例共享原型链中的colors和name。
继承的实质是利用构造函数的原型 = 某个构造函数的实例,以此来形成原型链。例如
// 定义父类function Parent() {}Parent.prototype.getName = ()=> { console.log('parent') }// 实例化父类let parent = new Parent()// 定义子类function Child() {} Child.prototype = parent // 实例化子类let child = new Child() child.getName() // parent// 此时 child.constructor === parent.constructor === Parent
a.最经典的继承模式
function Parent(name) {this.name = namethis.colors = ['red'] } Parent.prototype.getName = function() {console.log(this.name) }// 实例化父类let parent = new Parent()function Child(age, name) { Parent.call(this, name)this.age = age } Child.prototype = parent // 实例化子类let child = new Child(1, 'aaa') child.getName() // parent
这里会让我想到ES6中的class继承
class Parent { constructor(name) {this.name = namethis.colors = ['red'] } getName() { console.log(this.name) } }class Child extends Parent { constructor(age, name) {super(name) } } let child = new Child(1, 'aaa') child.getName() // parent
以上が一般的なパターンとプロトタイプ チェーンを理解するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。