js原型与原型链

148 阅读4分钟

1. 引言

  • js原型与原型链是js的一大基础知识,在面试中高频出现,在平时的学习中也尤为重要。故而总结了此篇文章,如有错误,请评论。

2. 普通对象与函数对象

  • 在学习原型与原型链之前要先回顾一下普通对象与函数对象的概念,这对于我们理原型与原型链有至关重要的作用。

  • 首先,在js中万物皆为对象。但对象与对象也是有区别的,分为普通对象与函数对象。其中Object和Function是js自带的函数对象。代码示例说明。

    // Object和Function都属于对象(函数对象)
    console.log(Object instanceof Object); //true  
    console.log(Function instanceof Object); //true
    
    // 普通对象
    var o1 = {};
    var o2 = new Object();
    console.log(typeof o1); // object
    console.log(typeof o2); // object
    
    // 函数对象
    function fn1() {};
    var fn2 = function() {};
    var fn3 = new Function();
    console.log(typeof fn1); // function 
    console.log(typeof fn2); // function
    console.log(typeof fn3); // function
    

    在以上代码中可以看出o1,o2,o3都是普通对象,fn1,fn2,fn3都是函数对象。如何区分,其实很简单,只要是通过new Function()创建的都是函数对象,其他的都是普通对象。fn1,fn2归根结底也是由new Function()创建的。而 Function Object(相当于构造函数)也都是由new Function()创建的

    一定要分清 普通对象与函数对象。

3. 通过构造函数了解原型

  • 构造函数示例代码

    function Person(name, age) {
      this.name = name;
      this.age = age;
    } 
    var p1 = new Person('jack', 20);
    console.log(p1.name); // jack
    console.log(p1.age); //20
    console.log(p1.constructor == Person) // true
    

    以上代码,Person是构造函数,p1为构造函数的实例,p1有一个constructor属性,该属性(是一个指针)指向Person。

    由此总结出:实例的constructor属性指向构造函数

4. 原型对象

  • 在js中,每当定义一个对象的时候,对象中都会包含一些预定义的属性。其中每个函数对象都有一个prototype属性,这个属性指向函数的原型对象。使用原型对象的好处是所有对象实例都共享它的属性和方法。

    function Person() {
      Person.prototype.name = 'jack';
      Person.prototype.say = function () {
         alert(this.name + ' say ...');
      };
    }
    var p1 = new Person();
    var p2 = new Person();
    p1.say(); // jack say ...
    p2.say(); // jack say ...
    console.log(p1.say == p2.say) // true
    
  • 那什么是原型对象呢?

    将上面的例子修改一下:

    Person.prototype = {
        name: 'jack',
        say: function () {
           alert(this.name + ' say ...');   
        }
    }
    

    原型对象就是一个普通的对象,即Person.prototype

  • 在上面,为原型对象Person.prototype添加了name,say两个属性,它还有一个默认属性:constructor

    在默认情况下,所有的原型对象都会自动获得一个consrtuctor(构造函数)属性,这个属性指向(是一个指针)指向 prototype 属性所在的函数(Person)

    用代码解释上面的话:

    console.log(Person.prototype.consrtuctor == Person) // true
    

    故而得出结论:原型的constructor属性指向其构造函数

  • 我们也可以这样理解原型对象:原型对象是构造函数的一个实例,构造函数先创建了一个实例,并将它赋值给了自己的prototype属性,之后的实例创建就以它为基准。用代码来展示:

    // 此代码只作为理解,真实不能运行
    var new = new Person();
    Person.prototype = new;
    //  那么
    console.log(new.constructor == Person) // true
    console.log(Person.prototype.constructor == Person) // true
    

    这样我们也能解释通为什么原型的constructor属性指向构造函数了。

5. 什么是__proto__?

  • js在创建对象时(不论是普通对象还是函数对象)都有一个__proto__的内置属性,用于指向创建它构造函数的原型p.__proto__ == Person.prototype

    要注意的是:关系链是直接存在于实例与原型之间,而不是与构造函数之间。

6. 原型链

  • 简单的来说:原型链就是构造函数,原型与实例三者之间的关系链

    我们用代码描述:

    // 构造函数
    function Person () {}
    // 实例
    var p = new Person();
    // 对象的__proto__属性指向创建它的构造函数的原型,以此来查找原型链出口
    console.log(p.__proto__ === Person.prototype);
    console.log(Person.__proto__ === Function.prototype);
    console.log(Function.__proto__ === Function.prototype);
    // 这样会出现循环,故而寻找Function.prototype的原型
    // 上文说过原型对象就是一个普通的对象,故而可以将Function.prototype看作一个object
    console.log(Function.prototype.__proto__ === Object.prototype);
    console.log(Object.__proto__ === Function.prototype);
    // 再找下去又会进入循环,故输出Object.prototype的原型
    console.log(Object.prototype.__proto__ ); // null
    // 此时就为原型链的出口 null
    
    

    如果还不了解,那就参考下方图片,写写代码,相信你一定会的

    原型链
    原型链

6. 总结

  • 原型和原型链是JS实现继承的一种模型
  • 原型链的形成是真正是靠__proto__ 而非prototype

以上就是小编对于原型与原型链的理解了,如有错误,请各位指正,感谢!