首页>>前端>>JavaScript->面不面试都要会的继承与原型链:原型链的尽头是null?

面不面试都要会的继承与原型链:原型链的尽头是null?

时间:2023-12-01 本站 点击:0

面不面试都要会的继承与原型链:原型链的尽头是null?

tips:每个技术点都值得优学优写:9期

好文推荐:

约2万字-Vue源码解读汇总篇(续更)

前端要会打组合拳,复盘30+技术点打出的功能

前言

我们知道,es 有一个关键字叫 class ,这是在 es2015(es6) 的时候引入的。它的中文意思是“类”, 在像 java 这样的编程语言中, 原生就有类(class)的支持,但 es6 引入的 class 关键字, 也只是语法糖,JavaScript 仍然是基于原型的。

当谈到继承时,JavaScript 只有一种结构:对象。 每个实例对象(object)都有一个私有属性(称之为 proto )指向它的构造函数的原型对象(prototype)。该原型对象也有一个自己的原型对象(proto),层层向上直到一个对象的原型对象为 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。

几乎所有 JavaScript 中的对象都是位于原型链顶端的 Object 的实例。

继承属性与原型链

javaScript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。

在原型链上,属性的被传递拥有,就是一种继承现象。(使用继承关键字 extends 的继承暂且不谈)

下面通过一个示例来理解继承与原型链

    // 使用语法结构创建的对象    let obj = {      a: 'hello',      fc: function () {        return this.a + 1      }    }    // obj 这个对象继承了 Object.prototype 上面的所有属性    // obj 自身没有名为 hasOwnProperty 的属性    // hasOwnProperty 是 Object.prototype 的属性    // 因此 obj 继承了 Object.prototype 的 hasOwnProperty    // Object.prototype 的原型为 null    // 原型链如下:    // obj ---> Object.prototype ---> null    console.log(obj)    console.log(obj.__proto__)    console.log(obj.__proto__.__proto__) // null,这就是原型链的终点    let subObj = Object.create(obj); // 创建的 subObj 本身没有自身的属性,但它继承有 obj 的属性。    console.log(subObj.fc()); // 3,fc() 继承自 obj    // 那么此时,我们就说 subObj 是一个继承自 obj 的对象,因为继承,obj 的一些属性被传递给了 subObj,    // 例如 fc() 就继承自 obj    // subObj 的原型链是 // subObj ---> obj ---> Object.prototype ---> null    console.log(subObj)

这是一张打印上面 obj 对象的原型链截图, 打印结果也符合上面关于 obj 对象原型链的结论:obj ---> Object.prototype ---> null

下面是一张打印 subObj 的原型连截图: 打印结果也符合上面关于 subObj 对象原型链的结论:subObj ---> obj ---> Object.prototype ---> null

创建对象和生成原型链的几种方法

使用 Object.create 创建的对象

Object.create() 是 es5 引入的创建对象的方法,使用该方法创建的新对象的原型就是 Object.create() 传入的第一个参数。

下面是一个例子。

let a = {a: 1};// 原型链: a ---> Object.prototype ---> nulllet b = Object.create(a);// 原型链:b ---> a ---> Object.prototype ---> nullconsole.log(b.a); // 1 ,b并没有自身属性,b.a 属性是继承而来的let c = Object.create(b);// 原型链:c ---> b ---> a ---> Object.prototype ---> nulllet d = Object.create(null);// 原型链:d ---> null // 根据定义,null 没有原型,因此 null 就是原型链的最后一个环节console.log(d.hasOwnProperty); // undefined, 因为 d 没有继承 Object.prototype,所以没有 hasOwnProperty 属性。

使用语法结构创建的对象

例如像下面这样,没用通过关键字创建的

let obj = {a: 1};// obj 这个对象继承了 Object.prototype 上面的所有属性// obj 自身没有名为 hasOwnProperty 的属性// hasOwnProperty 是 Object.prototype 的属性// 因此 obj 继承了 Object.prototype 的 hasOwnProperty// Object.prototype 的原型为 null// 原型链如下:// obj ---> Object.prototype ---> null

使用构造器创建的对象

在 JavaScript 中,构造器其实就是一个普通的函数。当使用 new 操作符 来作用这个函数时,它就可以被称为构造方法(构造函数)。

下面是一个示例

function Obj() {  this.arr = [];  this.arr2 = [];}Obj.prototype = {  addArr: function(v){    this.arr.push(v);  }};let g = new Obj();// g 是生成的对象,他的自身属性有 'arr' 和 'arr2'。// 在 g 被实例化时,g.[[Prototype]] 指向了 Obj.prototype。// 原型链如下:// g ---> obj ---> Object.prototype ---> null

使用 class 关键字创建的对象

ES6 引入了一套新的关键字用来实现 class。使用基于类语言的开发人员会对这些结构感到熟悉,但它们是不同的。 JavaScript 仍然基于原型。这些新的关键字包括 class, constructor,static,extends 和 super。

看见了吗? js 不仅有 class(类) 关键字,还有 extends(继承) 关键字。

下面是一个来自 Mozilla 的示例

class Polygon {  constructor(height, width) {    this.height = height;    this.width = width;  }}class Square extends Polygon {  constructor(sideLength) {    super(sideLength, sideLength);  }  get area() {    return this.height * this.width;  }  set sideLength(newLength) {    this.height = newLength;    this.width = newLength;  }}let square = new Square(2);

关于性能

在原型链上查找属性比较耗时,对性能有副作用,这在性能要求苛刻的情况下很重要。另外,试图访问不存在的属性时会遍历整个原型链。

遍历对象的属性时,原型链上的每个可枚举属性都会被枚举出来。要检查对象是否具有自己定义的属性,而不是其原型链上的某个属性, 则必须使用所有对象从 Object.prototype 继承的 hasOwnProperty方法。

小结

JavaScript 仍然是基于原型的,尽管有 class 和 extends 等关键字。

没错,原型链的最后一个环节是 null,null 是原型链的终点。

理解原型继承模型对于深入理解 JavaScript 是至关重要的,它是 JavaScript 的高级部分内容。

有一种继承是基于原型链的继承,这种继承当然也具有获得被继承者的属性的能力。

原文:https://juejin.cn/post/7099460734924357645


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/JavaScript/6442.html