JavaScript本身是没有类继承的,但是我们又可以使用ES6中的class关键字来写一个类,它的底层实现是原型链。原型链的介绍在MDN–原型链上的介绍已经很详细了,但我想分享一下我的理解,如果不正确的地方,欢迎指出。

什么是原型链

在chrome的devtool中,我们打印一个引用类型的变量时可以看到这个变量一般会有个__proto__属性。这个对应的就是这个变量的原型。每个引用类型的变量都会有其对应的原型,然后其对应的原型又有其对应的原型 套娃警告⚠️ ,这样的一条链被称为原型链,最终__proto__会指向null。

__proto__

就以上图为例子,我们需要先了解两个事实。首先js中的数组也是一个对象,第二 js的数组通过下表访问数组元素时,本质上是使用了方括号表示法的属性访问器来获取对象的属性。

__proto__

可能会疑惑我们常用的js的push方法,没有出现在这里。这其实是没有问题的,当我们调用a.push()的时候,首先会查看对象a上是否有push方法,没有则继续查看其__proto__上是否有push方法,以此类推,直到__proto__为null时,说明a上不存在push,因而报错,对于其他引用类型的变量也是同样的道理。我们使用构造函数new一个新的对象时,新对象的__proto__ 指向的就是这个构造函数的prototype。这样,我们将一个构造函数的prototype__proto__指向另一个构造函数的prototype,就可以用js的原型链实现类的继承。

js中的令人迷惑的this指向

js中的this指向和函数的调用方式有关,我们可以分为以下几种方式。

  1. 当作为普通函数调用时,this指向的就是全局对象,这里的全局对象具体是什么,需要看运行环境。

  2. 如果函数是作为对象的方法时,this指向的就是这个对象。

  3. 如果函数是作为构造函数的话,那么this会指向这个new产生的新对象。

  4. 除此之外还可以通过call,apply,bind这些方法来修改this的指向。

function func1(){
  console.log(this);
}


const obj1 = {
  func:func1
}

const person = {
  name:'chen'
}

func1(); // 函数作为普通函数调用
obj1.func(); // 函数作为对象的方法调用
const obj2 = new func1(); // 函数作为构造函数调用
func1.call(person); // 使用call来修改this指向
obj1.func.apply(person); // 使用apply来修改this指向

建议跟着敲一遍代码,有助于理解

说到了this指向问题,不得不提一下箭头函数箭头函数不仅仅是使函数的写法更加简短,箭头函数的this指向也与其他普通函数不同。箭头函数的this指向不会因为它的调用方式不同而不同,它的this指向的是定义函数处的this。

setTimeout(function(){
  console.log(this);
})

setTimeout(() => {
  console.log(this);
})

结语

理解原型链和js中的this指向问题以及箭头函数的一些特性可以帮助自己在写代码时,少踩挺多坑的。相关的有意思的问题还有挺多的,也有一些安全方面的问题,像原型链污染之类的。