函数声明提升
函数声明可以放在调用语句的后面;
另,对变量声明,在es6中推荐使用let/const来避免变量声明提升;
匿名函数
也叫拉姆达函数,匿名函数的name属性是空字符串;
var functionName = function(arg0, arg1, arg2){
//函数体
};
递归
如何使递归出错
function factorial(num){
if (num <= 1){
return 1;
} else {
return num * factorial(num-1);
}
}
var anotherFactorial = factorial;
factorial = null;
alert(anotherFactorial(4)); //出错!
如何解决这个错,安全递归
function factorial(num){
if (num <= 1){
return 1;
} else {
return num * arguments.callee(num-1);
}
}
arguments.callee是一个指向正在执行的函数的指针,但是在严格模式下,不能通过脚本访问arguments.callee;
但是可以使用命名函数表达式来达成相同的结果;
var factorial = (function f(num){
if (num <= 1){
return 1;
} else {
return num * f(num-1);
}
});
闭包
闭包是指有权访问另一个函数作用域中的变量的函数;
作用域链被保存在内部的[[Scope]]属性中
function createComparisonFunction(propertyName) {
return function (object1, object2) {
var value1 = object1[propertyName];
var value2 = object2[propertyName];
if (value1 < value2) {
return -1;
} else if (value1 > value2) {
return 1;
} else {
return 0;
}
};
}
//创建函数
var compareNames = createComparisonFunction("name");
//调用函数
var result = compareNames({name: "Nicholas"}, {name: "Greg"});
//解除对匿名函数的引用(以便释放内存)
compareNames = null;
由于闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存;
过度使用闭包可能会导致内存占用过多,我们建议读者只在绝对必要时再考虑使用闭包;
虽然像V8等优化后的JavaScript 引擎会尝试回收被闭包占用的内存,但还是要慎重使用闭包;
一个经典的闭包例子
function createFunctions(){
var result = new Array();
for (var i=0; i < 10; i++){
result[i] = function(){
return i;
};
}
return result;
}
实际上每个函数都返回10,因为它们的作用域链中都保存着createFunctions()函数的活动对象,引用的都是同一个i;
解决方法
function createFunctions(){
var result = new Array();
for (var i=0; i < 10; i++){
result[i] = function(num){
return function(){
return num;
};
}(i);
}
return result;
}
这是一个自执行函数,里面包含了一个新的闭包,这样每个函数都有num的一个副本;
this对象
this对象是在运行时基于函数的执行环境绑定的;在全局函数中,this 等于window,而当函数被作为某个对象的方法调用时,this 等于那个对象;
匿名函数的执行环境具有全局性,所以this对象通常指向window;
当然,在通过call()或apply()改变函数执行环境的情况下,this就会指向其他对象;
但是有时候,不大明显,如:
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()()); //"The Window"(在非严格模式下)
每个函数在被调用时都会自动取得两个特殊变量:this和arguments;
内部函数在搜索这两个变量时,只会搜索到其活动对象为止,因此永远不可能直接访问外部函数中的这两个变量;
但是呢,把外部作用域的this对象保存在闭包能够访问的变量里,那闭包也可以访问该对象了;
var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function () {
var that = this;
return function () {
return that.name;
};
}
};
alert(object.getNameFunc()()); //"My Object"
几种特殊情况下,this的值会意外地改变;
var name = "The Window";
var object = {
name : "My Object",
getName: function(){
return this.name;
}
};
object.getName(); //"My Object"
(object.getName)(); //"My Object"
(object.getName = object.getName)(); //"The Window",在非严格模式下
第三条呢,应该是相当于
var ttt = {
name : "emem",
getName: function() {
return this.name;
}
};
console.log((object.getName = ttt.getName)());
这样?
内存泄漏
闭包在ie9之前的问题,如果闭包的作用域链中保存着一个html元素,那么该元素无法销毁;
function assignHandler(){
var element = document.getElementById("someElement");
element.onclick = function(){
alert(element.id);
};
}
解决方案
function assignHandler(){
var element = document.getElementById("someElement");
var id = element.id;
element.onclick = function(){
alert(id);
};
element = null;
}
在上面的代码中,通过把element.id的一个副本保存在一个变量中,并且在闭包中引用该变量消除了循环引用;
但仅仅做到这一步,还是不能解决内存泄漏的问题,必须要记住:
闭包会引用包含函数的整个活动对象,而其中包含着element;
即使闭包不直接引用element,包含函数的活动对象中也仍然会保存一个引用;
因此,有必要把element变量设置为null;