原型链
核心:
- 普通对象:只有隐式原型
__proto__; - 函数对象:有隐式原型
__proto__和显式原型prototype。
注意点:隐式原型需通过Object.getPrototypeOf()获取,__proto__不是ES规范中的内容
Object.getPrototypeOf()
返回给定对象的原型(隐式原型)。如果没有继承属性,则返回null
// 例子:
let arr = [1, 2, 3]
Object.getPrototypeOf(Array) === Object.getPrototypeOf(arr) // false
Object.getPrototypeOf(Array) === Function.prototype // true
Array.prototype === Object.getPrototypeOf(arr) // true
继承
1.原型链继承
子类原型指向父类实例
// 伪代码:
function P() {
// ...
}
function C() {
// ...
}
C.prototype = new P()
// 例子:
function P() {
// ...
this.likes = [1, 2, 3]
this.nameP = 'parent'
}
function C() {
// ...
this.nameC = 'child'
}
C.prototype = new P()
C.prototype.constructor = C
const obj = new C()
obj.nameP = 'pp'
obj.likes.push(9) // 会修改obj原型上的likes属性
obj.likes = [9, 8, 7] // 会在obj自身对象上添加likes属性
console.log('obj', obj)
优点:继承了父类模版和原型上的属性
缺点:
- 无法多继承
- 来自原型对象的所有属性都被子类实例共享了
- 创建子类时无法向父类构造函数传参
注意:无法通过对象直接修改原型对象上的数据,但是可以修改原型对象上的引用类型属性的属性
2.构造继承
利用父类为子类添加属性
// 伪代码:
function P(...arg) {
// ...
}
function C() {
P.call(this, ...arg)
// ...
}
// 例子:
function C() {
P.call(this, ...arg)
// ...
}
// 例子:
function P(...arg) {
let [name, age] = arg
this.name = name
this.age = age
}
P.prototype.color = ['red', 'blue'] // 修改显式原型添加color
P.__proto__.num = [1, 2, 3] //给隐式原型添加num属性
function C(...arg) {
P.call(this, ...arg)
}
const obj = new C('zhangsan', 21)
console.log(obj)
console.log(obj.color) // undefined,无法继承父类原型上的属性。
console.log(P.color) // undefine,当读取函数对象的属性是,查找的原型是隐式原型
console.log(P.num) // [1, 2, 3]
优点:父类中的引用对象在子类中都是独立的,不被共享
缺点:
- 无法继承父类原型上的属性。
- 实父类的原型没有在实例的原型链上。
- 父类中相同的属性会被复制到每一个子类实例中。
3.组合继承
// 伪代码:
function P(...arg) {
// ...
}
function C() {
P.call(this, ...arg)
}
C.prototype === new P()
优点:
- 父类中的函数属性不被子类实例共享
- 子类实例可以共享父类原型中的属性
缺点:
子类实例上的属性会隐藏父类上的同名属性,父类中的属性会创建两份,内存浪费
父类构造函数调用两遍(
new P(),P.call())
4.寄生组合继承
// 伪代码:
function P(...arg) {
// ...
}
function C(...arg) {
P.call(this, ...arg)
// ...
}
C.prototype = Object.create(P.prototype)
优点:
- 只调用一次父类构造函数
- 子类实例可使用父类原型链上的属性和方法
5.原型式继承
// ES5之前:
function createObj(proto) {
function F() {}
F.prototype = proto
proto.constructor = F
return new F()
}
// ES5之后:
Object.create()
6.寄生式继承
// 封装原型式继承,来增强对象
function createObj(proto) {
// 寄生组合继承
const obj = Object.create(proto)
// 增强对象
obj.fn = function() {
//...
}
return obj
}
7.多继承
Object.assign()
// 将多个对象的属性拷贝(浅拷贝)到目标对象中,后面的属性会覆盖前面的同名属性
instanceof和isPrototypeOf
instanceof:
检测构造函数Fun的prototype属性是否出现在对象obj的原型链上
// 语法:obj instanceof Fun
// 例子:
const obj = [1, 2 , 3]
obj instanceof Array // true
obj instanceof Object // true
Object.prototype.isPrototypeOf:
对象obj1是否出现在对象obj2的原型链上
// 语法:obj1.isPrototypeOf(obj2)
// 例子:
const obj1 = [1, 2, 3]
Array.prototype.isPrototypeOf(obj1) // true
Object.prototype.isPrototypeOf(obj1) // true