Javavscript基础——原型和原型链

本文研究一下Javascript的核心基础——原型链和继承。javascript

对于使用过基于类的语言(如Java或C#)的人来讲,Javascript的继承有点难以搞懂,由于它自己没有class这种东西。(ES6中引入了class关键字,看上去也像传统的OOP语言,可是那只是语法糖,底层仍是基于原型)。java

原型链

MDN上对于原型链的解释:数组

当谈到继承时,JavaScript 只有一种结构:对象。每一个实例对象( object )都有一个私有属性(称之为 __proto__ )指向它的构造函数的原型对象( prototype )。该原型对象也有一个本身的原型对象( __proto__ ) ,层层向上直到一个对象的原型对象为 null。根据定义, null 没有原型,并做为这个 原型链中的最后一个环节。

几乎全部 JavaScript 中的对象都是位于原型链顶端的 Object 的实例。网络

这段话可能难以理解,咱们来举个例子:数据结构

const list = []; // 定义数组
list.__proto__ === Array.prototype; // true
list.__proto__.__proto__ === Object.prototype; // true
list.__proto__.__proto__.__proto__===null; // true
// 继承关系为
// list -> Array.prototype -> Object.prototype -> null

结合MDN的解释,咱们来解释一下上述例子:函数

list是Array的实例对象,使用了字面量的方式建立了对象实例this

每一个实例对象( object )都有一个私有属性(称之为 __proto__ )指向它的构造函数的原型对象( prototype )。
// list的构造函数是Array,因此list.__proto__指向构造函数Array的原型对象。
list.__proto__ === Array.prototype; // true
该原型对象也有一个本身的原型对象( __proto__ )
// Array.prototype也是对象,也有本身的原型对象,原型是Object.prototype
// 下面是数学运算(等量代换)
// list.__proto__ = Array.prototype
// Array.prototype.__proto__ = Object.prototype
list.__proto__.__proto__ === Object.prototype; // true
层层向上直到一个对象的原型对象为 null。根据定义, null 没有原型,并做为这个 原型链中的最后一个环节。
// 目前咱们来到了Object.prototype,根据规范,Object.prototype的原型对象为null
// list.__proto__ = Array.prototype
// Array.prototype.__proto__ = Object.prototype
// Object.prototype.__proto__ = null;
list.__proto__.__proto__.__proto__ === null; // true

原型链查找

当咱们访问对象的属性或者方法时,会先从对象自己开始查找,若是查找不到,则查找对象的 __proto__,层层向上查找,直到查找到属性,不然抛出错误。
const list = [];
list.toString();

属性查找过程以下:spa

  1. 查找list.toString()方法,没找到
  2. 继续查找list.__proto__,也就是Array.prototype,找到了
  3. 调用Array.prototype.toString

原型链结论

  1. 对象实例.__proto__ = 对象构造函数.prototype
  2. 几乎全部对象的原型都是Object.prototype
  3. null是对象,可是null没有原型
  4. 属性/方法查找采用优先返回机制。

函数

通过原型链的简单介绍,相信你们对原型和原型链有了一个比较直观的了解了,如今要说到的是函数。prototype

咱们知道,Javascript中函数也是对象,因此 Function.__proto__指向 Object.prototype

上面的结论在Javascript中是有问题的。咱们来聊一聊函数。code

先看看简单一点的例子,你们知道,Object是对象的构造函数构造函数也是函数,全部的函数的原型都是Function.prototype,因此Object.__proto__是等于Function.prototype的。

事实证实,也是如此。

image-20190923170248951

那么Function.__proto__为何不等于Object.prototype呢?Function不是对象吗?

Function确实是对象,同时仍是构造函数,能够经过new Function()来获得函数实例。

上面咱们说到全部函数的原型是Function.prototype,因此Function这个构造函数的原型__proto__等于Function.prototype

基于以上原理,还有如下相等关系:

  • Object.__proto__ === Function.prototype
  • Array.__proto__ === Function.prototype

引伸的问题

咱们知道Function.__proto__是指向Function.prototype,那个Function.prototype这个Function哪里来的?Function本身创造本身?那不是会死循环吗?

这个问题不是纯JS层面能解决的,牵涉到底层实现,下面是网络上别人整理的结论,有须要的能够研究一下V8的源码,这样能够完全解决这个问题。

  1. 用C/C++ 构造内部数据结构建立一个 OP 即(Object.prototype)以及初始化其内部属性但不包括行为。
  2. 用 C/C++ 构造内部数据结构建立一个 FP 即(Function.prototype)以及初始化其内部属性但不包括行为。
  3. 将 FP 的[[Prototype]]指向 OP。
  4. 用 C/C++ 构造内部数据结构建立各类内置引用类型。
  5. 将各内置引用类型的[[Prototype]]指向 FP。
  6. 将 Function 的 prototype 指向 FP。
  7. 将 Object 的 prototype 指向 OP。
  8. 用 Function 实例化出 OP,FP,以及 Object 的行为并挂载。
  9. 用 Object 实例化出除 Object 以及 Function 的其余内置引用类型的 prototype 属性对象。
  10. 用 Function 实例化出除Object 以及 Function 的其余内置引用类型的 prototype 属性对象的行为并挂载。
  11. 实例化内置对象 Math 以及 Grobal
  12. 至此,全部 内置类型构建完成。

函数结论

  1. 函数的原型都是Function.protype,构造函数也是函数,因此构造函数的原型也是Function.prototype

来自灵魂的拷问1

下面是一道有点难度的JS基础题,能够感觉一下:

function A() {
  
}

function B(a) {
  this.a = a;
}

function C(a) {
  if(a) {
    this.a = a;
  }
}

A.prototype.a = 1;
B.prototype.a = 1;
C.prototype.a = 1;
console.log(new A().a);
console.log(new B().a);
console.log(new C().a);

输出是

1
undefined
1

解释

  1. 为何输出1?

    由于new A()这个对象上没有属性a,因此去查找原型链,查到了F.prototype.a
  2. 为何输出undefined?

    由于new B时没有传递a,因此a是undefined,new B()这个对象是有a属性的,只不过值是undefined,因此不查原型链
  3. 为何输出1?

    由于new C()未传递a,因此a是undefined,因为if(a)的判断,new C()这个对象内部没有a属性,因此去查原型链

来自灵魂的拷问2

function F() {
  this.a = 1;
}
F.prototype.b = 2;

var f = new F();
console.log(f.hasOwnProperty('a'));
console.log(f.hasOwnProperty('b'));

输出是

true
false

解释

  1. 为何输出true`?
输出true比较好理解,由于构造函数 F声明了属性 a,因此 F的实例有 a属性
  1. 为何输出false?
b是 f的原型对象 F.prototype的属性,不是 b本身的,不能拿别人的说成本身的。

结尾

本文研究了原型和原型链之间的关系以及常见对象的原型和原型链,对于特殊对象Function也研究了一下,若是能搞懂后面两个问题,那本文对你来讲没什么问题了。

相关文章
相关标签/搜索