函数表达式和函数声明
函数声明: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 */ } ()); // 引荐运用这个