JS的底层小知识(一):作用域

167 阅读5分钟

前言

你有你的家,我有我的家,该划分好划分好。就像我们生活中,每个人家里都有各自的房间,大家知道哪些地方是自己的,这样就不会乱闯乱动别人的私人物品。编程的时候也有类似的规则,叫做 “作用域”。它就像给电脑里的代码分配房间或者说是领土,让它们知道哪里是自己的活动区域,哪里是自己不能去的地方。就好比,台湾永远是中国的一样,其他国家不要多bb。这样一来,不同的代码块知道自己该用哪些变量(就像家里的东西),不会混淆,这样程序运行起来就更有序、更顺利了。

注意你的言辞.gif

编译三部曲

  • 词法分析:也就是分词。这就好像是一位细致的图书管理员,将一堆书本(源代码)分类成一个个可管理的书架。它分辨出字符串、数字、关键字等。比如说:var a =123 ,编译器就会识别四个东西 : var ,a, =,123(分词)。
  • 解析:在书本被分类后,就要开始解析了,这好比将单词组成句子,理解其的含义。解析每个东西的作用 ,弄清楚是干嘛的(解析)。
  • 生成代码:最后,转换为机器可理解的语言。生成代码 var a = 123

代码的领土划分

  • 全局作用域:相当于一个广阔无垠的公共空间,所有变量在此皆可见,就好像地球一样,所有国家都在这里。

  • 函数作用域:比做成一个国家吧,函数内的变量只在自己国家的范围内生效,保护了数据的隐私,避免了不必要的干扰,总不能让其他国家窥探我国机密吧。

例如:

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

全局作用域中就包括以下:

var a=1
function foo() {}
foo();

函数作用域就包括以下:

var a=2
console.log(a)
  • 块级作用域ES6新增的领域,由 {} 围起的小天地,letconst 守护着其中的秘密,它们共同守护着局部数据的纯净,确保变量的生命期仅限于所属的代码块内。不让他人伸脚,自己也不伸手,就好像是闭关锁国一样。

例如:

let c = 5
{
    let c = 2
    console.log(c)
}
console.log(c)

第一个输出c的值就是2,第二个输出c的值就是5,因为在块级作用域,{}内的只能访问{}内的,{}外的只能访问{}外的

作用域法则:内外有别

内部作用域可以访问外部的作用域,反之则不行

如同国界,内部作用域可以自由探索地球的风景,而外界却无法窥视内部的奥秘。

例如:

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

这个输出的是1

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

而这个只能报错!!

词法作用域

简单来说,就是在写代码的时候就能确定的变量作用范围。想象一下,当你写程序时,每个变量就像是被分配了一个固定的“家”,这个“家”就是它的作用域。不论程序运行到哪里,变量都会记住它出生时所在的“家”。

更技术一点讲,词法作用域是在代码编写阶段,由代码的结构静态决定的。也就是说,变量在哪里定义,它就在哪里以及它内部的区域可用,而不会受到运行时调用位置的影响。比如,如果一个变量在函数体内定义,那么这个变量就只在这个函数体内部可见,出了这个函数体,变量就好像不存在一样。

欺骗词法作用域

  • eval():让原本不属于这里的代码,变得好像天生就定义在了这里一样,例如:
function foo(a,str) {
    eval(str);
    console.log(a,b);
}
foo(1,'var b=2') ;

注意:这里调用foo函数的时候传入函数的var b =2是字符串

那么如果没有eval(),输出的肯定是1,'var b=2',但是当eval() 出现,那么输出的就是1,2,神奇吧

  • with():一旦修改对象中不存在的属性时,这个属性会泄露到全局,变成全局变量,例如:
var obj ={
    a:1,
    b:2 ,
    c:3
}
with(obj) {
    a=3
    b=4
    c=5
    d=6
}
console.log(obj)
console.log(d)

console.log(obj)输出的值中没有d,所以d会跑到全局作用域去,变成全局变量,在全局中输出d的话就能输出结果6。怎么打个比方呢,就好像你去拿快递,你有四个快递,但是你拿到了五个,那你肯定只能拆开四个啊,另一个得还到快递站吧

var 与 let 的对决

  • var:声明的变量存在声明提升,在全局声明的变量会被添加到window对象上
  • let:而它拒绝提升,严格遵循块级作用域的规则,更为安全可靠。
var  a = 1 
{
    console.log(c)
    var c = 2
}

这个输出undefined

let c = 1 
{
    console.log(c)//暂时性死区
    let c = 2
}

这样就会报错,访问不到外面的c

总结

分好场地,划好线,不是自己的东西就别硬要去调用,不然会给你狠狠地报错哦!!!

是自己的永远是自己的,就好像台湾永远是中国不可分割的一部分一样。

弹射下班.gif

写得不好的地方以及需要修改的地方,欢迎大佬亲临指正昂💞