白话JavaScript原型链和继承
原型基础
- 每个函数都有一个prototype属性,指向函数的原型对象
- 每个对象都一个私有属性
__proto__
, 默认指向其构造函数的prototype - 在JS中所有函数都是
Function
构造出来的一种特殊对象,包括Function
本身;因此所有函数的__proto__
,指向Function.prototype
- 除函数外的所有对象都是由Object构造的,函数的原型对象也是;所以其
__proto__
指向Object.protptype。但Object这个函数的原型对象比较特殊,其__proto__
指向null
通过以上,我们可以推出,以下几个表达式的结果都是true
function Car () {}
c1 = new Car()
/** 对象的__proto__指向构造函数的prototype */
c1.__proto__ === Car.prototype
/** 函数的__proto__指向Function的prototype */
Car.__proto__ === Function.prototype
Object.__proto__ === Function.prototype
Function.__proto__ === Function.prototype
/** 原型对象的__proto__指向Object.prototype */
Car.prototype.__proto__ === Object.prototype
Function.prototype.__proto__ === Object.prototype
/** Object这个构造函数的原型对象,的__proto__指向null */
Object.prototype.__proto__ === null
__proto__
是一个非标准的,但是很多浏览器都实现了的属性,在新的语言标准中,用Object.get/setPrototypeOf()
代替对象的获取/赋值操作
由于现代的的JS引擎后优化属性访问,如更改对象原型,会拖慢新性能,所以通常不建议更改原型,而是直接在函数创建时,就定义好原型
Object.create(proto, propertyObject)
function Car() {}
Car.prototype.say = function () {console.log('hello')}
const customCar = Object.create(Car.prototype, {
name: { value: 'carname1', writable:true },
price: {
get: ()=> 10,
set: () => {}
}
})
原型链
当操作对象的属性时,如果在其自身找不到,就会去其原型对象 __proto__
找, 如果还没有找到,就会去原型对象的原型对象上找,直至找到,或者到达Object.prototype
这就是JS的原型链机制。
有了原型链机制,对象就可以直接访问原型链上的所有属性,因此在JS中对象的属性可以分为 自身属性和原型属性两种
继承
说白了继承就是,一个对象拥有(继承)了另一个对象的所有方法和属性,通过原型链机制,可以很方便在JS中实现继承
1、直接操作实例对象的 obj.__proto__
,指向另一个原型对象,支持非微软版本浏览器,IE11+,且只能设置对象为原型
2、直接操作构造函数的prototype,指向一个实例对象, 兼容性最好
3、用Object.create() 创建对象,创建时就指定好对象的原型: 兼容IE9+,且可以一次性处理好原型,有助于优化,还可以设置null
4、用set/getPrototypeof,支持IE9+,能够灵活的设置原型,还可以设置null