匿名函数
在JavaScript这门脚本语言中,默认的定义函数格式如下:
function fn(a,b){ console.log(a+b); }
某些情况下定义函数时会省略掉函数名称如下:
function (a,b){ console.log(a+b); }
这样的函数就叫做匿名函数。
而众所周知,函数是通过函数名来调用的,当没有名字时该如何调用,用在什么地方呢?
调用方式
一:放进变量中
let myfn = function(a,b) { console.log(a+b); }; myfn(1,2);
这样一来,这个变量就相当于函数名,因此这个函数就等于是个普通函数了
二:直接执行
常用方式
- (匿名函数)()
- (匿名函数())
(function(a,b){ console.log(a+b); })(1,2) (function(a,b){ console.log(a+b); }(1,2))
三:利用事件调用
let btn = document.getElementById("btn"); btn.onclick = function() { console.log("clicked!"); }
四:作为对象的方法调用
let myobj = { name:"yumefx", hello:function(){ console.log("hello,"+this.name); } }; myobj.hello();
这种方法与第一种方法赋予变量类似。
五:作为另一个函数的参数
function myfn(fn){ fn(); } myfn(function(){ console.log("yes"); });
变量作用域
在ES6之前,当使用var声明变量时,只有全局作用域和函数作用域。
以函数声明为界,函数内部变量和外部变量是两个不同的作用域,内部声明的变量不可以在外部被访问,而内部可以调用和覆盖外部声明的所有变量,即使外层函数已经执行完毕(作用域嵌套)。
var tmp = new Date(); function f() { console.log(tmp); if (false){ var tmp = "hello"; } } f(); //undefined
这段代码中,if代码块的外部使用外层的tmp变量,内部使用内层的tmp变量。但是由于变量提升,导致内层的tmp变量覆盖了外层的tmp变量。
所谓变量提升,就是将函数和变量的声明提升到当前作用域的最顶端,因此要想变量不是全局变量,那么就得用函数包起来。变量提升的结果就是声明的变量在声明前调用值为undefined而不会报错。
除此以外还有内存泄漏问题,例如用来计数的循环变量泄露为全局变量。
var s = "hello"; for (var i = 0; i < s.length; i++){ console.log(s[i]); } console.log(i);
在ES6之后,有了一个新的变量声明符号let,可以声明块级作用域,只有在当前代码块内有效,并且无法在声明前调用(let不存在变量提升)。
a //undefined { var a = 1; let b = 2; } a //1 b //ReferenceError: b is not defined.
另外,let不能在同一作用域内,重复声明同一个变量,例如不能在函数内部重新声明传入的参数。因此,匿名函数的闭包功能,其实就是在let出现前,将某些变量变成类似块级作用域的方式。
优点是可以防止内部变量污染外部变量,但要小心this指向、变量作用域、内存泄漏等问题。
this指向
var object = { name: "object", getName: function() { return function() { console.info(this.name) } } } object.getName()() //undefined
因为里面的闭包函数是在windows作用域下执行的,因此,this指向windows。
变量作用域
function outer() { var result = []; for (var i = 0; i < 10; i++){ result[i] = function() { console.info(i) } } return result; }
看起来似乎是输出等差数列0,1,2,3,4,…,9,实际上因为每个闭包函数访问变量i是outer执行环境下的变量i,随着循环的结束,i已经变成10了,所以最后打印出的是10,10,10,…,10
解决这个问题如下:
function outer() { var result = []; for (var i = 0; i < 10; i++){ result[i] = function(num) { return function() { console.info(num) } } } return result; }
这样访问的num,是上层函数执行环境的num,数组有10个函数对象,每个对象的执行环境下的number都不一样。
内存溢出
function showId() { var el = document.getElementById("app"); el.onclick = function(){ alert(el.id); } }
上面这样写会导致闭包引用外层的el,当执行完alert后,el无法释放内存。修改如下:
function showId() { var el = document.getElementById("app"); el.onclick = function(){ alert(el.id); } el = null; //主动释放el }
使用闭包解决递归调用问题
function factorial(num) { if(num <= 1) { return 1; }else{ return num * factorial(num-1); } } var anotherFactorial = factorial; factorial = null; anotherFactorial(4);
这样写是有问题的,因为最后是return num* argument.callee(num-1),argument.callee指向当前执行函数,但是在严格模式下不能使用该属性也会报错。
借助闭包实现修改如下:
function factorial = (function f(num) { if(num <= 1) { return 1; }else{ return num * factorial(num-1); } })
这样实际起作用的是闭包函数f,而不是外面的函数factorial。
每个人都经历着受骗和伤痛,
最终掌握了在这条街道生活下去的本领。
《我的晃荡的青春》
——东野圭吾
评论
856973 684954Black Ops Zombies is now available […]Take a appear here[…] 804863
467133 35258Thanks for all your efforts which you have put in this. extremely fascinating info. 434150
850852 780828Extremely informative and wonderful complex body part of articles , now thats user pleasant (:. 634214
61647 130245Utterly composed topic material , thanks for selective information . 547253
517339 881683I like the valuable information you offer within your articles. Ill bookmark your weblog and check once again here often. Im quite certain I will learn a lot of new stuff right here! Good luck for the next! 278935
849778 989607informatii interesante si utile postate pe blogul dumneavoastra. dar ca si o paranteza , ce parere aveti de inchirierea apartamente vacanta ?. 468561