穿越编程的魔法门:探索 JavaScript 词法作用域的奇妙世界

269 阅读5分钟

 前言

随着大众对浏览器需求的日益递增,浏览器的内核以及功能性也日渐强大,JavaScript在短短十多年的时间里从布兰登大叔某个不起眼的文件夹里,跃居成为了全球使用率第六的语言。其弱类型的特性,在减少鄙人这样的小白入门的痛苦的同时,也导致了一些不得不去重视的问题,其中作用域便是令鄙人数次头疼的问题之一

作用域

在JavaScript中,作用域可大致分为全局作用域和局部作用域,名字的区别就是它们作用范围的区别,拥有全局作用域的全局变量可在函数,循环或者对象中调用,而局部作用域只包含一部分,例如函数内或者是某个循环内,局部变量在全局中是不可被调用的,但倘若你一身反骨偏要把局部变量在全局调用,那么你的编译器也会一身反骨把not defined印在你的屏幕上。简单来说,全局变量就像是大街上的钱,当你在夜店里喊出那句“今晚全场消费由我买单”的时候,大街上的钱是可以被你捡去买单的。而局部变量是我口袋里的钱,你要是想拿我的钱去买单,那不好意思,警察叔叔会告诉你not defined。

词法作用域

世界之大,无奇不有,这句话放在JavaScript中也同样适用。就像是我的钱会被喊着九块九包邮的商家拿去买单,某些特殊情况下的‘局部变量’也会在全局可调用。

1. eval

我们先来看一段代码:

    function theEval(a,b) {
    eval(a)
    console.log('x='+x);
}

    theEval('var x = 12138',2)//输出:x=12138

 

很明显,无论是在theEval这个方法,还是全局中,我们都没有定义x,也没有给x赋值,但我们却可以得到x的值,这是因为“var x= 12138”这段字符串作为实参传给了方法中的形参a,而eval将形参a这段字符串转化为了一段JavaScript语句,相当与直接将var x= 12138写在了方法内,颇有一种魔法照进现实的感觉,这也就是为什么输出的是x的值,而不是比猴子腚还红的not defined

2. with

在JavaScript中,with语句用于创建一个临时的作用域,以便在其中可以更方便地访问对象的属性和方法。with的出现,在省去调用或修改对象属性的‘对象名.’同时,也为保住各位程序员头发做出一定的贡献,因此在初识with的一众小白心里,with的形象普遍都是清纯小可爱,但with确确实实有鲜为人知的另一面:

    var Obj={
    a:15
}
    with(Obj){
    a:16;
    e:45
}

Obj:我只有一个属性,你给我两个,什么意思?

  1.png 2.png

通常情况下,当我们给对象中不存在的键赋值时,对象会新增一个键值对,确保成功赋值,但在此处,通过with赋值的键值对并未进入到Obj中,说明并没有新增键值对,那么,e也就不存在了对吧,秉着不撞南墙不回头的想法,用实践出真知

     console.log('e='+e);//输出:e=45

Wait a minute,为什么出现了一个全局变量e?

这正是with鲜为人知的一面!

with:想不想要我的大键值对?

Obj:别!我已经满了!要溢出来!

是的,当通过with给对象中不存在的键赋值时,这个键值对会溢出,成为一个全局变量,而不是硬塞进对象中,从而导致全局出现了新的变量,所以,在使用with时,要特别注意对象是否有对应的键,毕竟,你也不想看见满屏幕都是比猴子腚还红的报错吧?

总结

当涉及到JavaScript的词法作用域时,我们必须小心使用witheval这两个功能。虽然它们在某些情况下可能会提供便利,但它们也带来了一些潜在的问题。

with语句可以简化代码,但它会改变作用域链的结构,可能导致代码可读性和性能问题。因此,当使用with语法时需要特别注意键值对数量和键名的匹配,避免出现键值对外泄的情况。

eval函数允许我们在运行时动态执行和评估字符串作为JavaScript代码。然而,它也带来了安全性和性能方面的考虑。不当使用eval函数可能会导致安全漏洞,并且它通常比其他替代方案更慢。

在编写JavaScript代码时,我们应该优先考虑使用更安全、可读性更好且性能更高的替代方案,而不是依赖于witheval。通过遵循最佳实践和了解词法作用域的工作原理,我们可以编写出更可靠和可维护的代码。

总而言之,了解witheval的用法是很重要的,但在实际开发中,我们应该谨慎使用它们,并确保在使用时考虑到潜在的问题和风险。通过合理的代码设计和选择适当的替代方案,我们可以编写出更高质量的JavaScript代码。