原型是JavaScript面向对象编程中非常重要的概念。今天就一次性battle明白~~~~走起~
开始之前先理解几个关键点
- 所有的引用类型(数组,函数,对象)可以自由扩展属性(除了null以外)
- 所有的引用类型都有一个
__proto__
属性(也叫隐式原型,是一个普通对象) - 所有的函数都有一个
prototype
属性(也叫显试原型,也是一个普通对象) - 所有的引用类型,
__proto__
属性都指向它的构造函数的prototype
属性 - 当试图得到一个对象的属性时,如果整个对象的本身不存在整个属性,那么它就会去找它的
__proto__
属性,也就是它的构造函数的prototype
属性中找。
要点说完了,根据要点理解
先上一张图,如果你看明白这张图了,基本就能解释出__proto__和prototype的关系和区别了。看不懂的继续走起~
先来一个铺垫再解释
构造函数
使用构造函数来创建一个对象
function Person(){
}
var person = new Person;
person.name = '张三'
console.log(person.name) //张三
Person就是一个构造函数,通过new创建了person对象实例。
其实构造函数和普通函数没有多大区别,首字母大写只是约定俗成的的事情。关键是调用它的方式——通过new,那么这里又有了新的问题,使用new调用之后内部会执行哪些操作呢?
进入正题分割线------------------------------------------
prototype
//Person是一个构造函数
funtion Person(){
this.age = 18;
this.showName = function(){
console.log ('I am' + this.name)
}
}
Person.prototype.name = '张三';
var person1 = new Person()
var person2 = new Person()
console.log(person1.name) //张三
console.log(person2.name) //张三
从这段代码不难看出,每一个函数都有一个prototype
属性,这个属性是一个指针,指向一个对象,只有函数才有。
如果我们要通过Person创建很多个对象,属性和方法都写在Person里面,那么我们通过实例对象创建的每一个对象里面都有showName方法,会占用很多资源。
prototype属性指向一个对象,那么这个对象是什么呢?
根据开篇第一张图可以清晰的看到,prototype
指向Person.prototype。没错Person.prototype就是原型对象,也就是person1和person2的原型
所以构造函数和原型之间的关系为
__ proto__
从开篇第一张图我们会看见,person1和person2实例对象下面有一个[[prototype]],其实没有标准的访问方式可以访问到它,但是主流浏览器在每个对象上都支持一个属性,就是__proto__,这个属性会指向该对象原型
function Person(){
}
var person = new Person()
console.log(person.__proto__ === Person.prototype) //true
so~~~我们得出总结:__proto__就是将对象与该对象的原型相连
在所有实现中都无法访问到[[prototype]],但是可以通过一些方法来确定对象之间是否存在这种关系
instanceof,这个操作符只能处理实例对象person和函数(带.prototype引用的Person)之间是否存在这种关系
person1 instaceof Person //true
isPrototypeOf,如果[[prototype]]指向调用此方法的对象,那么这个方法就会返回true
Person.prototype.isPrototypeOf(person1) // true
Person.prototype.isPrototypeOf(person2) // true
constructor
这个属性其实就是将原型对象指向关联的构造函数
function Person(){
}
Person.prototype.constructor === Person //true
再看下面的代码
function Person(){
}
var person = new Person()
person.constructor === Person //true
又有了新的疑惑,这不是实例对象person也有constructor属性了吗?其实是:通过原项链在原型Person.prototype上面找到的。
首先,fn的构造函数是Foo()。所以:
fn.__proto__
=== Foo.prototype
又因为Foo.prototype是一个普通的对象,它的构造函数是Object,所以:
Foo.prototype._ _ proto _ _=== Object.prototype
通过上面的代码,我们知道这个toString()方法是在Object.prototype里面的,当调用这个对象的本身并不存在的方法时,它会一层一层地往上去找,一直到null为止。
到这里差不多就能理解__proto__和prototype的关系和区别了。
整点相关的题外话----------------------------------------
原型链
他俩的关系我们探索完了,研究研究原型和原型链都是什么东东?
其实原型链就是依托__proto__和prototype连接起来的