首页 > 其他分享 >面试官:说说JS作用域和作用域链,我是这样回答的

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

时间:2022-10-06 08:22:15浏览次数:75  
标签:面试官 console 变量 作用域 JS var 全局 函数

作用域(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. 作用域能够减轻命名的压力。我们可以在不同的作用域内定义相同的变量名,并且这些变量名不会产生冲突。

标签:面试官,console,变量,作用域,JS,var,全局,函数
From: https://www.cnblogs.com/loveX001/p/16756969.html

相关文章

  • 聊聊前端面试中的js同步与异步问题
    前言我本来是打算写一篇co源码精读(为啥读co,因为它短),然鹅发现自己存在一系列基础问题没有搞透彻,打算写一个js基础系列文章,总结自己的理解(copy),希望与你在学习路上一同进......
  • vue res.data接收到的是字符串的处理方式,先转化成Json格式再解析
    api.postWachPay(param).then(res=>{this.html=res.data;letdata=JSON.parse(res.data)console......
  • 关于 NodeJS 模块化不得不说的坑
    关于NodeJS模块化不得不说的坑本文写于:2022-10-05CJS与ESM的简单介绍面临的问题问题1:如何交叉引入(ESM引入CJS、CJS引入ESM)问题2:ESM必须带上文件扩展名......
  • 从setTimeout理解JS运行机制
    序setTimeout()函数:用来指定某个函数或某段代码在多少毫秒之后执行。它返回一个整数,表示定时器timer的编号,可以用来取消该定时器。例子console.log(1);setTimeout(func......
  • js格式化文件大小
    constsizeUnit=['Bytes','KB','MB','GB','TB'];functionformatSize(fileSizeInBytes){constsizeType=parseInt(Math.floor......
  • 不就是SELECT COUNT语句吗,竟然能被面试官虐的体无完肤
    数据库查询相信很多人都不陌生,所有经常有人调侃程序员就是CRUD专员,这所谓的CRUD指的就是数据库的增删改查。在数据库的增删改查操作中,使用最频繁的就是查询操作。而在所有......
  • 就因为JSON.stringify,我的年终奖差点打水漂了
    本文已参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金。前言「欢迎在评论区讨论,掘金官方将在掘力星计划活动结束后,在评论区抽送100份掘金周边,抽奖详情见活动文章」......
  • js原生文件上传
    .upload-btn{width:96px;height:28px;font-size:12px;font-family:PingFangSC-Regular,PingFangSC;font-weight:400;color:#333333;line-height:......
  • 【c++基础】从json文件提取数据
    前言标注数据导出文件是json格式的,也就是python的dict格式,需要读取标注结果,可以使用c++或者python,本文使用c++实现的。JsonCpp简介JsonCpp是一种轻量级的数据交换格式,是个跨......
  • 详解js中的对象的深浅拷贝
    前言本文是整理的浅拷贝和深拷贝中涉及的知识点,在工作中是非常重要的,在面试中也是必考的,希望对小伙伴们有所帮助!为什么会产生深浅拷贝?首先我们要知道一个流程1,对象属......