为什么要有原型对象
看到了阮老师的Javascript继承机制的设计思想,明白了当初设计原型对象的历史原因:
- 为了降低JavaScript的学习难度,所以不引入
类(class)
的概念
- 构造函数使用
new
运算符实例化对象后,并不能共享属性和方法,这一点不利于之后的继承
大概基于以上两点,Brendan Eich决定给构造函数设置一个prototype
属性。
原型对象
每一个构造函数都有一个prototype
的属性,这个属性指向的一个对象,而这个对象就是原型对象。对于每一个实例化的对象而言,可以通过__proto__
属性访问到其构造函数的prototype
对象。按自己理解来说:每一个Function
都有一个prototype
属性,而该属性指向的就是原型对象,而原型对象上的作为一个公共区域,实例化的对象就可以访问到其构造函数的原型上的属性和方法。
举例说明
1 2 3 4 5 6 7 8 9 10 11 12
| function Person(name,age){ this.name = name; this.age = age; this.sayName = function(){ console.log(this.name) } } let p1 = new Person("baiji",10) let p2 = new Person("baixiaoji",20) console.log( baiji.sayName === baixiaoji.sayName )
|
如果按照上述的代码,每一次实例化对象的时候,都要创建一个sayName的函数,如果要构造1000个Person的实例,那么内存一定会吃紧,所以prototype
属性就可以将构造函数的公共的属性和方法都放上去。改写上述的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| function Person(name,age){ this.name = name; this.age = age; } Person.prototype.sayName = function(){ console.log(this.name) } let p1 = new Person("baiji",10) let p2 = new Person("baixiaoji",20) console.log( p1.sayName === p2.sayName ) class Person{ constructor(name,age){ this.name = name this.age = age } sayName(){ console.log(this.name) } }
|
将公共的方法抽取出来,挂在prototype
上,可以供实例化的对象调用公共方法。
文字说明
Person
是一个构造函数,通过new
可以构造出Person
的一个实例对象(p1、p2
),每一个对象都会有一个__proto__
的属性指向构造函数的原型对象(Person.prototype
)。每一个函数都会一个prototype
的方法,指向一个对象;而每一个原型对象(Person.prototype
)都有一个constructor
指向这个函数本身(Person()
)。
大概关系如下图:
这也就可以解释了,为什么每一个对象上都有一个toString
的方法,见下图:
这样指向一层又一层的原型对象(会有终止),就是JavaScript的原型链,而我们可以基于原型链的特性,实现之后的继承
。
继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| function Person(name,age){ this.name = name; this.age = age; } Person.prototype.sayName = function(){ console.log(this.name) } function Worker(name,age,workYear){ Person.call(this,name,age) this.workYear = workYear } inherit(Person,Worker) Work.prototype.sayWork = function(){ console.log(this.work) } function inherit(superType, subType){ Object.create 创建一个拥有指定原型和若干指定属性的对象 有兼容问题 这个实现的好处:防止对子类的原型对象修改的时候,修改到了父类的原型对象上 */ let __prototype = Object.create(superType.prototype) __prototype.constructor = subType subType.prototype = __prototype } let p1 = new Work("baiji",10,"学生")
|
其实JavaScript中的继承是通过原型链
的方式,实现了继承父类的方法和属性。由此,就是原型拓展的全部,知道了上面这些知识点,我们就可以去根据面向对象的方式去封装一些组件。
总结
- 每一个构造函数都有一个
prototype
属性,指向其原型对象,一般将公共的方法和属性挂在上面
- 由new运算符实例化的对象,通过
__proto__
属性访问其构造函数的原型对象
- JavaScript通过
原型链
的方式实现继承