likes
comments
collection
share

js核心系列(四) —— 作用域和作用域链

作者站长头像
站长
· 阅读数 29

js核心系列(四) —— 作用域和作用域链

作用域(scope)

JavaScript 中的作用域是一种机制,它决定代码片段对其他部分的可访问性。并且回答了以下问题: 从哪里可以访问?从哪里无法进入?谁可以访问它,谁不能? 简单来说,作用域就是规定变量与函数可访问范围的一套规则。

有哪些作用域

  • 全局作用域

  • 函数作用域

  • 块级作用域

全局作用域

在程序顶部或函数外部声明的变量被认为是全局范围变量。全局作用域中声明的变量与函数,可以在代码的任何地方被访问。

一般来说,以下三种情形属于全局作用域。

全局对象下的属性与方法

window.name

window.location

window.top

...

在最外层声明的变量与方法


let name = "jimmy";

function greet() {
  console.log(name);
}
greet(); // jimmy

在非严格模式下,函数作用域中未定义但直接赋值的变量与方法。

在非严格模式下,这样的变量自动变成全局对象window的属性。因此他们也是属于全局作用域。

function foo() {

  bar = 20;

}

function fn() {

  foo();

  return bar + 30;

}

fn(); // 50

需要注意的是,从一个完整的大型应用的角度来考虑,我们应该尽量少的将变量或者方法定义为全局。

  • 我们可能会无意间修改全局变量的值,但是其他场景并不知道
// 定义全局变量

const foo = { m: 200 }

function setM() {

  // 轻易的被修改

  foo.m = 300

}

setM()
  • 命名冲突:不同的开发者,在同一个项目里如果都使用全局变量的话,很容易造成命名冲突
  • 应用程序执行过程中,全局变量的内存无法被释放

避免使用全局变量是一个很好的做法,因为全局变量的值可能会在程序的不同区域发生变化。它可以在程序中引入未知的结果。因此,对于团队项目管理来说,每一个全局变量的使用,都应该引起足够的重视以防止影响到别的代码逻辑。

函数作用域

每一个花括号 {} 都是一个代码块。但需要注意的是,并不是所有花括号,都能够具备自己的作用域。函数声明或者函数表达式,能够让花括号具备作用域,我们称之为函数作用域。函数作用域中声明的变量与方法,只能被下层子作用域访问,不能被其他不相关的作用域访问。

let a = "hello";

function greet() {
    let b = "World"
    console.log(a + b);
}

greet();
console.log(a + b); // error

在上面的程序中,变量 a 是全局变量,变量 b 是局部变量。变量 b 只能在函数 hello 中访问。因此,当我们尝试访问函数外部的变量 b 时,会发生一个错误。

块级作用域

块级作用域由最近的一对包含花括号{} 界定。换句话说, if 块、while 块、function块,单独的块块级作用域。

letcosnt实际上为 JavaScript 新增了块级作用域。

function f1() {
  let n = 5;
  if (true) {
    let n = 10;
  }
  console.log(n); // 5
}

上面的函数有两个代码块,都声明了变量n,运行后输出 5。这表示外层代码块不受内层代码块的影响。如果两次都使用var定义变量n,最后输出的值才是 10。

ES6 允许块级作用域的任意嵌套。

{{{{
  {let insane = 'Hello World'}
  console.log(insane); // 报错
}}}};

上面代码使用了一个五层的块级作用域,每一层都是一个单独的作用域。第四层作用域无法读取第五层作用域的内部变量。

内层作用域可以定义外层作用域的同名变量。

{{{{
  let insane = 'Hello World';
  {let insane = 'Hello World'}
}}}};

块级作用域的出现,实际上使得获得广泛应用的匿名立即执行函数表达式(匿名 IIFE)不再必要了。

// IIFE 写法
(function () {
  var tmp = ...;
  ...
}());

// 块级作用域写法
{
  let tmp = ...;
  ...
}

作用域链(scope chain)

我们用一张图来理解

js核心系列(四) —— 作用域和作用域链

  • 蓝色框是全局作用域,定义了a变量,以及里面的所有函数。
  • 红色框是first函数的作用域,它定义了变量 b ,以及second函数。
  • 绿色框是第second的作用域。log语句用于输出变量 a、 b 和 c。

当代码执行道second函数是,打印变量a,b,c,但是变量 a 和 b 没有在second函数中定义,只定义了c。这时就会往上层作用域查找,于是从first函数找到了变量 b = 'Hello'。这时还没用找到a,所有再继续往上层作用域查找,然后找到了a = 'Hey',这样一层一层往上查找的过程,就被成为作用域链

当 JS 引擎无法在作用域链中找到变量时,它就会停止执行并抛出错误。

转载自:https://juejin.cn/post/7224501858411282487
评论
请登录