JavaScript作用域与作用域链深度解析及优化策略
作者:缓存笔记时间:2026-10-24 22:16:52浏览:231
本文深入解析JavaScript的全局与局部作用域机制,详细阐述作用域链的形成原理及变量查找过程。重点讨论如何通过局部变量优化全局访问性能,并分析with语法和catch块对作用域链的临时改变及其对性能的影响,帮助开发者编写更高效、规范的代码。
一、javascript的作用域
1、全局作用域
1、最外层函数和最外层函数定义的变量
var age = 20
function func1() {
var sex = "男"
function func2() {
console.log("hello func2")
}
func2()
}
console.log(age) //20
console.log(func1) //正常执行
console.log(func2) //报错
console.log(sex) //报错
2、所有未直接声明的变量,直接赋值为全局变量
function func1() {
var age = 20
sex = "男"
}
func1()
console.log(sex) //男
console.log(age) //报错
3、window对象上面的属性具有全局作用域
function func1() {
var age = 20
sex = "男"
console.log(top) //window....
}
func1()
console.log(sex) //"男"
console.log(top) //window....
在JavaScript中,全局作用域指的是最外层函数以及最外层函数中定义的变量。例如,在代码中,变量age定义在最外层,因此它具有全局作用域,可以在任何地方访问。而函数func2定义在func1内部,因此它不具备全局作用域,只能在func1内部访问。
和全局作用域相反,局部作用域只在函数内部可以访问到。function func1() { var age = 20 func1() function func1() { console.log("func1") }}func1()和全局作用域相反,局部作用域只在函数内部可以访问到。
function func1() {
var age = 20
func1()
function func1() {
console.log("func1")
}
}
func1()
2、局部作用域
函数也是对象,在函数内部存在一个属性[[scope]],该属性包含可以访问属性的集合。决定了哪些属性在函数中可以访问到。
下面我们以一个函数的例子来详细解说一下,函数作用域链。
1、在函数函数创建出来时。代码如下所示
function add(num1, num2) {
return num1 + num2
}

函数初始化时,会将自己的作用域链中放入全局变量
var total = add(10, 20)
这里是函数执行时,当执行时会创建一个新的对象放入作用域链中,这个对象中包括
arguments, 形参,this,以及返回值。

active object是活跃对象,是函数执行时创建的对象,然后scope chain类似于栈结构,函数执行前压入栈中,函数执行结束就从栈中弹出。函数访问属性的过程就是沿着scope chain从上往下一次查找。
与全局作用域相反,局部作用域仅在函数内部有效。如代码所示,变量age和函数func1都定义在func1函数内部,因此它们只能在func1函数内部访问。如果尝试在函数外部访问这些变量或函数,将会导致错误。
从上面的例子中,我们可以看出,访问全局作用域是最慢的,因为需要依次从上往下进行查找,应当尽可能少的使用全局变量,应该尽可能使用局部变量。如果在函数
中,使用多次全局变量,我们可以将全局变量转化为局部变量,然后在使用局部变量。
function changeColor(){
document.getElementById("btnChange").onclick=function(){
document.getElementById("targetCanvas").style.backgroundColor="red";
};
}
上面代码我们使用了两次document,但是document作为全局变量,此时我们应该将其转化为局部变量来使用,所以下面为转化后的代码。
function changeColor(){
var doc=document;
doc.getElementById("btnChange").onclick=function(){
doc.getElementById("targetCanvas").style.backgroundColor="red";
};
}
二、javascript的作用域链
在JavaScript中,函数也是对象,在函数内部存在一个属性[[scope]],该属性包含可以访问属性的集合。决定了哪些属性在函数中可以访问到。如代码所示,函数add有一个属性[[scope]],该属性包含全局变量和局部变量。
with语法的作用就是为了解决代码重写问题,是对象快捷书写方式,但是这么好的方式,为什么不大力推广使用呢?因为性能存在一些问题。function initUI(){ with(document){ var bd=body, links=getElementsByTagName("a"), i=0, len=links.length; while(i < len){ update(links[i++]); } getElementById("btnInit").onclick=function(){ doSomething(); }; }}这里使用with语法省略了document。with语法的作用就是为了解决代码重写问题,是对象快捷书写方式,但是这么好的方式,为什么不大力推广使用呢?
因为性能存在一些问题。
function initUI(){
with(document){
var bd=body,
links=getElementsByTagName("a"),
i=0,
len=links.length;
while(i < len){
update(links[i++]);
}
getElementById("btnInit").onclick=function(){
doSomething();
};
}
}
这里使用with语法省略了document。

with传入的对象的属性放入最上层,剩余的都往下压,导致局部变量的访问代价增大,所以with的性能不好。
下面我们以一个函数的例子来详细解说一下,函数作用域链。如代码所示,函数add有一个属性[[scope]],该属性包含全局变量和局部变量。当函数执行时,会创建一个新的对象放入作用域链中,这个对象中包括arguments, 形参,this,以及返回值。如代码所示,函数执行时,会创建一个新的对象放入作用域链中,这个对象中包括arguments, 形参,this,以及返回值。
我们在使用try--catch时,当代码执行错误时,会执行catch函数,catch函数中参数是错误对象,就是这个错误对象,会放到作用域链的头部。
但是try--catch我们在必要的使用得使用,我们可以这样解决。
try{
doSomething();
}catch(ex){
alert(ex.message); //作用域链在此处改变
}
处理后:
try{
doSomething();
}catch(ex){
handleError(ex); //委托给处理器方法
}
解决方案是:将catch错误处理交给另外一个函数进行处理。
三、作用域链和优化
active object是活跃对象,是函数执行时创建的对象,然后scope chain类似于栈结构,函数执行前压入栈中,函数执行结束就从栈中弹出。函数访问属性的过程就是沿着scope chain从上往下一次查找。如代码所示,函数执行时,会创建一个新的对象放入作用域链中,这个对象中包括arguments, 形参,this,以及返回值。
作者最新文章
为什么JS调用reload后页面没有立即更新
2026-11-05 13:38:29
crypto-js 加密结果与 Java/Python 后端不一致怎么解决
2026-11-05 12:09:48
js文件缓存导致代码不更新怎么强制刷新
2026-11-04 22:30:24
JS逆向调试技巧:编写Hook脚本监控加密函数输入输出
2026-11-04 10:42:46
Babylon.js 性能优化技巧:实例化网格与遮挡剔除
2026-11-03 23:43:22
