《前端开发核心知识进阶》之JavaScript基础强化:this、闭包

301 阅读4分钟

前言

看标题就知道,这篇文章是学习笔记。这篇文章主要整理两个知识点:this和闭包。这两个知识点在JavaScript中都是比较难懂并且十分重要的。我想,作者也是基于这个考虑才把这两个知识点放在书的前两章。

关于this,不得不说之事

在《JavaScript20年》中,Brendan Eich对于this的描述如下:

每个函数都有一个隐式的this形参。将函数作为方法调用时,这个形参会被设置为用于访问该方法的对象。这和大多数面向对象语言中的this(或self)含义相同。但是JavaScript在关联到对象的方法独立函数这两者之间,使用了单一的定义形式。这使this导致了许多程序员的困惑和bug。

翻译成大白话就是this是个坑。那么,为什么会有这个设计的坑呢?这就说来话长了。由于这并不是我们的重点,这里就不再赘述了。感兴趣的小伙伴可以戳这里来了解具体的情况。这里我们只需要借用作者的结论就好了:

  • this的灵活指向,属于JS自己发明的语言特性
  • this存在的问题是公认的
  • this的这种设计既不利于代码可读性,也不利于性能优化,完全可以对其施加强限制
  • this设计问题的根源,是产品营销需求与设计者个人偏好之间的冲突

this存在这样那样的问题,在实际的代码编写中,我们也尽量少用。但是,面试的时候,面试官似乎还是挺喜欢挑这些问题来问的。而且,作为使用者来说,不能因为他有问题就不用了,纠结语言本身的设计纰漏也没有意义。总之,不管怎么说,我们都需要好好学学this。

关于this的指向,可以分几类:

  • 隐式绑定:
    • 在函数体(这里指独立的,直接定义在文件中,不是包含在某个对象中作为该对象方法的函数)中,在严格模式下,函数内的this会被绑定到undefined上;在非严格模式下,this会被绑定到全局对象Windows/global
    • 当函数作为某个对象的方法,或者说该函数作为某个对象的属性值时,this会绑定到该对象上
  • 显式绑定:
    • 通过call/apply/bind方法可以显式的将this绑定到某个对象上
  • new绑定:利用new关键字调用某个函数(我们通常称之为构造函数)时,函数会被绑定在新创建的对象上
  • 箭头函数:this始终指向外层(函数或全局)的作用域

除此之外,this指向还有优先级问题:new绑定 -> 显式绑定 -> 隐式绑定 -> 默认绑定。此外,还需要关注的点就是this指向和直接调用它的位置相关。举个栗子:

const foo = {
    bar: 10,
    fn: function() {
        console.log(this)
        console.log(this.bar)
    }
}

var fn1 = foo.fn
fn1()

foo.fn()

这段代码中,fn1()输出的结果是windowsundefined, 而foo.fn()输出的结果是{bar: 10, fn: f}和10。这里就是我们所说的,直接调用含义了。中间不能隔着什么东西,一旦隔着就会导致this指向不同的地方。

最后一个知识点是关于bind的。callapply是直接进行过i昂管函数调用的;bind不会执行相关函数,而是返回一个新的函数,这个新的函数已经自动绑定了新的this指向

知识点差不多就这么多,如果有想看例题的小伙伴,可以直接参照原书。而且小编之前也总结过this的相关内容,请戳这里

“老司机”也会翻车的闭包

提到闭包就不得不提作用域和内存管理了。闭包的话,我已经在很多地方都总结过了,链接在这里:

在本文中,只需要做个简单的总结就好:

  1. 函数嵌套函数时,内层函数引用了外层函数作用域下的变量,并且内层函数在全局环境下可访问,进而形成闭包
  2. 闭包和作用域,作用域链,内存管理,函数调用栈这些概念直接相关:
    • 作用域和作用域链:老生常谈的概念,此处不赘述
    • 内存管理:对内存生命周期的管理,内存的证明周期包括分配内存、读写内存、释放内存
    • 函数调用栈:从字面上即可理解,它就是一个栈,用来保存函数调用的现场的
  3. 闭包不能乱用:可能会导致内存泄漏