词法作用域的理解

文章介绍了词法作用域的概念,它是如何在编程时由变量和块的定位决定的。通过示例解释了作用域的层级结构和查找规则。同时,提到了JavaScript中两种可以欺骗词法作用域的方法:eval()和with,但它们因性能问题和潜在风险应避免使用。

一.词法作用域的理解

大部分标准语言编译器的第一个工作阶段叫作词法化(也叫单词化)。回忆一下,词法化的过程会对源代码中的字符进行检查,如果是有状态的解析过程,还会赋予单词语义。这个概念是理解词法作用域及其名称来历的基础。简单地说,词法作用域就是定义在词法阶段的作用域。换句话说,词法作用域是由你在写代码时将变量和块作用域写在哪里来决定的,因此当词法分析器处理代码时会保持作用域不变(大部分情况下是这样的)。
function foo(){
    var b  = a*2;
    function LogNums(c){
      console.log(a,b,c)//2,4,8
    };
    LogNums(b*2)
};
var a = 2;
foo();
  1. 在这个例子中全局作用域存在a变量,foo函数。

  1. 在foo里面的作用域中存在b变量,以及LogNums这个函数。

  1. 在LogNums中存在c这个参数。

  1. 可以发现作用域是逐级向里面包含的,在这个例子中最里面的LogNums可以访问自己作用域的标识符,也可以访问外面的标识符,词法作用域完全由写代码期间函数所声明的位置来定义。

  1. 作用域的查找是如果在当前的作用域没有查找到这个变量,那么会去外层作用域进行查找,直到找到为止,如果我们希望查找到同名变量,但是之前声明的同名变量值不变,可以在内部的作用域使用"遮蔽效应",创建了同名变量去不在让作用域向外面去进行寻找。

  1. 如果我们想去在使用"遮蔽效应"的同时去找到之前在全局作用域中什么的变量,可以使用window.变量名的方法来实现,不是全局作用域的变量则不行,因为只有全局作用域声明的变量会绑定到浏览器提供的window对象上面。

二.欺骗词法

  1. 概念:如果说词法作用域完全由写代码期间函数所声明的位置来定义,那么我们要怎样去修改词法作用域呢,别急,js中有可以实现这个功能的东西,下面听我介绍两个。

  1. eval()可以实现,eval会去接收一个字符串作为参数,如果其中有声明,或者是要声明的函数,那么就会去动态的修改所在的这个词法作用域,可以这样理解,eval()中传递参数声明的变量,和直接声明的并没明显区别,唯一的缺点就是性能(下面会说)。

function foo(str, a) {
    eval( str ); // 欺骗!
    console.log( a, name );
}
var name = 'nike';
foo( "var name = "mike";", 1 ); // 1, 3

在这段代码中我们声明了一个foo函数,在函数里面传递了,str和a两个参数,str就是b的什么区别是里面有引号,以供eval使用,经过eval的作用,我们在foo里面又声明了name,遮盖全局作用域的name,这样一看是不是改变了,是,但是eval成也这点败也这点,因为一开始在eval改变作用域前,他只能解析到foo函数作用域和全局作用域的name,eval调用后,在foo作用域中增加了在foo中声明的name, 改变了作用域,重新解析,致使其性能不好。

  1. with,with通常用于去引用一个对象,接受这个对象作为参数,在with函数中进行的声明操作都被视为在给with传递的对象修改属性。

var obj = {
    a: 1,
    b: 2,
    c: 3
};
obj.a = 2;
obj.b = 3;
obj.c = 4;
with (obj) {
    a = 3;
    b = 4;
    c = 5;
}

缺点:with传递的对象如果没有操作的属性,就会挂载到全局作用域中,并且使用with,运行速度会慢,原理是使用with我就不能区分你这是全局变量还是局部变量,作用域两个都查一遍才行,而不是像静态作用域一样,在局部找到了,就不去往上查了。

function foo(obj) {
    with (obj) {
        a = 2;
    }
}
var o1 = {
    a: 3
};
var o2 = {
    b: 3
};
foo( o1 );
console.log( o1.a ); // 2
foo( o2 );
console.log( o2.a ); // undefined
console.log( a ); // 2

三.总结

eval和with在2023年你应该避免使用这两个祸害,共同缺点都是性能不好,给setTimeout和 setInterval传递第一个参数,可以起到和eval一样的效果,new Function也是,其传递的最后一个参数也是和eval一样的。在严格模式下,with被完全砍掉其作用,eval没那么严重,但也"残血"了,足以可以看出其危害。

本篇对标于你不知道的JavaScript上卷第一部分第二章,是作者整理的笔记,例子基本都是采用书中的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值