JS 原型链详解

280 阅读4分钟

Js 原型链的详细解释

首先知道三个名词:实例对象构造函数原型对象

先说构造函数,任何我们写的一个方法都可以被称为构造函数。

function a(){ 
  return 1;
}

上面的已经算是构造函数了,但是和我们常用的构造函数有点区别,下面这个是我们常用的构造函数。这里有一个 new 的概念,当我们new 一个构造函数时,构造函数是否有返回值,以及返回值的类型都会不一样。这里先预留一下new的位置。

function a1(){ 
  this.a1=1;
}

再说实例对象,实例对象就是 通过构造函数funchtion a() 实例化,或者new 出来的一个变量,被称为实例对象。

let b = new a();

再说原型对象,什么是原型对象?js 没有类的概念。像java 中的class,当 我们new 一个java类的时候,我们new 的是这个类的构造函数。这个java 类有一个默认的或者重写的构造函数。所以,js 的原型对象和java 的这个类差不多。java 类的构造函数相当于js 的构造函数。所以原型对象就是包含构造函数的这个对象。如果构造函数需要访问原型对象,提供了这样的一个属性fn.prototype。可以让我们通过构造函数来访问。 如果还是很模糊,那么接着看。

那我们先来看看es6 。在es6 中,已经有了class 和constructor 的概念。这个过程已经和java 很像了。class 就是等同于原型对象这一层次了,因为class包含着的构造函数的整个结构和原型对象非常相像

function Person(name,age) {
    this.name = name;
    this.age=age;
}
Person.prototype.say = function(){
    return "我的名字叫" + this.name+"今年"+this.age+"岁了";
}
var obj=new Person("小明",88);



上面是es5 的写法。此时的原型对象 Person.prototype === obj.proto

我们这里可以看到 es6 的class类和原型对象非常相似。Person.prototype 的整个结构和class 如出一辙,同样的contructor,同样的say 方法。

可是下面的结论表象上推翻,定义了一个es6的class。

class Person1{//定义了一个名字为Person的类
    constructor(name,age){//constructor是一个构造方法,用来接收参数
        this.name = name;//this代表的是实例对象
        this.age=age;
    }
    say(){//这是一个类的方法,注意千万不要加上function
        return "我的名字叫" + this.name+"今年"+this.age+"岁了";
    }
}
var obj1=new Person1("小红",88);
console.log(obj1.say());

我们可以看到 class: Person1.prototype 才是真正的原型对象,并不是我们推想的class:Person1 就是原型对象。但是我们结合这java 想一下,java 中的构造函数可以重写。一个类可以有多个构造函数。但是js 中却不行。所以 我们这个时候new Person1 的时候,这个Person1,其实并不是真正的class 类,而是执行的class 对应的构造函数(constructor) 。所以Person1.prototype并不是指类:Person1的prototype,而是指这个类的构造函数的prototype。

到这里为止,我们就看出来了,原来的结论并不是错误的。es6 class的整个结构(通过对比我们也看的出来,class的结构和prototype 很相像),其实就是原型对象。这个就是对这三个定义的解释。

到此为止,这是对这个名词的解释。基本上就能理解对应的是什么东西了。接下来再加一点辅料。

  1. 实例对象怎么找到它的原型对象呢?
  2. 实例对象怎么找到它的构造函数呢?

我们前面提到了 构造函数通过 Person.prototy 找到原型对象的。 那通过构造函数实例化出来的实例对象怎么找到原型对象呢?js 中提供了一个属性,但凡是对象类型(typeof Person1,typeof obj1),都有一个__proto__ ,实例对象可以通过它来找到对应的原型对象。obj1.__proto__。既然已经找到对应的原型对象了,那么实例化对象的构造函数则是obj1.__proto__.constructor 。这样就把整个js 串起来了

刚才提到了对象类型,都有一个__proto__ 属性。构造函数(typeof constructor) 也是对象,所以它也有这个属性。prototype是构造函数所特有的。

  1. 可以看到__proto__属性都是由一个对象指向一个对象,即指向它们的原型对象(也可以理解为父对象),那么这个属性的作用是什么呢?它的作用就是当访问一个对象的属性时,如果该对象内部不存在这个属性,那么就会去它的__proto__属性所指向的那个对象(可以理解为父对象)里找,如果父对象也不存在这个属性,则继续往父对象的__proto__属性所指向的那个对象(可以理解为爷爷对象)里找,如果还没找到,则继续往上找…直到原型链顶端null,这就是原型链。
  2. 我们平时调用的字符串方法、数组方法、对象方法、函数方法等都是靠__proto__继承而来的