1.原型
什么是原型?
prototype
:显式原型,每一个构造函数都有一个prototype属性,它是一个普通对象,这个对象的方法和属性都会被构造函数所拥有__proto__
: 隐式原型, 每一个引用类型(数组、函数、对象)都有一个__proto__属性,指向构造函数的原型对象扩展
: 所有引用类型,它的’_ _ proto_ _'属性指向它的构造函数的’prototype’属性
原型的作用:数据共享
1.1原型的数据共享
在构造函数内部添加say方法
function Person(name) {
this.name = name
this.say = function() {
console.log('hello')
}
}
// 实例化
var p = new Person('小明')
var p1 = new Person('小红')
console.log(p.say === p1.say) //false
总结:不同实例上的同名方法是不相等的
实例两个相同业务的构造函数实在没有必要,通过把函数定义转移到构造函数外部来解决以上问题
function Person(name) {
this.name = name
this.say = say
}
function say() {
console.log('hello')
}
// 实例化
var p = new Person('小明')
var p1 = new Person('小红')
console.log(p.say === p1.say)//true
总结:通过把构造函数中say方法转移到外部,但是如果方法多了就要创建很多个全局函数。
在原型对象中添加say方法
// Person构造函数
function Person(name) {
this.name = name
}
// 在原型对象中添加say方法
Person.prototype.say = function() {
console.log('hello')
}
// 实例化
var p = new Person('小明')
var p1 = new Person('小红')
console.log(p.say === p1.say) //true
总结:p和p1的say方法指向同一块内存,实现了数据共享,和上面在构造函数内部及外部创建对象相比就在性能方面就要好的多
1.2查找机制
// Person构造函数
function Person(name) {
this.name = name
this.say = function() {
console.log('构造函数中say方法')
}
}
// 在原型对象中添加say方法
Person.prototype.say = function() {
console.log('原型对象中的say方法')
}
// 实例化
var p = new Person('小明')
p.say()
总结:在调用p.say方法时,首先会去实例对象上查找是否有这个方法,如果有即调用,如果没有则调用prototype对象中say方法。其实它的查找机制还远不止如此,这样我们可以引申出第二个概念----"原型链"
2.原型链
在2.2中可能有人会奇怪,为什么实例对象中如果没有,会去原型对象中找呢?
1. 首先我们知道实例对象中的__proto__是指向构造函数中prototype的,所以实例对象身上没有,才会去原型对象身上查找
这里又引申出来一个问题,如果原型对象身上也没有这个方法呢?
在上面原型的介绍中说了,prototype也是一个对象,而每一个对象中都__proto__对象,而所有对象中的__proto__都指向它的prototype,这样一层一层向上查找的的链式结构,被称为"原型链"
2.1 案列
// Person构造函数
function Person(name) {
this.name = name
this.say = function() {
console.log('构造函数中say方法')
}
}
// 实例化
var p = new Person('小明')
// 判断p实例对象是否有tostring方法
var res = p.toString() ? true : false
console.log(res)
这里我的实例对象和原型对象中都没有这个tostring方法,那这个方法是从那来的呢?
- 首先
Person
是构造函数,p
是实例对象,那么就有p.__proto__ === Person.prototype
- 我们上面说到过,
prototype
是一个普通对象,它内部也有__proto__
,并且指向Object.prototype
,那么就得到了这层关系----Person.prototype.__proto__ === Object.prototype
- 联系以上两点,我们知道了它会按照这个查找机制,直到为
null
为止
总结:当实例对象调用tostring方法时,发现在实例对象和原型对象上查找时并没有这个方法,于是通过Person.prototype查找,发现还是没有,再通过Person.prototype.__person__查找,最后在Object.prototype身上找到了这个tostring方法,之所以能按照这种机制找到tostring方法,就是因为原型链的存在