如何理解JavaScript中的原型和原型链

本文基于理解基础知识进行的拓展,并不会包含太多的基础知识。

首先是一张关系图,避免抽象化理解时产生的困难
prototype_20180924213826847264.jpg

Function对象

函数对象是JavaScript学习中不可避免的一部分,而且这一部分相对重要且抽象
函数的创建方式有2种:

  • 字面量创建
var foo = function(){
    console.log("test");
}
  • new 关键字创建实例对象
//let 函数名 = new Function(“参数列表”,”函数体”);
let sum = new Function("num1,num2","return num1+num2");

我们多数在使用new关键字的时候,是用于创建实例对象

那么我们首先来看一下,在使用new关键字创建实例对象的时候,都经历了什么:

  • 创建一个新对象
  • 链接到原型对象,继承属性和方法
  • 将构造函数的作用域赋给新对象(this指向改变到实例对象中)
  • 返回新对象

这个时候我们的关注点 原型对象 就出现了:

原型对象prototype

  • prototype是一个显式原型属性(也可以叫它原型对象),只有函数才有该属性,通常我们叫这个时候的函数为"构造函数"
  • prototype的伴随构造函数的声明就会被自动创建
  • 原型对象prototype只有一个属性:constructor

代码举例:

function Student(name,age){
this.name = name;
this.age = age;
}
let s1 = new Student("Tom",17);

首先我们创建了一个构造函数Student,此时Student的结构中会出现一个prototype属性,即原型对象,这是引擎自动给它的,我们可以直接进行使用
Student_20180924221654485528.png

  • 实例对象prototype中的constructor属性:
    constructor_20180924221916814233.png

此时可以看出constructor对应的是构造函数,也就是Student

并且这是一个公有不可枚举属性,一旦改变了prototype,这个属性就会不见,当然可以再手动添加回去

而当我们再使用new关键字创建实例对象s1之后,我们来看一下s1的结构:

s1_20180924222333446126.png

实例对象s1中除了在Student获得的age,name属性之外,还有一个__proto__属性,所以它又是什么东西呢?

__proto__是什么

__proto__是每个对象都有的隐式原型属性,指向了创建该对象的构造函数的原型对象prototype,但是 prototype是内部私有属性,我们并不能访问到,所以使用__proto__进行访问

至于__proto__是如何产生的,上面的new关键字创建函数的时候的第三部"链接到原型,继承属性和方法"的时候就让实例对象,例如s1拥有了__proto__属性

从实例对象s1__proto__指向构造函数Studentprototype,构成了原型链

通过原型链的概念,我们就不难理解实例对象是如何继承构造函数中原型对象的属性和方法了

function Student(name,age){
    this.name = name;
    this.age = age;
}
Student.prototype.method = function(){
    console.log("我的名字是"+this.name+",我的年龄是"+this.age);
}
let s1 = new Student("Tom",17);
s1.method();    //我的名字是Tom,我的年龄是17

深思考:底层的函数是如何创建出来的

从第一张关系图中以及后面的说明,我们可以知道Function.__proto__ === Function.prototype是成立的,并且Function.prototypeFunction.__proto__都指向Function.prototype
那么Function到底是谁创建出来的呢?是它自己创建了自己吗?

并不是

在解释这个问题前,我们要先说一下Object
JavaScript中所有对象都可以通过原型链一层一层的最终找到 Object.prototype(原型链顶端),虽然 Object.prototype 也是一个对象,但是这个对象却不是 Object 创造的,而是JavaScript引擎自己创建了 Object.prototype

所以可以推论出 所有实例都是对象,但是对象不一定都是实例

紧接着再说回Function的创建问题

在控制台打印一下FunctionFunction.prototype
Function-prototype_20180925003617520584.png

在JavaScript引擎首先创建了 Object.prototype之后 ,又创建了 Function.prototype,并且通过 __proto__ 将两者联系了起来

既然Function.prototype 是JavaScript引擎创建出来的对象,所以就没有"到底是先有鸡还是先有蛋"这样的悖论问题出现

同样可以推导出:并不是所有的实例对象(函数)都是通过 new Function 方式创建出来的

这是一种比较严谨的说法,因为在通过字面量创建函数的时候就不会调用 函数构造器,也就是说它并不是new出来的,也就不存在这样的情况

至于说为什么Function.__proto__ === Function.prototype,有种说法是为了不产生混乱,就把二者链接在了一起

在JavaScript的函数和对象的学习中,剖析其中关系网时,真的会让人上瘾,想要一层一层的去搞清楚弄明白,而且只有在理清了其中每一层的对应关系时才会对它们有一个更加清晰的认知,在使用的时候也会更加的自信,解决开发问题的时候更加的灵活和优雅 so, let's hacking

本文链接:

http://godrry.com/archives/how-to-understand-the-prototype-and-prototype-chain.html
1 + 9 =
沙发还热乎呢~