乐趣区

关于javascript:面试官说说JS作用域和作用域链我是这样回答的

作用域(scope)

1. 什么是作用域

概念 :作用域是在程序运行时代码中的某些特定局部中变量、函数和对象的 可拜访性

从应用方面来 解释,作用域就是变量的应用范畴,也就是在代码的哪些局部能够拜访这个变量,哪些局部无法访问到这个变量,换句话说就是这个变量在程序的哪些区域可见。代码演示:

function Fun() {var inVariable = "外部变量";}
Fun();
console.log(inVariable); // Uncaught ReferenceError: inVariable is not defined
//inVariable 是在 Fun 函数外部被定义的,属于局部变量,在内部无法访问,于是会报错

从存储上来 解释 的话,作用域 实质 上是一个 对象, 作用域中的变量能够了解为是该对象的成员

总结:作用域就是代码的执行环境,全局作用域就是全局执行环境,部分作用域就是函数的执行环境,它们都是栈内存

2. 作用域分类

作用域又分为 全局作用域 部分作用域。在 ES6 之前,部分作用域只蕴含了函数作用域,ES6 的到来为咱们提供了 ‘块级作用域’(由一对花括号包裹),能够通过新增命令 let 和 const 来实现;而对于全局作用域这里有一个小细节须要留神一下:

  • 在 Web 浏览器中,全局作用域被认为是 window 对象,因而所有全局变量和函数都是作为 window 对象的属性和办法创立的。
  • 在 Node 环境中,全局作用域是 global 对象。

全局作用域很好了解,当初咱们再来解释一下部分作用域吧,先来看看 函数作用域,所谓函数作用域,顾名思义就是由函数定义产生进去的作用域,代码示例:

function fun1(){var variable = 'abc'}
function fun2(){var variable = 'cba'}
fun1();
fun2();
// 这里有两个函数,他们别离都有一个同名变量 variable,在严格模式下,程序不会报错,// 这是因为这两个同名变量位于不同的函数内,也就是位于不同的作用域中,所以他们不会产生抵触。

咱们再来看看 块级作用域 ,ES6 之前 JavaScript 没有块级作用域,只有全局作用域和函数(部分)作用域。块语句({} 两头的语句),如 ifswitch 条件语句,forwhile 循环语句,不同于函数,它们 不会创立一个新的作用域 ;然而 ES6 及之后的版本,块语句也 会创立一个新的作用域, 块级作用域可通过新增命令 let 和 const 申明,所申明的变量在 指定块 作用域外无奈被拜访。块级作用域在如下状况被创立:

  1. 在一个函数外部
  2. 在一个代码块(由一对花括号包裹)外部

let 申明的语法与 var 的语法统一。基本上能够用 let 来代替 var 进行变量申明,但会将变量的作用域限度在以后代码块中 (留神:块级作用域并不影响 var 申明的变量)。 然而应用 let 时有几点须要 留神

  • 申明变量不会晋升到代码块顶部,即不存在变量晋升
  • 禁止反复申明同一变量
  • for 循环语句中()外部,即圆括号之内会建设一个暗藏的作用域,该作用域不属于 for 后边的 {} 中,并且只有 for 后边的 {} 产生的块作用域可能拜访这个暗藏的作用域,这就使循环中 绑定块作用域有了妙用

参考 前端进阶面试题具体解答

这里别离演示一下 ES5 和 ES6 版本的代码,ES5:

if(true) {var a = 1}
for(var i = 0; i < 10; i++) {...}
console.log(a) // 1
console.log(i) // 10

ES6:

for (let i = 0; i < 10; i++) {console.log(i);//0,1,2,3,4,5,6,7,8,9
 }
console.log(i);// Uncaught ReferenceError: i is not defined
if (true) {let i = 9;}
console.log(i);// Uncaught ReferenceError: i is not defined

作用域链(scope chain)

概念:多个作用域对象间断援用造成的链式构造。

应用 方面 解释:当在 Javascript 中应用一个变量的时候,首先 Javascript 引擎会尝试在以后作用域上来寻找该变量,如果没找到,再到它的下层作用域寻找,以此类推直到找到该变量或是曾经到了全局作用域,如果在全局作用域里依然找不到该变量,它就会间接报错。

存储 方面 解释:作用域链在 JS 外部中是以数组的模式存储的,数组的第一个索引对应的是函数自身的执行期上下文,也就是以后执行的代码所在环境的变量对象,下一个索引对应的空间存储的是该对象的内部执行环境,顺次类推,始终到全局执行环境

代码示例:

var a = 100
function fun() {
    var b = 200
    console.log(a) //100
// fun 函数部分作用域中没有变量 a,于是从它的上一级,也就是全局作用域中找,// 在全局中 a 被赋值为 100,于是输入 100
    console.log(b)//200 fun 函数部分作用域中有变量 b,并且它被赋值为了 200,输入 200
}
fun()

再来看个栗子:

var a = 10
function fun() {console.log(a)
}
function show(f) {
   var a = 20
   (function() {f()   //10,而不是 20; 函数的作用域是在函数定义的时候就被决定了,与函数在哪里被调用无关
   })()}
show(fun)

因为变量的查找是沿着作用域链来实现的,所以也称作用域链为 变量查找的机制。是不是很好了解,这里再来补充一点作用域的作用

  1. 作用域最为重要的一点是平安。变量只能在特定的区域内能力被拜访,外部环境不能拜访外部环境的任何变量和函数,即能够向上搜寻,但不能够向下搜寻, 有了作用域咱们就能够防止在程序其它地位意外对某个变量做出批改导致程序产生事变。
  2. 作用域可能加重命名的压力。咱们能够在不同的作用域内定义雷同的变量名,并且这些变量名不会产生抵触。

1. 什么是作用域

概念 :作用域是在程序运行时代码中的某些特定局部中变量、函数和对象的 可拜访性

从应用方面来 解释,作用域就是变量的应用范畴,也就是在代码的哪些局部能够拜访这个变量,哪些局部无法访问到这个变量,换句话说就是这个变量在程序的哪些区域可见。代码演示:

function Fun() {var inVariable = "外部变量";}
Fun();
console.log(inVariable); // Uncaught ReferenceError: inVariable is not defined
//inVariable 是在 Fun 函数外部被定义的,属于局部变量,在内部无法访问,于是会报错

从存储上来 解释 的话,作用域 实质 上是一个 对象, 作用域中的变量能够了解为是该对象的成员

总结:作用域就是代码的执行环境,全局作用域就是全局执行环境,部分作用域就是函数的执行环境,它们都是栈内存

2. 作用域分类

作用域又分为 全局作用域 部分作用域。在 ES6 之前,部分作用域只蕴含了函数作用域,ES6 的到来为咱们提供了 ‘块级作用域’(由一对花括号包裹),能够通过新增命令 let 和 const 来实现;而对于全局作用域这里有一个小细节须要留神一下:

  • 在 Web 浏览器中,全局作用域被认为是 window 对象,因而所有全局变量和函数都是作为 window 对象的属性和办法创立的。
  • 在 Node 环境中,全局作用域是 global 对象。

全局作用域很好了解,当初咱们再来解释一下部分作用域吧,先来看看 函数作用域,所谓函数作用域,顾名思义就是由函数定义产生进去的作用域,代码示例:

function fun1(){var variable = 'abc'}
function fun2(){var variable = 'cba'}
fun1();
fun2();
// 这里有两个函数,他们别离都有一个同名变量 variable,在严格模式下,程序不会报错,// 这是因为这两个同名变量位于不同的函数内,也就是位于不同的作用域中,所以他们不会产生抵触。

咱们再来看看 块级作用域 ,ES6 之前 JavaScript 没有块级作用域,只有全局作用域和函数(部分)作用域。块语句({} 两头的语句),如 ifswitch 条件语句,forwhile 循环语句,不同于函数,它们 不会创立一个新的作用域 ;然而 ES6 及之后的版本,块语句也 会创立一个新的作用域, 块级作用域可通过新增命令 let 和 const 申明,所申明的变量在 指定块 作用域外无奈被拜访。块级作用域在如下状况被创立:

  1. 在一个函数外部
  2. 在一个代码块(由一对花括号包裹)外部

let 申明的语法与 var 的语法统一。基本上能够用 let 来代替 var 进行变量申明,但会将变量的作用域限度在以后代码块中 (留神:块级作用域并不影响 var 申明的变量)。 然而应用 let 时有几点须要 留神

  • 申明变量不会晋升到代码块顶部,即不存在变量晋升
  • 禁止反复申明同一变量
  • for 循环语句中()外部,即圆括号之内会建设一个暗藏的作用域,该作用域不属于 for 后边的 {} 中,并且只有 for 后边的 {} 产生的块作用域可能拜访这个暗藏的作用域,这就使循环中 绑定块作用域有了妙用

参考 前端进阶面试题具体解答

这里别离演示一下 ES5 和 ES6 版本的代码,ES5:

if(true) {var a = 1}
for(var i = 0; i < 10; i++) {...}
console.log(a) // 1
console.log(i) // 10

ES6:

for (let i = 0; i < 10; i++) {console.log(i);//0,1,2,3,4,5,6,7,8,9
 }
console.log(i);// Uncaught ReferenceError: i is not defined
if (true) {let i = 9;}
console.log(i);// Uncaught ReferenceError: i is not defined

作用域链(scope chain)

概念:多个作用域对象间断援用造成的链式构造。

应用 方面 解释:当在 Javascript 中应用一个变量的时候,首先 Javascript 引擎会尝试在以后作用域上来寻找该变量,如果没找到,再到它的下层作用域寻找,以此类推直到找到该变量或是曾经到了全局作用域,如果在全局作用域里依然找不到该变量,它就会间接报错。

存储 方面 解释:作用域链在 JS 外部中是以数组的模式存储的,数组的第一个索引对应的是函数自身的执行期上下文,也就是以后执行的代码所在环境的变量对象,下一个索引对应的空间存储的是该对象的内部执行环境,顺次类推,始终到全局执行环境

代码示例:

var a = 100
function fun() {
    var b = 200
    console.log(a) //100
// fun 函数部分作用域中没有变量 a,于是从它的上一级,也就是全局作用域中找,// 在全局中 a 被赋值为 100,于是输入 100
    console.log(b)//200 fun 函数部分作用域中有变量 b,并且它被赋值为了 200,输入 200
}
fun()

再来看个栗子:

var a = 10
function fun() {console.log(a)
}
function show(f) {
   var a = 20
   (function() {f()   //10,而不是 20; 函数的作用域是在函数定义的时候就被决定了,与函数在哪里被调用无关
   })()}
show(fun)

因为变量的查找是沿着作用域链来实现的,所以也称作用域链为 变量查找的机制。是不是很好了解,这里再来补充一点作用域的作用

  1. 作用域最为重要的一点是平安。变量只能在特定的区域内能力被拜访,外部环境不能拜访外部环境的任何变量和函数,即能够向上搜寻,但不能够向下搜寻, 有了作用域咱们就能够防止在程序其它地位意外对某个变量做出批改导致程序产生事变。
  2. 作用域可能加重命名的压力。咱们能够在不同的作用域内定义雷同的变量名,并且这些变量名不会产生抵触。

退出移动版