原型链的知识一直是考察JS基础的一大考点,虽然在工作开发中不会用到很多,但是掌握这一块的知识,对于理解js的框架设计,帮助我们阅读源码都有很大的作用。尤其在面试阶段,原型链相关的问题也会考察很多。所以,彻底理解它,是每个前端程序员的必备技能。
1. 原型链的概念
文章开头的那张图,就是一张原型链的图形展示。任何函数都可以理解为构造函数,当我们声明一个函数的时候,js引擎会给这个函数加上一个prototype
属性,prototype
属性就指向了该构造函数的原型对象。当我们通过new
运算符生成构造函数的实例对象后,该实例对象会有一个__proto__
属性用来指向生成该实例对象的构造函数的原型对象。原型对象也是一个对象,它内部也有一个__proto__
属性,指向构造该对象的构造函数的原型对象,以此类推......直至原型链的顶端Object.prototype
.其实,原型链就是用__proto__
属性链接起来的原型对象的链接关系。当我们访问一个对象的属性或方法时,我们会优先在该对象内部寻找,如若没有,会去该对象的原型对象上寻找,还没有,我们就逐层往上级的原型对象上查找,若有,就直接返回,否则一直寻找直至顶级。
怎么样?是不是觉得原型链也没有什么高深的东西。脑中构思原型链的指向图,不管面试怎么问,都能轻松应对啦!
2. 创建对象的几种方式
理解了什么是原型链,我们再来补充一下创建对象的方式,通过这几种方式来帮助我们巩固一下对原型链的理解
- 字面量的方式: 这种方式本质上就是生成Object()构造函数的实例对象,原型对象就是Object构造函数的原型对象
var obj = {
name:"author"
}
//这种方式生成的结果和 new Object({name:"author"}) 一模一样
- 通过构造函数new创建
通过自定义构造函数new 生成的实例对象,原型对象指向自定义的构造函数的原型对象
function Buffer(){} var buffer = new Buffer()
- Object.create()的方式创建
object.create创建对象的方式,是根据参数传入的对象生成一个对象,该对象的原型对象就是传入的参数对象
var _prototype = { name:"author" } var obj = Object.create(_prototype)
3. new
运算符和 instanceof
运算符的原理
当我们通过new
运算符根据一个构造函数生成一个实例对象的时候,我们用实例对象 instanceof
构造函数,就会返回true,我们一般都会用这个方法来判断对象是否由某个构造函数创建的。那么他们的原理是什么呢?
new
function _new(fn){
if(typeof fn === 'function'){
var newObj = Object.create(fn.prototype);
var res = fn.call(newObj);
if(typeof res !== 'object' && typeof res !== 'function'){
return newObj
}else{
return res
}
}else{
throw new Error('the arguments must be function')
}
}
instanceof
我们先看一个例子,instanceof
到底是如何工作的:
function Buffer(){
}
var buffer = new Buffer();
console.log(buffer instanceof Buffer) // true
console.log(buffer instanceof Object) // true
instanceof
内部是根据实例对象的__proto__指向是否和构造函数的prototype指向一致,我们发现buffer
是通过Buffer
构造函数实例化出来的,所以buffer instanceof Buffer
结果为true可以理解,但是buffer instanceof Object
的结果也为true,由此我们可以得出,这里面的判断规则肯定不止判断一层这么简单,经过验证可得,instanceof
是可以判断整个原型链上的原型对象是否和构造函数的原型对象相等的。
function _insatnceof(obj,fn){
var obj_proto = obj.__proto__;
var target_proto = fn.prototype;
while(obj_proto){
if(obj_proto === target_proto){
return true
}
obj_proto = obj_proto.__proto__;
}
return false
}
最后我们再以一个题目验证一下自己是否完全掌握吧
Super.prototype.name = 'Super';
function Super(){}
new s1 = new Super();
console.log(sl.name); // 'Super'
Super.prototype = {
name:'suber'
}
console.log(sl.name); //'Super'
Super.prototype.name = 'Super';
function Super(){}
new s1 = new Super();
console.log(sl.name); // 'Super'
Super.prototype.name = 'Suber'
console.log(sl.name); //'Suber'
一眼就能知道答案的,原型链的知识已经掌握了哦