还搞不透JS原型链?一篇就够了,面试尽管问,再也不慌啦

768 阅读4分钟

原型链.png

原型链的知识一直是考察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'

一眼就能知道答案的,原型链的知识已经掌握了哦

其实原型链就是概念性的知识,本身并没有什么难度,就是理解起来有点绕。希望这篇文章能够帮助有需求的伙伴理清思绪。学海无涯,砥砺前行!