JavaScript如何实现继承?

JavaScript对象和继承

JavaScript在编程语言界是个异类,它和其他编程语言很不一样,JavaScript可以在运行的时候动态地改变某个变量的类型。

比如,你永远也没法想到像isTimeout这样一个变量可以存在多少类型;除了布尔值true和false,它还可能是undefined、1和0、一个时间戳、甚至一个对象。

除了变量可以在运行时被赋值为任何类型外,JavaScript也能实现继承,但它不像JAVA、C++等编程语言一样依靠类来实现继承,而已依靠原型来实现继承。

我们不禁想问为什么?因为JavaScript中有个特殊的存在:对象。每个对象还有一个原型对象,并可以从中继承方法和属性。

提到对象和原型,你是否曾经有过以下这些疑惑:

        1.JavaScript的函数怎么也是个对象?

        2._proto_和prototype到底是什么关系?

        3.JavaScript中对象是怎么实现继承的?

        4.JavaScript是怎么访问对象的方法和属性的?

我们依据这些问题,探究JavaScript对象和继承

        1.JavaScript的函数怎么也是个对象?

首先,原型对象和对象是什么关系?

在JavaScript中,对象由一组或多组的属性或值组成;对象的用途很是广泛,因为它的值既可以是原始类型(number、string、boolean、null、undefined、bigint和symbol)还可以是对象和函数。

不管对象、函数还是数组,它们都是Object的实例,除了原始类型以外,其余都是对象。

因此,在JavaScript中,函数也是一种特殊的对象,它同样拥有属性和值,所有的函数会有一个特别的属性prototype,该属性的值是一个对象,这个对象便是我们常说的"原型对象"。

我们在控制台中打印下这个属性,打印结果如下:

JavaScript如何实现继承?_第1张图片

        2._proto_和prototype到底是什么关系?

 可以看到,该原型对象有两个属性,constructor和Prototype;到这里,我们仿佛看到了疑惑二,_proto_和prototype到底是什么关系?

_proto_属性指向对象的原型对象;对于函数来说,它的原型对象便是prototype。函数的原型对象有以下特点

1.默认情况下,所有函数的原型对象prototype都拥有constructor属性,该属性指向与之关联的构造函数,在这里构造函数便是Person函数

2.Person函数的原型对象prototype同样拥有自己的原型对象,用_proto_属性表示

 因此Person.prototype的原型对象为Object.prototype

JavaScript如何实现继承?_第2张图片

 从图中,我们可以知道:

1.对于函数来说,每个函数都有一个prototype属性,该属性为该函数的原型对象

        3.JavaScript中对象是怎么实现继承的?

使用prototype和_proto_实现继承

通过将对象A的_proto_属性赋值为对象B

即A._proto_=B

此时使用A._proto_便可以访问B的属性和方法

JavaScript可以在两个对象之间创建一个关联

使得一个对象可以访问另一个对象的属性和方法,从而实现了继承

到此,疑惑3也基本差不多了解了,那么JavaScript又是怎么使用prototype和_proto_实现继承的呢?

继续以Person为例,

var lily = new Person("Lily");

//这条语句其实在JavaScript内部引擎实行了以下代码
var lily = {}
lily._proto_ = Person.prototype
Person.call(lily,"Lily")

JavaScript如何实现继承?_第3张图片

 根据这张图,我们可以得出:

1.每个函数的原型对象(Person.prototype)都拥有constructor属性,指向该原型对象的构造函数(Person)

2.使用构造函数(new Person())可以创建对象,创建的对象称为实例对象(lily)

3.实例对象通过将_proto_属性指向构造函数的原型对象(Person.prototype),实现了该原型对象的继承

现在关于疑惑2的问题,我们可以得出以下答案:

1.每个对象都有_proto_属性来表示自己所继承的原型对象,但只有函数才有prototype属性

2.对于函数来说,每个函数都有一个prototype属性,该属性为该函数的原型对象

3.通过将实例对象的_proto_属性赋值为其构造函数的原型对象prototype

        JavaScript可以使用构造函数创建对象的方式,来实现继承

        4.JavaScript是怎么访问对象的方法和属性的?

最后,我们来看问题四,JavaScript是怎么访问对象的方法和属性的?

JavaScript是通过原型链访问对象的方法和属性

JavaScript首先会优先在该对象上搜寻

如果找不到,还会依次层层向上搜索该对象的原型对象、

该对象的原型对象的原型对象等(套娃告警)

接下来,

JavaScript中的所有对象都来自Object

Object.prototype._proto_ = null

null没有原型,并作为这个原型链中的最后一个环节

最后,JavaScript会便利对象的整个原型链

如果最终依然找不到,此时会认为该对象的属性值为undefined

        5.总结

由于通过原型链层层遍历可能带来性能问题,当试图访问不存在的属性时,会遍历整个原型链。在原型链上查找比较耗时,对性能有副作用,这在性能要求苛刻的情况下很重要。因此,我们在设计对象的时候,要注意原型链的长度。

除了通过原型链的方式实现JavaScript的继承,JavaScript中实现继承的方式还有:

JavaScript如何实现继承?_第4张图片

其中,原型链继承方式中引用类型的属性被所有实例共享,无法做到实例私有;

经典继承方式可以实现实例属性私有,但要求类型只能通过构造函数来定义

虽然继承的方式有很多种,但实际上都离不开原型对象与原型链的内容。

随之ES6\ES7等新语法糖的出现,在日常开发中可能更倾向于使用class\extends等语法来编写代码,原型继承等概念逐渐变淡;但不管语法糖如何先进,JavaScript的设计在本质上依然没有变化,依然是基于原型来实现继承的。

原创不易,点赞关注,支持一下吧

你可能感兴趣的:(前端基础,-T,java,服务器,前端)