白话JavaScript原型链和继承

原型基础

  1. 每个函数都有一个prototype属性,指向函数的原型对象
  2. 每个对象都一个私有属性 __proto__, 默认指向其构造函数的prototype
  3. 在JS中所有函数都是Function构造出来的一种特殊对象,包括Function本身;因此所有函数的__proto__,指向Function.prototype
  4. 除函数外的所有对象都是由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

posted @ 2021-08-07 11:00  Claiyre  阅读(84)  评论(0编辑  收藏  举报