认识javascript中的原型和原型链
原型和原型链是javascript中非常重要的概念,下面让我们来认识一下这两个熟悉的陌生人。
原型
概念:无论何时,只要创建一个函数,就会按照规则为这个函数创建一个prototype属性,prototype属性指向的对象也就是原型对象。每次调用这个函数创建一个新实例,这个实例内部指针([[prototype]])就会被赋值为这个函数的原型对象,由于javascript脚本中没有设置可以访问[[prototype]]特性的标准模式,所以Firefox,Safari和Choreme会在每个对象上暴露一个__proto__属性,通过这个属性就可以访问到对象的原型。
作用:通过对象的原型,我们可以获取到这个对象有哪些属性,并在实例化对象时,直接拥有这些属性。
下面,我们同样通过例子来理解原型的概念。
例子中,我们定义了构造函数Person,并给Person的原型对象上设置了三个属性。同时也通过Person函数构建了两个实例person和person2。
例子的输出结果都是true,所以我们可以看出,实例化的对象的__proto__属性是指向构造函数的prototype属性,所以这person.__proto__ 和 Person.prototype是全等的。
同时为了方便理解这个例子,我们可以画一个图来理解prototype和__proto__之间的关系。
绘画能力有限,不过应该还是能够看懂。通过这个图我们能清晰的看到,不管是构造函数的prototype属性还是实例的__proto__属性都是指向构造函数的原型对象。
现在我们了解了原型,那原型的作用是什么,这种规则有什么用,现在我们也来探索一下。
通过上面的例子,我们可以看到,实例对象person和person2都具有Person原型对象的属性。学习过面向对象思想的同学应该了解了,通过原型,我们可以将javascript 当做面向对象的语言使用,通过提炼公共属性与方法,实现代码优化。
同样,我们也通过一个例子来看看面向对象编程的优点。
function Person() { //构造函数 } Person.prototype.name = "张三"; Person.prototype.age = "18"; Person.prototype.sex = "man"; /* 原型方式 */ const person = new Person(); //实例1 const person2 = new Person(); //实例2 /* 原始方式 */ let person3 = { name: '王五', age: 18, sex: 'man' }; let person4 = { name: '周六', age: 18, sex: 'man' }
相信例子中两种创建对象方式的优劣都是显而易见的。接下来我们了解下原型链。
原型链
了解原型之后,原型链的理解相信就是水到渠成的了。
概念:每一个构造函数都有一个原型对象,该原型对象有个属性回指向构造函数,通过该构造函数实例化的对象也有一个指针指向原型。如果当这个原型是另一个构造函数的实例时,这个原型的__proto__属性将指向另一个函数的原型对象。这样实例和原型之间就构造了一条原型链。
作用:实现继承。
同样我们也来一个例子。
function a() { } a.prototype.a = 'a'; function b() { } b.prototype.__proto__ = a.prototype; b.prototype.b = 'b'; function c() { } c.prototype.__proto__ = b.prototype; c.prototype.c = 'c'; let obj = new c(); console.log({a: obj.a, b: obj.b, c: obj.c})
例子中,我们声明了三个函数,并分别给三个函数的原型上添加了三个不同的属性。我们通过将c函数的原型对象的原型设置成b函数的原型对象(让c函数的原型对象是b函数的实例),实现了c继承b的效果,同时将b函数的原型对象的原型设置成a的原型对象(让b函数的原型对象是a函数的实例),实现了b继承a。通过串联c,b,a的原型,形成了一条原型链。
所以,上面这段程序最后的输出值是{a: "a", b: "b", c: "c"},因为实例obj通过原型链拿到了a,b,c三个属性。
同样我们用一张图描述下上面三个函数的关系。
虽然图画的不怎么好,不过能看懂就行。相信通过这张图,你应该知道为什么叫原型链了吧。
总结:原型和原型链主要用于面向对象编程,实现类的封装与继承,是我们开发中不可获取的一部分。对他的使用要不断深研、探索。
浅陋见识,不足之处,请大神指正。