一 闭包和作用域
闭包是一个受到保护的变量空间,由内嵌函数生成。JavaScript具有函数级的作用域。这意外着定义在函数内部的变量在函数外部不能被访问。JavaScript的作用域又是词法性质的。这意味着函数运行在定义它的作用域中,而不是在调用它的作用域中。把这两个因素结合起来,就能通过把变量包裹在匿名函数中而对其加以保护。(引自《JavaScript设计模式》)
var i = 100;
function makeCounter() {
var i = 0; // makeCounter的私有变量
return function() { console.log(++i); } //该函数运行的环境是makeCounter的作用域
}
var counter = makeCounter(); // counter即makeCounter的内嵌函数,运行时获取的i来源于makeCounter作用域中的i
counter(); // 输出: 1 而非 101 –>因为makeCounter返回的内嵌函数能获取i,所以也称为makeCounter的特权函数。
counter(); // 输出: 2
i; // 输出: 100 –>说明makeCounter内的i不能被调用
二 () 调用函数
(1)function foo() {} 和 var foo = function() {},foo都是这里function(){}的引用,调用时只需要直接foo()。但如果直接将()放在function(){}后面则会触发语法错误。
function() {..}() // SyntaxError: Unexpected token(
原因:当解析器遇到function关键字时,默认将其视为一个函数声明(function declaration),而不是一个函数表达式(function expression)。如果不显式告诉解析器将其视为一个函数表达式,则会视其为一个函数声明缺少名字的语法错误。
(2)针对上面语法错误,给函数加个名称并加()执行,会触发另外一个语法错误:
function foo() {….} () ; // SyntaxError: Unexpected token )
原因:()放在表达式后面表示这个表达式是个函数并将要被调用,而()放在statement(函数声明也是个statement)后面则是用来与前一个statement分隔,仅仅是一个分组运算符(用在控制运算优先级的,即: 1 + 2 * ( 3 + 4 )这里括号的作用 )
修正:()作为分组运算符时,需要包含一个表达式,所以简单加个表达式 (1)。
function foo() {…}(1) ; //不会报错了,这样的写法其实等同于下面
function foo() {…} // 所以foo在这里并没有被调用,而仅仅是个声明而已。
(1)
(3)上面的修正只是解决了语法错误,但实际上并没有达到我们的期望:让函数立即执行。最常用的方法是将其包在()内。因为在JavaScript中,()只能包含表达式而不能是语句statement。
(function() {..}()) 或者 (function() {..})()
(4)其他方法:原理都是令解析器认识到()前是一个表达式
var x = function() { return 1; }();
true && function() { .. } ();
0, function() {..} ()
! function() {..} ()
~ function() {..} ()
+ function() {..} ()
– function() {..} ()
三 利用闭包保存状态
(1)下面例子给元素添加事件监听,结果每个a点击后输出的都是elmes数组的长度。
原因:for执行后的每个elems的点击事件函数中绑定的是 j ,当点击时输出的 j 确确实实就是 j 在for运行之后的值。
var elems = document.getElementsByTagName(‘a’);
for (var j = 0; j < elems.length; j++) {
elems[j].addEventListener(‘click’, function(e) {
e.preventDefault();
console.log (j);
}, false);
}
(2)下面例子利用立执行函数,将 j 锁定。
for执行完后 j 虽然已经变成elems数组的长度,但是匿名函数每次立即执行的时候传入的参数是相应的 j
var elems = document.getElementsByTagName(‘a’);
for (var j = 0; j < elems.length; j++) {
(function( lockedIndex ) {
elems[j].addEventListener(‘click’, function(e) {
e.preventDefault();
console.log(lockedIndex);
}, false);
})(j);
}
四 函数定义内调用本身
function foo() { foo(); }
var foo = function() { arguments.callee(); } // arguments.callee 在ECMAScript5中被废弃了。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。