4,函数作用域中可用arguments来获取函数实参,arguments虽然可以通过下标访问,但它不是数组,原型不是Array.prototype. 它是实参的一个副本,通过arguments可模拟方法的重载。
function add(){ if(arguments.length == 1){ alert(1); } if(arguments.length == 2){ alert(2); } if(arguments.length == 3){ alert(3); } } add(1); add(1,2); add(1,2,3);
5,作用域:
全局,函数,eval。js没有块级作用域。
var local0=3; function outer2(){ var local2 = 1; function outer1(){ var local1 =2; //这里能访问local1,local2,local3. } } ////////newFunction比较特殊//////// function outer(){ var i = 0; var func = new Function("console.log(i)") func(); } outer(); UncaughtReferenceError: i is not defined
利用函数作用域,类库的经常写法是立即执行函数表达式。里面声明的变量是局部的,不会造成全局污染。
6,作用域的好处是内部函数可以访问定义它们的外部函数的参数和变量(this,arguments除外),而且内部函数拥有比它外部函数更长的生命周期,这个内部函数就是一个闭包。
7,闭包:一个内部函数能自由访问父函数的参数与变量,通过函数字面量创建的函数对象包含一个连到外部上下文的连接,这被称为闭包。
function outer(){ var a =1; return function(){ return a; } } var func = outer(); // 变量a没有被释放 func(); //即使外函数执行完成仍然可访问其变量 闭包能实现更好的封装,利用getUsername访问私有变量。 (function(){ var username= "zhang"; var output ={}; output.getUsername = function(){ return username; } window.output = output; }()); output.getUsername(); "zhang" output.username; undefined
闭包会导致一个作用域环境在函数调用完成后仍然不能释放----空间浪费,甚至内存泄漏
用闭包保存状态
和普通function执行的时候传参数一样,自执行的函数表达式也可以这么传参,因为闭包直接可以引用传入的这些参数,利用这些被lock住的传入参数,自执行函数表达式可以有效地保存状态。
// 这个代码是错误的,因为变量i从来就没背locked住 // 相反,当循环执行以后,我们在点击的时候i才获得数值 // 因为这个时候i才真正获得值 // 所以说无论点击那个连接,最终显示的都是I am link #10(如果有10个a元素的话) var elems =document.getElementsByTagName('a'); for (var i = 0; i< elems.length; i++) { elems[i].addEventListener('click', function(e) { alert('I am link #' + i); }, 'false'); } //这个是可以用的,因为他在自执行函数表达式闭包内部 // i的值作为locked的索引存在,在循环执行结束以后,尽管最后i的值变成了a元素总数(例如10) // 但闭包内部的lockedInIndex值是没有改变,因为他已经执行完毕了 // 所以当点击连接的时候,结果是正确的 var elems =document.getElementsByTagName('a'); for (var i = 0; i< elems.length; i++) { (function(lockedInIndex) { elems[i].addEventListener('click', function(e) { alert('I am link #' +lockedInIndex); }, 'false'); })(i); } //你也可以像下面这样应用,在处理函数那里使用自执行函数表达式 // 而不是在addEventListener外部 // 但是相对来说,上面的代码更具可读性 var elems =document.getElementsByTagName('a'); for(var i = 0; i< elems.length; i++) { elems[i].addEventListener('click', ( function(lockedInIndex) { return function(e) { e.preventDefault(); alert('I am link #' +lockedInIndex); }; })(i), 'false'); }
其实,上面2个例子里的lockedInIndex变量,也可以换成i,因为和外面的i不在一个作用域,所以不会出现问题,这也是匿名函数+闭包的威力。
bind方法(ES5,绑定this,柯里化):
8,柯里化:
functionadd(a,b,c){ return a+b+c; } var func=add.bind(undefined,100); func(1,2) //103 var func2 =func.bind(undefined,200); func2(10); //310
9,执行上下文
变量对象:VO 抽象概念,不是js中的变量。用于存储执行上下文中的变量,函数声明,函数参数。
函数每执行一次都会产生一个执行上下文,一个VO,全局作用域也有一个执行上下文与VO。
VO是个抽象的概念,并不能访问,但是浏览器对全局VO设置了一个属性window指向VO本身。所以window.window.window….===window
函数的执行分为:变量初始化阶段与执行阶段。
变量初始化阶段时VO按照如下顺序填充
1,函数参数(若未传入,初始化该参数值为undefined)
2,函数声明(若发生命名冲突,会覆盖)
3,变量声明(初始化变量值为undefined,若发生命名冲突,会忽略)
函数表达式不会影响VO,这解释了为什么不能使用命名函数表达式的名字访问。
alert(x); //function var x = 10; alert(x); //10 x=20; function x(){}; alert(x); //20 if(true){ var a = 1; }else{ var b = true; } alert(a); //1 alert(b); //undefined
执行上下文是一个栈。