总结JS的作用域、作用域链、变量提升、暂时性死区、柯里化、闭包

73 阅读5分钟

前言

我们总是会看到或者听到作用域和作用域链、柯里化、闭包等词语,甚至使用到,趁着现在我总结一下,希望可以帮助大家了解它们,也希望得到大家的指点。

什么是作用域?

作用域是指 js 变量使用时所存在的一个区域,简单来说就是可访问区域。

window.a = 1
function a(){
// 输出 1,虽然局部没有 a 变量,但是 全局中有。
console.log(a)
var b = 2
}
// 报错,全局中无法使用局部变量。
console.log(b)

js有哪些作用域?

js具体有如下作用域

  • Global全局作用域:在浏览器环境下就是 window,在 node 环境下是 global
  • Local函数作用域:本地作用域,或者叫函数作用域
  • Block块级作用域:es5之前只有函数作用域和全局作用域,es6开始引入了块级作用域
  • Script作用域:let、const 声明的全局变量会保存在 Script 作用域,这些变量可以直接访问,但却不能通过 window.xx 访问,如果需要访问需要把变量挂载在全局作用域上。
  • 模块作用域:其实严格来说这也是函数作用域,因为 node 执行它的时候会包一层函数,算是比较特殊的函数作用域,有 module、exports、require 等变量
  • Catch Block 作用域:catch 语句的作用域可以访问错误对象
  • With Block 作用域:with 语句的作用域就是传入的对象的值
  • Closure 作用域:函数返回函数的时候,会把用到的外部变量保存在 Closure 作用域里,这样再执行的时候该有的变量都有,这就是闭包。eval 的闭包比较特殊,会把所有变量都保存到 Closure 作用域
  • Eval 作用域:eval 代码声明的变量会保存在 Eval 作用域

什么是作用域链?

在局部作用域使用变量时,如果在自己作用域找不到对应变量,则会往上一级作用域查找,直到全局作用域,如果全局作用域无此变量则会报 undefined。相反,全局作用域中无法使用局部作用域中的变量。

window.a = 1
function a(){
    //函数作用域中没有声明a但是往上级作用域中一直找,到window全局作用域中找到了a
    console.log(a)
}

什么是闭包?

如下add函数中声明了sum 但是add的匿名函数中使用到了sum,这样就形成了闭包,红宝书中说闭包是指有权访问另一个函数作用域中的变量的函数。因为匿名函数中会使用到sum,此时sum会存储到Closure作用域中。

var add = function(){ 
    let sum = 0 
    return function(){ 
        return sum + 1 
    } 
}
  • 使用闭包的目的――隐藏变量,间接访问一个变量,在定义函数的词法作用域外,调用函数。
  • 闭包通常在回调函数、私有属性、函数柯里化中使用。

什么是柯里化?

函数柯里化就是将一个多参函数转为单参函数,简化函数的传参,但是会增加函数内部的复杂程度。

// 正常计算两数相加的方法 
function sum(x, y){ 
    return x + y 
 } 
console.log(sum(2, 2)) 
// 使用闭包实现柯里化 
function sum(x){ 
    return function(y){ 
        return x + y 
    } 
} 
function sum_t(x){ 
    //可以执行A逻辑
    return function(y){ 
        //可以执行B逻辑
        return function(z){ 
            return z+ x + y 
        } 
    } 
} 
// 先声明一个变量拿到自增方法(匿名函数) 
const sum_function = sum(1) 
// 在调用这个方法进行自增,输出:3 
console.log(sum_function(2)) 
// 亦或者直接调用sum方法传入两个参数,输出也是:3 
console.log(sum(1)(2))
//当然如果有更多的参数比如sum_t方法,可以变成如下这种调用方式,但是柯里化层数太多了个人觉得复杂度会提升很大,不是很有必要。
sum_t(1)(2)(3)

什么是变量提升

变量可以在声明之前使用,值为undefined。这种感觉比较奇怪的现象就是变量提升。

为了纠正这种现象,let命令改变了语法行为,它所声明的变量一定要在声明后使用,否则报错。

下面代码中,变量foovar命令声明,会发生变量提升,即脚本开始运行时,变量foo已经存在了,但是没有值,所以会输出undefined。变量barlet命令声明,不会发生变量提升。这表示在声明它之前,变量bar是不存在的,这时如果用到它,就会抛出一个错误。

// var 的情况
console.log(foo); // 输出undefined
var foo = 2;
// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;

什么是暂时性死区

在变量声明前就使用变量,这种情况会报错,就是暂时性死区,比如下面的代码

if (true) {
  // 暂时性死区开始
  name = '小明'; // ReferenceError
  console.log(name); // ReferenceError

  let name; // 暂时性死区结束
  console.log(name); // undefined

  name = '大明';
  console.log(name); // 大明
}

暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。

总结

把自己的理解,再加上查阅资料,整合成了这个文档,如果有不对的地方希望大家指正,有些作用域我自己也没关注,趁着这个机会,终于也有了大致的了解,再一次变成了我写博客的动力,只要写了就会有收获,加油。