定义函数的两种方式:函数声明、函数表达式
函数声明:function name(arg1,arg2){ // “function”是关键字 “name” 是函数名字
// 函数体
}
函数声明的一个重要特征是 函数声明提升 意思是在读取代码前会读取函数声明。这意味着可以把函数声明放在调用它 的代码后面。栗子:
sayhi();
function sayhi(){ alert(" hi ") }
函数表达式:var funcName = function(arg1,arg2){
// 函数体
}
解释:创建了一个 匿名函数 (因为function关键字后面没有标识符)赋值给了变量 funcName。函数表达式在使用前必须赋值。否则会导致错误。栗子:
sayhi(); // 错误,函数还不存在
var sayhi = function(){ alert("hi") }
理解函数提升的关键,在于理解函数声明和函数表达式之间的区别。栗子:
// 不要这样做
if(condition){
function sayHi(){
alert("Hi!");
}
} else {
function sayHi(){
alert("Yo!");
}
}
//可以这样做
var sayHi;
if(condition){
sayHi = function(){
alert("Hi!");
};
} else {
sayHi = function(){
alert("Yo!");
};
}
7.1递归
通过名字调用自身。
会导致错误的栗子:
function fac(num){
if(num<=1){
return 1
}else{
return num * fac(num-1)
}
}
var anotherFactorial = factorial;
factorial = null;
alert(anotherFactorial(4)); //出错!
正确的用法1:arguments.callee (非严格模式)
function factorial(num){
if (num <= 1){
return 1;
} else {
return num * arguments.callee(num-1);
}
}
正确的用法2:命名函数表达式 (严格模式)
var factorial = (function f(num){
if (num <= 1){
return 1;
} else {
return num * f(num-1);
}
});
7.2闭包
有权访问另一个函数作用域中变量的函数。创建闭包的方式:大函数套小函数。
function c(name){
return function(o1,o2){
var val1 = o1[name];
var val2 = o2[name];
if(val1<val2){ return -1;}
else if(val1>val2){
return 1
}else{
return 0
}
}
}
解释:突出的那两行代码是一个匿名函数中的代码,他们访问了外部函数中的变量name。即使这个匿名函数被返回了,而且在其他地方调用,但它仍然可以访问变量name。值所以能访问这个变量,是因为这个匿名函数的作用域链中包含c()的作用域。
在函数中访问一个变量时,会从作用域中搜索相应名字的变量。当函数执行完毕后,局部活动对象会被销毁,内存中仅保存全局作用域。但是闭包情况不同。
在另一个函数内部定义的函数,将包含外部函数的活动对象添加到它的作用域中。因此在c()函数内部定义的匿名函数的作用域链中,实际上会包含外部函数c()的活动对象。c()函数在执行完毕后,其活动对象也不会销毁,因为匿名函数的作用域仍然在引用这个对象。意思是,当c() 函数返回后,其执行环境的作用域会被销毁,但它的活动对象仍然会留在内存中。直到匿名函数被销毁后,c()的活动对象才会销毁。栗子:
var creat = hreat(" name "); // 创建函数
var result = creat( { name: "ni"} , { name: "gery"} ); // 调用函数
creat = null;
解释:首先创建都变量保存在creat中。通过将creat设置为null解除该函数的引用,就等于通知垃圾回收将其清除。
7.2.1闭包与变量
作用域链的这种配置机制有个副作用,即闭包只能取得包含函数中任何变量的最后一个值。但其实闭包所保存的是整个变量对象,而不是某个特殊的变量。栗子:
function c(){
var r = new Array();
for(var i=0;i<10;i++) {
r[i] = function(){return i}
}
return r
}
解释:表面上这个函数会返回一个函数数组。每个函数都应该返回自己的索引值,即位置0的函数返回0,位置1的函数返回1,.....。但实际上,每个函数都返回10,因为每个函数的作用域中都保存着c() 函数的活动对象,所以它们引用的都是同一个变量i。但是我们可以通过创建另一个匿名函数强制让闭包的行为符合预期。栗子:
function c(){
var r = new Array();
for(var i=0;i<10;i++){
r[i] = function(n){
return function(){
return n
}
}(i);
}
return r;
}
返回 1到9不同的值
7.2.2关于this对象
this对象是在运行时基于环境绑定的:在全局函数中,this等于window,而当函数被作为某个对象的方法调用时,this等于那个对象。匿名函数的执行环境具有全局性,其this对象指向window。
var name = "the window";
var object = {
name:"my object",
getName:function(){
return function(){
return this.name
}
}
}
alert( object.getName() ) // "the window"
解释:函数被调用时会获取两个变量,this和arguments。内部函数在搜索这两个变量时,只会搜索到其活动对象为止,永远不可能访问到外部函数中的这两个变量。不过,把外部作用域中的this对象保存在闭包能够访问到的变量里,就可以让闭包访问该变量了。栗子:
var name = "the window";
var object = {
name:"my object",
getName:function(){
var that = this;
return function () {
return that.name
}
}
}
alert( object.getName() ) // "my object"
解释:在定义匿名函数之前,把this对象赋值给了that的变量。在定义了闭包之后,闭包也可以访问这个变量,因为它是我们在包含函数中特意声明的一个变量。
7.2.3内存泄漏
如果闭包作用域中保存着一个html元素,那么该元素将无法被销毁。栗子:
function assign() {
var el = document.getElementById( "someEl" );
el.onclick = function() {
alert(el.id)
}
}
这个闭包又创建了一个循环引用。由于匿名函数保存了一个对assign()的活动对象的引用,因此就无法减少对el的引用数。因此el所占的内存就永远不会被回收。不过可以通过以下方式解决。栗子:
funtion assign() {
var el = document.getElementById( "someEl" );
var id = el.id;
el.onclick = function() {
alert(id)
};
el = null;
}
解释:通过把el.id的一个副本保存在一个变量中,并且在闭包中引用该变量消除了循环引用。但这样还不能解决内存泄漏的问题。必须要记住:闭包会引用包含整个活动对象,而其中包含着el。即使闭包不直接引用el,包含函数的活动对象中也仍然会保存一个引用。因此必须把el变量设置为null。这样就能够解除对DOM对象的引用的,顺利减少其引用数,确保正常回收其占用的内存。
7.3模仿块级作用域
(function () { // 私有作用域
// 这里是块级作用域
}) ()
这种技术经常在全局作用域中被用在函数外部,从而限制向全局作用域中添加过多的变量和函数。在一个由很多开发人员共同参与的大应用程序中,过多的全局变量和函数很容易导致命名冲突。而通过创建私有作用域,每个开发人员有自己的变量也不会搞乱全局作用域。栗子:
(function(){
var now = new Date();
if(now.getMonth() == 0 && now.getDate() == 1){
alert( "happy new year!" )
}
})()
解释:变量now现在是匿名函数中的局部变量,而我们不必在全局作用域中创建它。这种做法可以减少闭包占用的内存问题,因为么有指向匿名函数的引用。只要函数执行完毕,就可以立即销毁作用域链了。
7.4私有变量
任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数的外面访问这些变量。私有变量包括函数的参数、局部变量和在函数 内部定义的其他函数。栗子:
function add(num1,num2){
var sum = num1+num2;
return sum;
}
解释:这里有3个私有变量 num1,num2和sum。函数内部可以访问它们但函数外部不可以。

7007

被折叠的 条评论
为什么被折叠?



