说说JavaScript的原型和继承

526 阅读3分钟

关于JS的原型与继承相关概念

在ES6后,JavaScript已经可以通过class、extends等语法糖实现面向对象的写法,但是以前的原型对象也是必须得了解的,看文章的话记不清楚,自己总结一下。

构造函数

在说原型前,有必要了解一下以前的面向对象的写法,JavaScript的对象是通过构造函数实例获得的:

function Fo(msg) {
  this.msg = msg
}

Person就是一个构造函数,一般首字母大写。并且有两个形参:namesex,赋值给了this对象。

此时,可以通过new的操作获取一个Person的实例:

const f = new Foo('Hello')
console.log(f.msg)  // Hello

这里有一个问题,为什么通过man这个实例对象能访问到msg呢,这里就要说一下new的操作了,其实在new实例化构造函数时,内部做了以下操作:

var obj = {}
obj.__proto__ = Person.prototype
Person.call(obj)
return obj

上面看到了两个奇怪的东西,就是__proto__prototype,那这两个又是啥呢?

原型对象和原型链

__proto__:每个实例对象都有这一个属性,与构造函数的原型函数对应;

prototype:原型对象,每一个构造函数都有这一个对象。

从下面这张图,我们可以看到两者的关系:

src=http___image.bubuko.com_info_201810_20181030000300294146.png&refer=http___image.bubuko.jpg

从图中可以看到,实例对象的__proto__是指向其构造函数的prototype的,并且有JavaScript在使用属性或方法时有一个特点就是,在当前实例对象寻找需要调用的属性或方法,如果没有,则向实例对象的proto**(也就是构造函数的prototype对象上找),直到找到或者到顶层的null为止。**

并且从代码中,可以看到是完全相等的:

console.log(f.__proto__ === Fo.prototype) // true

f.__proto__ => Fo.prototype => Fo.prototype.__proto__ => Object.prototype => null,这就形成了一条链式的结构,这就是所谓的原型链

继承

JavaScript的继承分为两种情况,第一种是继承属性,可以通过call方法来实现,第二种就是方法的继承,需要通过原型链来实现,看一下代码:

function Foo(msg) {
  this.msg = msg
}

Foo.prototype.printMsg = function () {
  console.log(this.msg)
}

function Fo(msg, greet) {
  Foo.call(this, msg)
  this.greet = greet
}

Fo.prototype = new Foo()
Fo.prototype.constructor = Fo

Fo.prototype.printGreet = function () {
  console.log(this.greet)
}

const fo = new Fo('Hello', 'How are you?')
console.log(fo)

打印这个fo,可以看到下图:

7B@MK{E)KPVE95HFD55LU4B.png
明显的,greet属性和msg属性就是属于Fo实例对象的,然后看看printMsgprintGreet这两个方法:

  1. printGreet此时是在fo__proto__上的,即Fo.prototype上;
  2. 又因为Fo.prototype又指向了Foo的实例对象,所以Fo.prototype其实是指向Foo的实例对象的;
  3. 最后可以看到printMsg是挂在Foo.prototype原型对象上的。 所以,可以看出我们访问fo对象的属性时,本身就在实例对象上,可以直接访问到;
    而当访问方法时,是通过访问fo.__proto__Fo.prototypeFoo实例对象上找的,又根据原型链的特点,访问不到对应方法时,会往Foo实例对象的__proto__属性即Foo.prototype上找,所以就实现了方法的继承。

总结

其实,JavaScript的对象是通过new构造函数得到的,new的过程内部已经帮我们处理过了;
继承是通过原型链的特点来实现的,把“子类”的prototype指向“父类”的实例对象,这样通过原型链查找时,自然会找从子类找向父类。