概述 :关于函数声明和函数表达式的区别
sayHi();//正确:函数声明提升
function sayHi(){
alert("Hi!");
}
sayHi(); //错误:函数还不存在
var sayHi = function(){
alert("Hi!");
};
//错误的做法实际上,这在 ECMAScript 中属于无效语法,JavaScript 引擎会尝试修正错误,将其转换为合理的状态。不同浏览器尝试修正错误的做法并不一致。大多数浏览器会返回第二个声明,忽略condition;Firefox 会在 condition 为 true 时返回第一个声明。因此这种使用方式很危险。
if(condition){
function sayHi(){
alert("Hi!");
}
} else {
function sayHi(){
alert("Yo!");
}
}
//可以这样做
var sayHi;
if(condition){
sayHi = function(){
alert("Hi!");
};
} else {
sayHi = function(){
alert("Yo!");
};
}
函数还可以作为其他函数的值返回,返回的函数可能会被赋值给一个变量, 或者以其他方式被调用;在把函数当成值来使用的情况下,都可以使用匿名函数。
1、 递归
在非严格模式下,可以使用arguments.callee (指向正在执行的函数的指针)来实现对函数的递归调用
function factorial(num){
if (num <= 1){
return 1;
} else {
return num * factorial(num-1);
}
}
var anotherFactorial = factorial;
factorial = null;
alert(anotherFactorial(4)); //出错!
//以下两种方法即使设置factorial=null,都不会报错
function factorial(num){
if (num <= 1){
return 1;
} else {
return num * arguments.callee(num-1);
}
}
var factorial = (function f(num){
if (num <= 1){
return 1;
} else {
return num * f(num-1);
}
});
2、闭包
关于this对象
匿名函数的执行环境具有全局性,因此其 this 对象通常指向 window。但有时候由于编写闭包的方式不同,这一点可能不会那么明显。
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()()); //"The Window"(在非严格模式下)
在几种特殊情况下,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",在非严格模式下
内存泄漏
如果闭包的作用域链中保存着一个 HTML 元素,那么就意味着该元素将无法被销毁。如下所示
function assignHandler(){
var element = document.getElementById("someElement");
element.onclick = function(){
alert(element.id);
};
}
以上代码创建了一个作为 element 元素事件处理程序的闭包,而这个闭包则又创建了一个循环引用。由于匿名函数保存了一个对 assignHandler() 的活动对象的引用,因此无法减少 element 的引用数。只要匿名函数存在,element 的引用数至少也是 1,因此它所占用的内存就永远不会被回收。
通过两步可以解决以上问题:(1)、把 element.id 保存在一个变量中,并且在闭包中引用该变量(消除循环引用)(闭包会引用包含函数的整个活动对象,而其中包含 element。即使闭包不直接引用 element,包含函数的活动对象中也仍然会保存一个引用,所以还必须进行下一步)(2)、把 element 变量设置为 null
function assignHandler(){
var element = document.getElementById("someElement");
var id = element.id;//第一步
element.onclick = function(){
alert(id);
};
element = null;//第二步
}
3、私有变量
特权方法:有权访问私有变量和私有函数的公有方法
function MyObject(){
//私有变量和私有函数
var privateVariable = 10;
function privateFunction(){
return false;
}
//特权方法
this.publicMethod = function (){
privateVariable++;
return privateFunction();
};
}
静态私有变量
创建静态私有变量会因为使用原型而增进代码复用,但每个实例都没有自己的私有变量。
(function(){
var name = "";
Person = function(value){
name = value;
};
Person.prototype.getName = function(){
return name;
};
Person.prototype.setName = function (value){
name = value;
};
})();
var person1 = new Person("Nicholas");
alert(person1.getName()); //"Nicholas"
person1.setName("Greg");
alert(person1.getName()); //"Greg"
var person2 = new Person("Michael");
alert(person1.getName()); //"Michael"
alert(person2.getName()); //"Michael"
模块模式
模块模式是为单例(只有一个实例的对象)创建私有变量和特权方法。如果必须创建一个对象并以某些数据对其进行初始化,同时还要公开一些能够访问这些私有数据的方法,那么就可以使用模块模式。
var singleton = function(){
//私有变量和私有函数
var privateVariable = 10;
function privateFunction(){
return false;
}
//特权/公有方法和属性
return {
publicProperty: true,
publicMethod : function(){
privateVariable++;
return privateFunction();
}
};
}();