【前端学习 -JS(7)作用域】

 作用域

扩展

JavaScript是门动态语言,跟Java不一样,JavaScript可以随意定义全局变量和局部变量,每一个函数都是一个作用域,当函数执行时会优先查找当前作用域,然后逐级向上。

JavaScript是静态作用域,在对变量进行查询时,变量值由函数定义时的位置决定,和执行时的所处的作用域无关。

ES6已经有块级作用域了,而且用 let 和 const 定义的变量不会提升。

概念

作用域:变量或者函数的有效作用范围

作用域链:我们需要查找某个变量值,会先在当前作用域查找,如果找不到会往上一级查,如果找到的话,就返回停止查找,返回查找的值,这种向上查找的链条关系,叫作用域

7-1 相关案例

(1)变量提升/变量由函数定义时的位置决定

var a = 1;
function fn() {
  console.log('1:' + a);
  var a = 2;
  bar()
  console.log('2:' + a)
}
function bar() {
  console.log('3:' + a)
}
fn()

分别打印:1:undefined 3:1 2:2

「解析:」

第一个 a 打印的值是 1:undefined 而不是 1。因为我们在 fn() 中定义了变量 a,用 var 定义的变量会提升到当前作用域的顶部(即当前函数作用域顶部),但是赋值还在原地,所以是undefined。

第二个a 打印的值是 3:1 而不是 2。因为函数 bar 是定义在全局作用域中的,所以作用域链是 bar -> global,bar 里面没有定义a,所以就会顺着作用域链向上找,然后在 global 中找到了 a。注意:查找是在其定义的执行上下文环境中查找。

第三个 a 打印的值是 2:2。这句话所在的作用域链是 fn -> global,执行 console.log('2:' + a) 会首先在 fn 作用域里查找 a,找到有 a,并且已经赋值为2,所以结果就是2。

(2)变量赋值

var a = 1;
function fn() {
  console.log('1:' + a);
  a = 2
}
a = 3;
function bar() {
  console.log('2:' + a);
}
fn();
bar();

分别打印:1:3 2:2

「解析:」

第一个 打印的值是 1:3。首先, fn 中的 a = 2 是给变量 a 赋值,并不是声明变量。然后,执行函数 fn,此时 a 已经赋值为3了,注意,fn()是在a=3后面执行。

第二个 打印的值是 2:2。函数 bar 所能访问的作用域链为 bar->global,在执行函数 bar 时,由于在bar前执行了fn()将a修改为2了,所以这个时候拿到的a为2。

(3)全局变量声明提前

if(!(a in window)){
    var a = 10;
}
console.log(a);

打印:undefined

「解析:」

相当于:

var a;
if(!(a in window)){
    a = 10;
}
console.log(a);

用 var 定义的变量会提升到当前作用域的顶部(即当前全局作用域), 所以a会声明提前到window中,但值还是在原地,即为undefined。 所以if得到是a in window是ture 故不走里面赋值 console.log(a) == undefined

上一个例子的变种:

(function(){
 var  x = c =  b = {a:1}
})()
console.log(c,b) // {a: 1} {a: 1}
console.log(x.a); // error , x is not defined

注意: x是在函数中声明的,是局部变量,c和b未声明,直接赋值,所以是全局变量。 赋值过程是从右往左的,即b={a:1},c=b,x=c

(4)变量提升/运算符顺序

(function(){
  var a = b = 3;
})()
console.log(typeof a === "undefined"); // true
console.log(typeof b === "undefined"); // false
console.log(typeof b === "number" && b ===3); // true

// 这里涉及的就是立即执行和闭包的问题,还有变量提升,运算符执行方向(=号自右向左)

// 那个函数可以拆成这样

(function()
  var a; /* 局部变量,外部没法访问*/
  b = 3; /* 全局变量,so . window.b === 3 , 外部可以访问到*/
  a = b;
})()

(5)变量提升/运算符顺序

var x = 1;
if (function f(){console.log(2)}) {
x += typeof f;  
}
console.log(x);  // 1undefined

//因为函数体在()中会以表达式去运行,fn函数不起作用,函数不会执行。

//最后表达式转换为true,f未声明(上面的函数没起作用),值为undefined

「知识点:」

(1) 在JavaScript中,通过 let 和 const 定义的变量具有块级作用域的特性。

(2) 通过 var 定义的变量会在它自身的作用域中进行提升,而 let 和 const 定义的变量不会。

(3) 每个JavaScript程序都具有一个全局作用域,每创建一个函数都会创建一个作用域。

(4) 在创建函数时,将这些函数进行嵌套,它们的作用域也会嵌套,形成作用域链,子作用域可以访问父作用域,但是父作用域不能访问子作用域。

(5) 在执行一个函数时,如果我们需要查找某个变量值,那么会去这个函数被 定义 时所在的作用域链中查找,一旦找到需要的变量,就会停止向上查找。

(6) “变量的值由函数定义时的位置决定”这句话有歧义,准确说是查找变量时,是去定义这个函数时所在的作用域链查找。


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宇六岁鸭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值