深入学习JS中的作用域

今天就带领大家一起来学习JS中的作用域,首先介绍一些关于作用域的定义。JS中的作用域是指变量的可访问范围。这个范围可以是全局的(Global)或局部的(Local)。在JS中,作用域是由函数定义的。这意味着变量的可见性是基于它们被声明的位置。

在JavaScript中,有以下几种作用域:

  1. 全局作用域(Global Scope):在整个JS代码中都能访问的变量称为全局变量。全局变量在所有函数之外声明,在任何地方都可以被引用。

  2. 函数作用域(Function Scope):在函数内声明的变量只能在该函数内部访问。这意味着它们在函数外部是不可见的。

  3. 块级作用域(Block Scope):在ES6之前,JavaScript没有块级作用域。但是,通过使用letconst关键字,可以在特定的块中创建作用域。

了解作用域的概念对于编写JavaScript代码至关重要,因为它决定了变量的可见性和生命周期。

代码执行前的步骤

每段代码在执行出结果之前,都要进行以下步骤,也就是词法分析,解析,生成代码

js 复制代码
  var a = 3;
1.词法分析

词法单元:var, a, = ,3

2.解析(语法分析)

将词法单元转换成一个逐级嵌套的程序语法结构树 --- 抽象语法树

3.生成代码

var a = 3;

有效标识符

在 JavaScript 中,有效标识符是用来命名变量、函数、对象属性等的名称。

js 复制代码
function foo(a){//foo有效标识符a,b,c,bar
    var b = 2;
    function bar(c){//bar有效标识符只有c
        console.log(a+b+c);
    }
    bar(3);
}
foo(1);

在上面的代码中a,b,c,bar是foo的有效标识符,bar是一个函数的声明,其中bar中的有效标识符只有c。

作用域

内层作用域是可以访问外层作用域的,外层作用域不能访问内层作用域

js 复制代码
function foo(){
    var a = 1;
}
foo();
console.log(a);

这段代码执行结果会报错,显示a没有被定义,就是因为打印a的代码在全局作用域,而a的值定义在了函数作用域内,外层作用域访问不了内层作用域中a的值。

js 复制代码
//改变一下代码,将a定义在全局作用域
var a = 1;
function foo(){
    console.log(a);
}
foo();

改变一下代码,将变量a定义在全局作用域,这时是可以打印出来a的结果为1,因为调用函数foo时,函数要打印a的值,就会先在函数foo中找有没有定义a的值,如果在函数作用域里找不到a的值,就会去全局作用域里面找到a的值。

现在给出一段代码,你知道最后会打印出来什么吗?

js 复制代码
 var a = 1;

 function foo(){
    var a = 2;
    console.log(a);
 }
 foo();


这段代码的调用栈如上图所示,打印a的值会先在foo中找,结果找到了a=2,所以最后会打印出来结果为2,如果在foo中没有找到,才会去全局找。

声明提升

JavaScript 中的声明提升是指在代码执行过程中,变量和函数声明会被提升到其所在作用域的顶部,而实际赋值留在原来的位置。这意味着在变量或函数声明之前就可以访问它们,但是在赋值之前访问的值会是 undefined。

js 复制代码
console.log(a);//输出undefined
var a = 2;

正常情况下,上面的代码执行出来的结果应该会报错,因为,代码是从上往下执行的,在打印a之前并没有a的定义,但这段代码的执行结果是undefined,就是因为var声明的变量会声明提升,这段代码等效于下面的代码。

js 复制代码
var a;
console.log(a);//输出undefined
a = 1;

块级作用域

块级作用域是指在代码块(通常由花括号 {} 包裹)内部声明的变量或函数只在该代码块内部可见,外部无法访问。在 JavaScript 中,块级作用域通常与 let 和 const 关键字一起使用。

js 复制代码
if(true){
    let a = 1;//或者const a = 1;
}
console.log(a);

上面的代码的执行结果就是a is not defined ,因为let和{}形成了一个块级作用域,外层的全局作用域访问不了内层的块级作用域,所以结果是undefined。

let 和 var 的区别

  • var 存在声明提升,let不存在
  • let 会和{}形成块级作用域
  • var 可以重复声明变量, let 不可以
  • let 暂时性死区,暂时性死区(Temporal Dead Zone,简称 TDZ)是指在块级作用域中,使用 let 或 const 声明的变量在声明之前无法被访问,否则会抛出错误。

举个例子:

js 复制代码
if(1){
    console.log(a);//ReferenceError: Cannot access 'a' before initialization
    let a = 2;
}

尽管 a 已经在后面被赋值为 2,但在声明之前访问 a 会抛出 ReferenceError。这是因为在 let 声明的变量在代码执行到声明处之前处于暂时性死区,不能被访问。

  • const 用于声明不可变的常量,而 let 用于声明可变的变量。const声明的变量不允许更改。

欺骗词法作用域

  • eval() 将原本不属于这里的代码变成就像天生就定义在这里一样
js 复制代码
function foo(str,a){
    eval(str);//把字符串转化为代码
    console.log(a,b);
}
var b = 2;
foo('var b = 3',1);

按照正常情况下,结果应该是1和2,但是这段代码输出的结果是1和3,就是因为eval把字符串'var b = 3' 直接转化成了代码var b = 3 ,这种情况就称是eval()欺骗了词法作用域。

  • with(){} 用于修改一个对象中的属性值,但如果修改的属性在原对象中不存在,那么该属性就会被泄露到全局
js 复制代码
function foo(obj){
    with(obj){
        a = 2;
    }
}
var o2 ={
    b: 3
}
foo(o2);
console.log(a);

按照前面所说,外层作用域不能访问内层作用域,所以上面的代码按照常规来说结果应该是a is not defined ,但是上面的结果却可以打印出来2,就是因为with(){}将a = 2泄露到了全局。

以上就是今天给大家带来的内容,希望可以帮助大家更好的学习js,如果有什么地方不对也可以评论区告诉我哦。

相关推荐
Bubluu2 分钟前
浏览器点击视频裁剪当前帧,然后粘贴到页面
开发语言·javascript·音视频
鎈卟誃筅甡14 分钟前
Vuex 的使用和原理详解
前端·javascript
呆呆小雅19 分钟前
二、创建第一个VUE项目
前端·javascript·vue.js
m0_7482393325 分钟前
前端(Ajax)
前端·javascript·ajax
Fighting_p29 分钟前
【记录】列表自动滚动轮播功能实现
前端·javascript·vue.js
前端Hardy30 分钟前
HTML&CSS:超炫丝滑的卡片水波纹效果
前端·javascript·css·3d·html
Domain-zhuo1 小时前
Git和SVN有什么区别?
前端·javascript·vue.js·git·svn·webpack·node.js
一只搬砖的猹2 小时前
cJson系列——常用cJson库函数
linux·前端·javascript·python·物联网·mysql·json
CodeClimb2 小时前
【华为OD-E卷-租车骑绿道 100分(python、java、c++、js、c)】
java·javascript·c++·python·华为od
"追风者"2 小时前
前端(八)js介绍(1)
前端·javascript