执行环境
当执行流执行到函数时会创建一个执行环境,这个执行环境包含了函数内部 语句可以访问的所有变量和函数,当代码执行完时,销毁执行环境。所以一般情 况下,局部变量在函数执行完时会被销毁。
作用域、调用对象
很多人认为作用域是在函数执行时创建的,这是有偏差的理解!
作用域分词法作用域和动态作用域:
- 词法作用域是在函数定义的时候创建的,作用域的本质是创建它的外层函数的调用对象组成的对象链,函数内部属性[[scope]]指向此作用域。
- 当调用函数时,会创建一个调用对象(有些地方称活动对象),这个调用对象保存了函数参数和局部变量。将此调用对象推入词法作用域的前端,因此执行时作用域发生了变化,称为动态作用域。
实质上作用域只有一个,都是内部属性[[scope]],词法作用域和动态作用域是时间上的不同造成的划分。作用域链是一条对象链,函数自己的活动对象,接着是父函数的活动对象,接着是祖父函数的活动对象。。。。 函数执行时,是沿着作用域链去寻找标识符的值的,先从自己的活动对象开始。 with、catch 会改变动态作用域,将with的对象和catch的对象压入作用域链前端。
下面出个例子看你是否对作用域链理解到位了:
var obj = {a:1,b:2}; var fn = function(){ var c=3; var a = 5; console.log(a); //a等于多少? with(obj){ a=6; return function(){return a+c;}; } }(); fn(); //结果是多少?
闭包
啥是闭包?
官方:所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
民间:内部函数拥有外部函数的环境。
通俗的就是内部函数可以访问外部函数的变量。
形成机理:作用域链。
内存及变量查找效率
当有闭包,且内部函数赋给了外部变量引用时,要特别注意内存 。没有赋给外部变量时,代码执行完后执行环境销毁,不会有变量贮存内存。但赋给了外部变量时,闭包的词法作用域链会持有外层函数的活动对象,使得外部的变量不会回收。为了有效回收应该将变量设为null,断开引用。
var fn = function(){ var div = document.getElementById("div"); return function(){}; }; //div不会销毁
根据作用域链的原理,处于作用链前端的变量会更快找到,所以尽量用局部变量。
1 var a,b,c; 2 var fn = function(){ 3 var d,e,f; 4 return function(){ 5 var h,j,k; 6 return typeof nothing; //nothing这个变量查找了整条作用域链,直到查询到window中这个变量,才返回"undefined". 7 }; 8 };
this
this跟arguments一样是函数执行时,活动对象的一部分。this是动态的,函数执行时候绑定。
大概有这几种情况:
(1) 函数,this==window
var fn = function(){console.log(this==window);}; //true fn(); //不管函数fn在任何地方定义,是顶层函数,还是嵌套 ,this都等于window
(2) 方法,this==obj
var obj = {}; obj.fn = function(){console.log(this==obj);}; //true obj.fn();
(3)setTimeout setInterval,this==window
var obj = {}; var fn = function(){console.log(this==obj);}; setTimeout(fn,1000); //? obj.fn = fn; setTimeout(obj.fn,1000); //?
(4)call,apply将函数or方法绑定给了对象,this==obj
var obj = {}; var fn = function(){console.log(this==obj);}; //true fn(); fn.call(obj);
(5)事件处理程序
DOM0
btn.onclick = function(){console.log(this==btn);}; //true
DOM2
btn.addEventListener("click",function(){ console.log(this==btn); //true },false);
IE
btn.attachEvent("onclick",function(){ console.log(window==btn); //true });