函数表达式和函数声明
函数声明:function 函数名称 (参数:可选){ 函数体 }
函数表达式:function 函数名称(可选)(参数:可选){ 函数体 }
函数声明只能出现在程序或函数体内。从语法上讲,它们不能出现在Block(块)({ ... })中。表达式和声明存在着十分微妙的差别,函数声明会在任何表达式被解析和求值之前先被解析和求值。无论变量在函数内何处声明,在代码执行阶段,变量都会在提到函数开始执行之前被声明,而函数声明发生在更早的阶段。
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被绑定到创建的新对象。apply调用模式
apply允许我们选择this的值,方法接受两个参数,第一个是要绑定this的值,第二个是参数数值。第一个参数为null,undefined将会被替换成全局对象。严格模式use strict的时候不会被替换。
作用域
很多现代语言都推荐延迟声明变量,js中推荐在函数体的顶部声明函数中可能用到的所有变量。
JScript的Bug
例1:函数表达式的标示符泄露到外部作用域
typeof g; // "function"
var f = function g(){};
IE9已经修复
例2:将命名函数表达式同时当作函数声明和函数表达式
typeof g; // "function"
var f = function g(){};
函数声明会优先于任何表达式被解析,上面的例子展示的是JScript实际上是把命名函数表达式当成函数声明了,因为它在实际声明之前就解析了g
匿名函数
匿名函数中this 一般指向window对象
XXXX.prototype.__XXX = function(){} 不是匿名函数
闭包
在编写递归函数时,使用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 */ } ()); // 推荐使用这个
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。