简单了解JavaScript中的作用域

708 阅读5分钟

目录

1. 作用域是什么

2. 全局作用域和函数作用域

3. 块级作用域

4. 欺骗词法作用域

1.作用域是什么

几乎所有编程语言最基本的功能之一,就是能够储存变量中的值,并且能够在之后对这个值进行访问或修改。事实上,正是这种能够储存和访问变量的值的能力将变量引入了程序中

然而将变量引入程序后,这些变量将要放在哪里?当程序需要时如何找到它们?因此我们需要在程序中有一样东西来存储变量,而这就是作用域。

在JavaScript中可以将作用域简单的分为:

全局作用域、函数作用域和块级作用域

通常将JavaScript是一种“动态”或者“解释执行”语言,但事实上它是一门编译语言,并且它还是一种“弱语言”。它在编译 (代码执行之前所经历的步骤) 时不会确定变量的类型,而是通过赋的值来确定变量的类型。

2.全局作用域和函数作用域

全局作用域:全局作用域是整个 JavaScript 程序的最外层作用域,包括了所有其他作用域

函数作用域:函数作用域是在函数内部创建的作用域,其内部的变量和函数对外不可见

如下列代码所示

var a=1 //此时a为全局变量
function foo(){
    var a=2
}
foo()
console.log(a)

当代码开始运行时,引擎会先进行编译,编译是由上而下进行的,因此此时的结果是1,而为何不是2,则是因为

变量的查找会从自己的作用域开始查找,找不到再去外层找,不能从外部作用域往内部作用域查找 此时console.log()可以在自己的作用域中找到a,因此输出结果应该为1

又如:

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

此时的输出结果是2,因此引擎在编译时,变量的查找是由内而外的

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

此时将会显示“b is not defined”,可以看出在从函数外访问函数内的变量是不行的

提升

在JavaScript中,提升这一概念也是较为重要的

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

此时代码输出结果为undefined,而不是报错。因此说明a只是没有赋值的, 而这是因为引擎在编译时只执行了 “var a” 告诉引擎这里定义了一个变量a,而引擎执行过程中是由上而下的,所以此时a是没有赋值的,这就导致了引擎只知道有a这个变量,而不知道a的值是多少,所以就返回了undefined。这种现象就是提升。

3.块级作用域

任何一对花括号{}中的语句集都属于一个块,在这之中定义的所有变量在代码块外都是不可见的,我们称之为块级作用域。

块级作用域主要使用let和const两个关键词

let:

(1)对于var的提升来说,在let中这是不被允许的

if(true){
    console.log(a); // 此时会形成暂时性死区
    let a=2
}

(2)对于let来说,在同一作用域中不能声明重复的变量,否则会报错 (3)let能与{}形成块级作用域

if(1){
    let a=1
} //形成了块级作用域

console.log(a); //此时代码会报错

const:

在const中它与let的用法基本一致,都有暂时性死区,并不能再同一作用域中声明重复的变量,也能与{}产生块级作用域

但是const声明的是常量,声明的同时必须赋值,并且它的值在之后是不能被修改的,一旦修改就会引起报错

4.欺骗词法作用域

词法作用域并不是另一个作用域而是指就是当该代码书写的位置在哪,那么它的作用域也就在哪

欺骗词法作用域也就是绕过了正常的词法作用域规则

eval:

JavaScript中的eval()函数可以接受一个字符串作为参数,并将其中的内容视为好像在书写时就存在于程序中这个位置的代码。

如下列代码:

function foo(str){
    eval(str); // 欺骗
    var a=1
    console.log(a,b);
}
foo('var b=2')

在执行eval()函数时,引擎并不知道前面的代码是以动态形式插入进来的,并对词法作用域的环境进行修改。因此这里的输出结果为1,2

with:

with通常被当做重复引用同一个对象的多个属性的快捷方式,可以不需要重复引用对象本身。 比如:

var obj={
    a:1,
    b:2,
    c:3,
}
//单调的重复“obj”
obj.a=2;
obj.b=3;
obj.c=4;
//简单的快捷方式
with(obj){
    a=2,
    b=3,
    c=4
}

事实上这不仅仅是为了方便的访问对象属性。 比如:

function foo(obj){
    with(obj){
        a=2
    }
}
var o1={
    a:3
};
var o2={
    b:3
};
foo(o2);
console.log(o2,a); // undefined
console.log(a);// 此时a被泄漏到全局作用域上了

with可以将一个没有或有多个属性的对象处理为一个完全隔离的词法作用域,因此这个对象的属性也会被处理为定义在这个作用域中的词法标识符 (变量) 因此a就会被泄漏到全局作用域上