对原型链的理解

50 阅读3分钟

对原型链的理解

最近每天的主要任务都是在准备面试题,经过上次卓动的面试感觉自己的基础还是很薄弱的,在看了 new 操作符的时候用到了对象原型这一概念后,我就觉得有必要先把原型链给弄明白来。

什么是原型?

原型链字面意思就是原型的链,那自然就离不开原型。何为原型?简单来说,原型是一个普通的对象,这个对象有一个 constructor 属性,指向该对象的构造函数,同时原型上可以有一些属性和方法。为了更好地理解什么是原型,这里先引入使用 class 实现继承,学过 Java 都可以直接理解,示例代码如下:

    class Person {
      constructor(name) {
        this.name = name;
      }      
    }
​
    class Student extends Person {
      constructor(name, age) {
        super(name); 
        this.age = age
      }
​
      study() {
        console.log(`${this.name}正在写代码`);
      }
    }
​
    let student = new Student('PandaGuo', 22); 
    console.log(student.name);  // PandaGuo
    student.study();  // PandaGuo正在写代码

如上述代码所示,Student 类继承了父类 Person ,最终 new 出来的 student 对象可以访问到 name 属性,也可以访问到 study() 方法,但是在控制台上打印出 student 对象却没有发现 study() 方法:

image.png

继续展开下面的 [[Prototype]]其实就可以找到相应的 study() 方法:

image.png

这是因为 studnet 对象中如果找不到相应的 study() 方法,那么它就会往它自身的 _proto_ 中寻找相应的方法。这里打印出 student._proto_可以发现结果是一个对象:

image.png

而我们再试着打印出 Student 类中的 prototype 可以发现也是一个与之相同的对象:

image.png

继续在控制台上对二者进行比较,发现返回值为 true,说明二者指向的是同一个对象:

image.png

这时候我们可以根据上述的结果画出这样一张图:

image.png

由图可知,每一个对象都有一个原型 _proto_,一般叫做隐式原型,这个隐式原型会指向构建该对象的类的显示原型,也就是prototype,当对象查找不到需要的属性或方法的时候,会在它自身的隐式原型 _proto_ 上继续查找。

什么是原型链?

像刚刚上图所示,如果我在 student 对象中需要找到一个 play() 方法,而这个 play() 方法一开始是定义在 Person 类当中,那么 student 就会在它原型的原型当中去找这个类,具体图例如下所示:

image.png

如果 student 对象又调用了某个属性或方法且不存在于 Person 类当中,那么和上面一样,以此类推,又会到 Person 的原型当中去寻找,这种搜寻轨迹形似一条长链,且 prototype 在这条链当中充当了连接的作用,所以我们将这种实例与原型链条命名为原型链。如果找不到对应的属性或方法,实例对象会一直在这条原型链中继续往下寻找,直到找到或者到达顶端位置,而原型链的顶端是 Object.prototype 对象,对应的值是 null:

image.png

而如果需要判断某个对象或者方法是否为自身所拥有的,可以用 hasOwnProperty() 方法来检测,如果是,则返回 true, 否则返回 false

instanceof 运算符

instanceof 运算符可以用于检查一个对象是否是某个特定类型的实例。其具体的实现过程也涉及到了原型链,语法结构为:" A instanceof B ",在判断时会在原型链中递归查找 A._proto_ 是否等于 B.prototype,如果整条原型链都没有找到符合条件的类型则返回 false,相反则返回 true

instanceof 一般可以用来判断 ArrayObject,具体代码如下:

    let arr = [];
    let obj = {};
    console.log(typeof arr);  // object
    console.log(typeof obj);  // object
    /* 直接使用 typeof 判断 数组和对象不可行,二者的返回结果都是 Object *//* 而使用 instanceof 就可以区分二者 */
    console.log(arr instanceof Array);  // true(因为 arr 本身是数组,在 Array.prototype 中可以找到相应的实例)
    console.log(obj instanceof Array);  // false(obj 本身是对象,在 Array.prototype 中找不到相应的实例)

参考资料: 3原型链哔哩哔哩_bilibili