函数声明:function 函数名称 (参数:可选){ 函数体 }javascript
函数表达式:function 函数名称(可选)(参数:可选){ 函数体 }
函数声明只能出如今程序或函数体内。从语法上讲,它们不能出如今Block(块)({ ... })中。表达式和声明存在着十分微妙的差异,函数声明会在任何表达式被解析和求值以前先被解析和求值。不管变量在函数内何处声明,在代码执行阶段,变量都会在提到函数开始执行以前被声明,而函数声明发生在更早的阶段。java
var foo; if (true) { foo = function (){ console.log(1); }; }else { foo = function (){ console.log(2); }; } foo(); // 1 先声明foo变量,在代码处理的第一步,按照程序执行顺序,建立函数表达式并赋值给foo。 if (true) { function foo(){ console.log(1); } }else { function foo(){ console.log(2); } } foo(); // 2 基于Gecko的浏览器 //1 函数声明在代码处理的第一步。function foo(){ console.log(2); }会覆盖前面的操做。 alert(sum(10,10)); //20 function sum(num1, num2){ return num1 + num2; } alert(sum(10,10)); //causes an error var sum = function(num1, num2){ return num1 + num2; }; //变量sum的声明仍是放在第一步处理
命名函数表达式:var bar = function foo(){};须要注意的地方:这个名字只在新定义的函数做用域内有效(IE9之前版本的IE版本中)。命名函数表达式方便调试。浏览器
var f = function foo(){ return typeof foo; // foo是在内部做用域内有效 }; // foo在外部用因而不可见的 typeof foo; // "undefined" IE5-IE8中 " function" f(); // "function"
方法调用模式
当一个函数保存为对象的一个属性时,咱们称它为方法。当一个方法被调用的时候,this被绑定到该对象。若是调用表达式包含一个提取属性的动做xxx.xxx,那么它就是被当作方法来调用的。安全
函数调用模式
this被绑定到全局对象。闭包
构造器调用模式
this被绑定到建立的新对象。app
apply调用模式
apply容许咱们选择this的值,方法接受两个参数,第一个是要绑定this的值,第二个是参数数值。第一个参数为null,undefined将会被替换成全局对象。严格模式use strict的时候不会被替换。函数
不少现代语言都推荐延迟声明变量,js中推荐在函数体的顶部声明函数中可能用到的全部变量。
JScript的Bugthis
例1:函数表达式的标示符泄露到外部做用域 typeof g; // "function" var f = function g(){}; IE9已经修复 例2:将命名函数表达式同时看成函数声明和函数表达式 typeof g; // "function" var f = function g(){};
函数声明会优先于任何表达式被解析,上面的例子展现的是JScript其实是把命名函数表达式当成函数声明了,由于它在实际声明以前就解析了ges5
匿名函数中this 通常指向window对象
XXXX.prototype.__XXX = function(){} 不是匿名函数prototype
在编写递归函数时,使用arguments.callee (可是es5严格模式下会报错)比使用函数名安全。
函数中的变量对象就是活动对象。
不管何时在函数内部访问一个变量时,就会从做用域链中搜索相应变量。通常来说,当函数执行完毕后局部活动对象将被销毁。可是闭包状况有所不一样。
在createComparisonFunction()函数内部定义的匿名函数的做用域链中,不包含外部函数createComparisonFunction()的活动对象。
var compare = createComparisonFunction("name");
var result = compare(obj1, obj2);
createComparisonFunction()在执行完毕之后,其执行环境的做用域链被销毁,可是其活动对象不会被销毁,由于匿名函数的做用域链中仍然引用着这个活动对象,直到匿名函数执行完毕后,才一块儿被销毁。闭包会携带包含他的函数的做用域,占用内存较多。
闭包只能取得包含函数中变量最后保存的值。 function createFunctions() { var result = new Array(); for (var i = 0; i < 10; i++) { result[i] = function() { return i; }; } return result; } var funcs = createFunctions(); //every function outputs 10 for (var i = 0; i < funcs.length; i++) { document.write(funcs[i]() + "<br />"); } 使用匿名函数再添加一层外部函数,多了一个闭包使原来的闭包知足条件 function createFunctions() { var result = new Array(); for (var i = 0; i < 10; i++) { result[i] = function(num) { return function() { return num; }; }(i); } return result; } var funcs = createFunctions(); //every function outputs 10 for (var i = 0; i < funcs.length; i++) { document.write(funcs[i]() + "<br />"); }
闭包直接能够引用传入的参数,利用这些被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) { e.preventDefault(); 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) { e.preventDefault(); 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'); }
利用了闭包的特性,能够避免全局变量
javascript语法中()内部不能包含语句,只能包含表达式。 (function () { /* code */ } ()); // 推荐使用这个