什么是原型
原型是 JavaScript 中每个对象都有的一个内置属性,它指向另一个对象。这个被指向的对象就称为该对象的"原型"。
- 关键特性
-
共享机制:原型上的属性和方法可以被所有实例共享
-
备用存储:当对象自身没有某个属性时,会去原型上找
-
函数特有:只有函数对象才有 prototype 属性(用作构造函数时)
什么是原型链
原型链是由多个原型对象通过 [[Prototype]] 链接形成的链式结构。当查找一个属性时,JavaScript 会沿着这条链一直向上查找。
为什么要有原型链
1. 减少内存浪费
- 没有原型链的情况
function Person(name) {
this.name = name
this.sayName = () => {
console.log(this.name)
}
}
let alice = new Person('Alice')
let bob = new Person('Bob')
console.log(alice.sayName === bob.sayName) // 不相等
- 有原型链的情况
function Person(name) {
this.name = name
}
Person.prototype.sayName = () => {
connsole.log(this.name)
}
let alice = new Person('Alice')
let bob = new Person('Bob')
console.log(alice.sayName === bob.sayName) // 相等
2. 代码复用/继承问题
没有原型链:只能通过复制属性实现继承(低效且容易出错) 有原型链:自然形成继承链,方法和属性自动复用
// 传统继承方式(繁琐)
function extend(child, parent) {
for (var key in parent) {
if (parent.hasOwnProperty(key)) {
child[key] = parent[key]
}
}
}
// 原型链继承(简洁优雅)
function Animal() {}
Animal.prototype.eat = function () {}
function Dog() {}
Dog.prototype = Object.create(Animal.prototype)
// Dog 自动获得 eat 方法
注意这里的extend和类的extend是不一样的,而es6之后的类是原型链的语法糖,也就是说es6的extend是基于原型链实现的
为什么不建议用__proto__
| 原因 | 说明 |
|---|---|
| 非标准 & 已弃用 | 规范不推荐,未来可能移除 |
| 性能杀手 | 破坏引擎优化,导致严重 slowdown |
| 安全隐患 | 可能引发原型污染漏洞 |
| 语义模糊 | 易与 prototype 混淆,降低代码可读性 |
特别的
所有原型链的终点都指向于Object.prototype,而他的上一层是null