做用域是理解JS的关键所在,一样做用域关系到性能。其实主要仍是标识符的解析会影响到性能。而咱们主要是从特别细微的地方去分析做用域的性能问题。 可能有一些地方的优化在引擎不断优化的状况下已经成效渐微,可是我以为仍是有必要去从根源了解为何咱们要这么作,javascript
追根溯源前端
咱们如下面这个函数为例java
function add(num1, num2) {
var sum = num1 + num2;
return sum;
}
var total = add(5, 10);
复制代码
咱们先来看一下在执行add函数的时候的做用域链 闭包
标识符解析: 当函数在执行的时候,每遇到一个变量都会去搜索执行环境的做用域链,查找同名的标识符,且搜索过程是从做用域链的头部开始。搜索的时候先以当前运行函数的活动对象开始一直到做用域链的最后,若是搜索到则使用这个标识符对应的变量,没有则为视为标识符未定义。 因此一个标识符所在的位置越深,它的读写速度越慢,因为全局变量老是存在于执行环境做用域链的最末端。因此函数中局部变量 > 全局变量。函数
other: 当名字相同的两个变量在做用域链的不一样部分的时候,那么标识符则为最早找到的那个性能
var a = 1;
function test(){
var a = 2;
return a;
}
test(); // a = 2
复制代码
最佳实践优化
funtion init(){
var doc = document,
bd = doc.body;
doc.getElementById('test').onclick = function(){}
bd.className = '';
}
复制代码
问:既然做用域对性能有影响,那咱们有什么办法去临时改变做用域链么? 答案是有的,JS中with和try-catch是能够临时改变做用域链的。ui
疑惑spa
追根溯源3d
咱们仍是要找个例子,好比:
funtion init(){
with(document){
var bd = body,
links = getElementsByTagName('a');
getElementById('test').onclick = function(){}
bd.className = '';
}
}
复制代码
咱们把前面的init函数改造了一下,当执行到with语句的时候,执行环境的做用域链被临时改变了,一个新的活动对象被建立,它包含了参数指定的对象的全部属性,并将新的活动对象推入做用域链的首位(这就解决了咱们的第一点疑惑,因此定义with语句的目的主要是简化屡次写同一个对象的工做)。如图:
try-catch一样在try代码块中发生错误的时候,会自动跳转到catch,其将异常对象推入做用域链,并将其置于做用域链的首位。同with。
最佳实践
try{
init()
} catch(error) {
handleError(error)
}
复制代码
因为只有一条语句,且没有对局部变量的访问,因此做用域链的临时改动并不会影响性能。
问:函数的闭包也依赖做用域,那闭包有没有性能问题? 答案:有的
追根溯源
再来个闭包例子:
function assignEvents() {
var id = 'xdi9592';
document.getElementById('save-btn').onclick = function(event) {
saveDocument(id);
}
}
复制代码
当执行assignEvents函数时,就会建立包含当前环境的第一个活动对象,而后再就是全局的一个活动对象。当闭包被建立的时候,它的[[scope]]属性被初始化为前面的两个活动对象。以下图:
最佳实践
参考资料:
《高性能JS》