一、继承
对于有基于类的语言经验 (如 Java 或 C++) 的开发人员来说,JavaScript 有点令人困惑,因为它是动态的,并且本身不提供一个
class
实现。(在 ES2015/ES6 中引入了class
关键字,但只是语法糖,JavaScript 仍然是基于原型的)。
在继承中:JS只有一种结构:对象,每个对象有一个私有属性([[prototype]])浏览器中表示为__proto__ ,它指向原型对象(prototype)。同理,该对象又有一个私有属性([[prototype]])指向该对象的原型对象,通过这样一层一层的直到最后的原型对象为null,并成为原型链的最后一个环节。
我们通过一段代码来描述这个过程:
1 | let animal = {} |
二、继承的实现
1.Object.create(proto)
参数
proto
新创建对象的原型对象。
propertiesObject
可选。如果没有指定为
undefined
,则是要添加到新创建对象的可枚举属性(即其自身定义的属性,而不是其原型链上的枚举属性)对象的属性描述符以及相应的属性名称。这些属性对应Object.defineProperties()
的第二个参数。
返回值
在指定原型对象上添加新属性后的对象。
1 | let bird = {type: 'bird'} |
2.通过构造函数
先来看一个例子
1 | function person(name) { |
hbb是new出来的,继承于person,按道理来说,hbb的原型应该是person?但是person是个函数,不是对象。
1 | hbb.__proto__ === person.prototype // true |
这个prototype又是什么?
只有函数才有prototype属性
为什么?因为ES规范就这样。
当创建一个函数时,JS会自动为函数添加一个属性prototype
,这个属性的值是个对象,里面有一个constructor
属性,当你使用 new
构造一个对象时,会自动把新对象的__proto__指向它
原理如下:
1 | // 上面例子等同于: |
3.通过class(es6)
class其实就是构造函数的一个语法糖。只是让代码看起来更加OOP。。而已。
1 | function person(name) { |
那么,class的继承和构造函数有什么区别?1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28function A(name) {
this.name = name
}
function B(type, name) {
this.type = type
A.call(this, name)
}
let C = new B('name', 'type')
B.prototype.__proto__ === A.prototype // false
B.__proto__ === A // false
// B 和 A 之间的继承其实不是真正的继承,通过一个call的方法实现的伪继承,复制了一下属性而已。
class A {
constructor(name) {
this.name = name
}
}
class B extends A {
constructor(name, type) {
super(name)
this.type = type
}
}
let C = new B('name', 'type')
C.__proto__ === B.prototype // true
B.prototype.__proto__ === A.prototype // true
B.__proto__ === A // true
// B 和 A实现了继承
1 | // B 的实例继承 A 的实例 |
三、原型链
原型链就是继承实现中的一个类似链表的东西。
比如:
1 | function person(name) { |
1 | let bird = {type: 'bird'} |
1 | class A { |
js中是通过__proto__
指针来实现原型链的。
关键的地方在于:object没有prototype对象,而function有prototype对象。
创建原型链的过程中:__proto__
是指向父亲的prototype属性。