深入理解 JS 原型
JS 中的原型链在面试中可以说是“必考题”
日常开发不常遇到,而且在 ES6 之后,原型链就更少见了
但是,如果设计框架或封装组件,可能就需要了解原型链
ES6 的类,可以认为是 ES5 的语法糖,因此本文主要以探究 ES5 为主
JS 创建对象与其他语言区别
以 C# 为例,C++/JAVA 等类似
C#
在 C# 中,类相当于一个模板,对象是模板创造的实例。代码编译后,类本身除了静态属性和静态函数,没有其他用处
JS
在 JS 中,ES5 及之前是没有类的概念,对象是通过构造函数创建的,即构造函数承载了类的功能,在构造函数中可以使用 this 为实例对象增加字段和函数,以及赋值的操作。这点对于有其他语言基础的人来说,思想较难转变
构造函数其本身也是对象,也可以当成一个普通函数来用
1 | function Func(){} // 构造函数 |
JS 通过构造函数创建对象,会有以下对象参与其中
- 构造函数:作为对象的构造函数
- 构造函数的原型对象:对象的对象原型将指向构造函数的原型对象
原型对象与对象原型
一般每个构造函数都有一个原型对象 Func.prototype
,简单函数的原型对象是 Object
对象
一般每个对象都有一个对象原型 obj.__proto__
,这个对象原型指向的是构造函数的原型对象,即
1 | function Func(){} // 构造函数 |
由于 JS 中调用对象方法时,先查找对象自身方法,再查找原型对象 __proto__
中的方法,因此对象可以使用对象原型中的方法
原型对象也可以置空,这样构造函数创建的对象将没有额外方法,如 toString
, hasOwnProperty
等
原型对象都有 constructor
字段,指向对应的构造函数
原型链
由前面得出结论,对象和对象原型 __proto__
形成了一个原型链,原型链的最顶端是 null
,形如
1 | obj.__proto__.__proto__.__proto__ == null // true |
上面可能有很多节点,也可能没有节点,取决于创建方法
类每多一次继承就会多一个节点,ES5 中的写法是给构造函数的 prototype
赋值并修改 prototype.constructor
1 | function Father() {} // 父类构造函数 |
下面写法会创建一个没有原型链的顶层对象,一般不会用到
1 | const obj = Object.create(null, {}) |
每个对象,都可以使用原型链中的任意方法,因为调用方法时会按原型链逐级向上查找
ES6 类和对象
ES6 之前通过 构造函数 + 原型
实现面向对象编程
ES6 通过 类
实现面向对象编程
类的本质也是函数,也可以简单的认为,类就是 ES5 构造函数的简单写法