JavaScript系列之原型(链)

89 阅读4分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

什么是原型

原型对象

  • 在JS中,构造函数有一个prototype属性,该属性指向另一个对象;这个对象的所有属性和方法,都会被构造函数拥有;
  • prototype属性里面的所有属性和方法都是公共的,都会被构造函数所生成的所有实例继承;这就意味着,我们可以把公共的属性和方法,直接定义在prototype属性上面;
function Person(){}
Person.prototype.sayHi = function() {}
// 实例化对象
let d1 = new Person()
d1.sayHi()
let d2 = new Person()
d2.sayHi()
  • 将公用的方法定义在原型对象上面,实例化对象时不会开辟新的空间,有利于性能的提升;
  • 可以通过原型对象,对原来的内置对象进行自定义的扩展:
Array.prototype.sum = function(){ // code }

对象原型

  • 通过构造函数实例化的对象上面默认有一个__proto__属性,指向构造函数的原型对象;
  • d1.proto === Person.prototype;
  • 正是有对象原型的存在,当在对象自身找不到对应的属性和方法时,会到原型对象prototype上面去找;

constructor属性

  • 原型对象和对象原型上都有constructor属性,该属性指向构造函数本身;
  • constructor属性主要是记录该对象引用自哪个构造函数,它可以让原型对象重新指回原来的构造函数,有如下的情形:
function Star () {
  this.name = 'demo'
}
console.log(Star.prototype.constructor === Star) // true
Star.prototype = {
  name: 'john'
}
console.log(Star.prototype.constructor === Object) // true

// 这里prototype被Object构造函数的实例覆盖,所以constructor属性指向Object构造函数
function Moon () {
  this.name = 'David'
}
let currentMoon = new Moon()
Star.prototype = currentMoon
console.log(Star.prototype.constructor === Moon) // true

// 这里currentMoon 是Moon构造函数的实例,当prototype直接被该对象覆盖,constructor属性指向了Moon构造函数
  • 为了解决这样的问题,我们将constructor属性指向原来的构造函数即可:
Star.prototype.constructor = Star
console.log(Star.prototype.constructor === Moon) // false

构造函数,实例,原型对象之间的关系

  • 为了更加清晰地展示它们之间的关系,画图无疑是更直接的方式(以Star构造函数为例);

image.png

原型链

  • 有了上面对原型的了解,顺势就能引出原型链的存在方式,我们还是以Star构造函数为例:

    • 首先Star构造函数实例对象的__proto__指向Star的prototype属性;
    • prototype属性也是一个实例对象,它是由Object构造函数实例而来,所以它的__proto__属性指向Object.prototype;
    • 最后Object.prototype.proto 指向null,此时也标志这原型链的结束;
    • 当然图解的方式还是最清晰的:

image.png

  • 正是因为原型链的存在,当我们想要查找属性和方法时,在当前对象找不到时,就沿着原型链寻找,如果找到,那么就直接返回;否则就一直找下去,直到到原型链的末尾null才会结束;

  • 在实际的开发中,我们不会直接用到对象原型__proto__,它存在的意义就是将原型对象进行链接,进而形成一个链条,或者说形成一条路线;

  • 有了原型链的存在,我们可以做些什么?

    • 我们可以将公用的属性和方法定义在原型对象上,由于原型链的存在,实例对象都可以使用这些属性和方法;
    • 利用原型链可以实现子类继承父类的属性和方法;(原型链继承)

具体分析Function和Object之间的关系

  • js中一切皆是对象,函数也是对象的一种,函数对象;
  • 函数对象都是由Function构造函数生成的实例:
function fn() {}
console.log(fn.**proto** === Function.prototype) // true
  • Object构造函数也是函数,也是由Function生成的实例:
console.log(Object.**proto** === Function.prototype) // true
  • Function本身也是函数,也是由Function生成的实例:
console.log(Function.**proto** === Function.prototype) // true
  • 所有对象都是Object的实例,包括Function.prototype也是一个普通的对象:
console.log(Function.prototype.**proto** === Object.prototype) // true
  • 下面做一个简单的总结:

    • 所有的对象都是继承自Object对象,Object对象直接继承根对象null;
    • 一切函数对象,包括构造器函数,都是继承自Function对象;

本文可作为学习笔记,如果觉得有帮助,请多多点赞加评论!