原型和原型链,到底应该怎么理解?

in 前端 with 0 comment

原型和原型链在 JavaScript 里是一个非常重要的概念和应用。在前端的面试中一般情况下也是必考题目
那么怎么才能非常准确的理解原型和原型链呢?

原型

想要解释原型,得先知道两个属性
首先,每一个函数都有一个 prototype 属性,称作“原型”,属性值的数据类型是 Object
当解释原型时,通常指的是一个引用类型的变量的原型
引用类型有一个 __proto__ 的属性,称作隐式原型,属性值的数据类型是 Object

const arr = [1, 2, 3, 4]
console.log(arr.__proto__)

result

引用类型的 __proto__ 属性指向的是其构造函数的 prototype 属性
这个例子可以看出,arr 的构造函数是 Array()
一个数组可以直接声明,也可以这样声明:

conat arr = new Array()

所以

arr.__proto__ === Array.prototype // true

原型链

const obj = {
  test: 'fine'
}

当访问一个 对象 类型变量的属性时,会首先在这个对象上寻找

console.log(obj.test) // fine

如果对象上没有这个属性,则会到他的隐式原型 __proto__ 上(也就是它的构造函数的 prototype)找

console.log(obj.hasOwnProperty('test')) // true

如果还没有找到,就会一直查找其原型的原型,最后会查找到 null 上
原型链
这种一层层向上查找的链条,就是原型链

原型链编程

有时候我们会给某一种数据类型的原型上添加一些属性或者方法,来进行一些操作,例如:

String.prototype.repeat = function (count) {
  let str = ''
  for (let i = 0; i < count; i += 1) {
    str += this
  }
  return str
}
console.log('hi'.repeat(3))

有时候我们会自己定义构造函数
例如构造函数 Cat 构造猫,javis 是我家养的一只猫,是 Cat 构造出来的,所以要 new 一个 Cat 对象
Cat 构造函数可以传入 namewalkjavis 被声明以后传入了一个 gender 属性
所以一下控制台输出分别演示了 javis 这个对象的属性、原型和原型链

function Cat (name) {
  this.name = name
  this.walk = 'catwalk'
}

const javis = new Cat('javis')
javis.gender = 'male'

console.log(javis.gender) // male
console.log(javis.name) // javis
console.log(javis.walk) // catwalk
console.log(javis.hasOwnProperty('meow') // false
console.log(javis.meow) // undefined

总结

一个对象实体的隐式原型指向它的构造函数的显式原型
构造函数的隐式原型指向 Object 的显式原型
Object 的隐式原型指向 null
这就构成了原型链
可以在原型链上编程完成一些工作