什么是匿名函数:没有实际名字的函数
匿名函数的作用:
1、通过匿名函数可以实现闭包(必须掌握的知识点)
2、模拟块级作用域,减少全局变量。执行完匿名函数,存储在内存中相对应的变量会被销毁,使用块级作用域,会大大降低命名冲突的问题,不必担心搞乱全局作用域了。
详解匿名函数:
声明一个普通函数:
function fx () {
console.log('good girl')
}
将函数的名字去掉
function () { // 此时浏览器会报错
console.log('good girl')
}
正确定义的匿名函数
(function () {
// 由于没有执行该匿名函数,所以不会执行匿名函数体内的语句。
console.log('fx')
})
对去掉名字的函数加入括号后就是一个匿名函数了:
小括号的作用:
小括号能把我们的表达式组合分块,并且每一块,也就是每一对小括号,都有一个返回值。这个返回值实际上也就是小括号中表达式的返回值。所以,当我们用一对小括号把匿名函数括起来的时候,实际上小括号返回的就是一个匿名函数的Function对象。因此,小括号对加上匿名函数就如同有名字的函数般被我们取得它的引用位置了。所以如果在这个引用变量后面再加上参数列表,就会实现普通函数的调用形式。通俗点讲就是,加入小括号后就实现了和具名函数一样的形式。
匿名函数自执行,也称为立即执行函数表达式(IIFE)
方式一
// 无参数的匿名函数
(function () {
console.log('fx')
})();
// 带参数的匿名函数
(function (a, b, c) {
console.log('参数一:', a) // 参数一: 这是普通函数传参的地方
console.log('参数二:', b) // 参数二: 我是参数二
console.log('参数三:', c) // 参数三: fx
})('这是普通函数传参的地方', '我是参数二', 'fx')
方式二
// 推荐使用
(function () {
console.log('fx')
}())
方式三
!function (fx) {
console.log(fx)
}('fx')
方式四
let fx = function (fx) {
console.log(fx)
}('fx')
IIFE常用用法
IIFE 的另一个非常普遍的进阶用法是把它们当作函数调用并传递参数进去。
var a = 2;
(function IIFE (global) {
var a = 3
console.log(a) // 3
console.log(global.a) // 2
})(window)
console.log(a) // 2
IIFE 还有一种变化的用途是倒置代码的运行顺序,
将需要运行的函数放在第二位,
在 IIFE 执行之后当作参数传递进去
var a = 2;
(function IIFE (def) {
def(window)
})(function def (global) {
var a = 3
console.log(a) // 3
console.log(global.a) // 2
})
匿名函数应用场景
1.事件
$('#fx').onclick = function () {
console.log('给按钮添加点击事件')
}
2.对象
var obj = {
name: 'fx',
fx: function () {
return this.name + ' is' + ' good girl'
}
}
console.log(obj.fx()) // fx is good girl
3.函数表达式
var fx = function () {
return 'fx is good girl'
}
console.log(fx()) // fx is good girl
4.回调函数
setInterval(function () {
console.log('fx is good girl')
}, 1000)
5.作为函数的返回值
function fx () {
// 返回匿名函数
return function () {
return 'fx'
}
}
console.log(fx()()) // fx
匿名函数模仿块级作用域
if (true) {
var a = 12 // a为全局变量
}
console.log(a) // 12
for (var i = 0; i < 3; i++) {
// console.log(i)
}
console.log(i) // 3 for没有自己的作用域,所以当循环结束后i就成为全局变量
if () {}for () {} 等没有自己的作用域。
如果有,出了自己的作用域,
声明的变量就会立即被销毁了。
但可以通过匿名函数来模拟块级作用域:
function fn () {
(function () { // 这里是我们的块级作用域(私有作用域)
var fx = 'good girl!' // 此变量在外部并未定义
console.log(fx) // good girl!
})()
console.log(fx) // 报错Uncaught ReferenceError: fx is not defined
}
fn()
习题一
function test(a, b, c, d){
console.log(a + b + c + d);
}(1, 2, 3, 4);
// 不执行也不报错
==============
function test(){
console.log(a + b + c + d);
}();
// 报错:Uncaught SyntaxError: Unexpected token )
习题二
function fxFn (){
var arr = [];
for(var i = 0; i < 10; i ++){
arr[i] = function (){
console.log(i);
}
}
return arr;
}
var fx = fxFn();
for(var j = 0; j < 10; j++){
fx[j]();
}
详解
fxFn
中由于for
不是块级作用域,所以var i
变成 fxFn
的局部变量,每次新的i都会覆盖原来的,最终i=10
。所以会输出10个10
习题三
function fxFn(){
var arr = [];
for(var i = 0; i < 10; i ++){
(function(j){
arr[i] = function (){
console.log(j + " ");
}
}(i))
}
return arr;
}
var fx= fxFn();
for(var j = 0; j < 10; j++){
fx[j]();
}
详解:
这题使用了立即执行函数,把fxFn
中的i
当参数传给了,匿名函数的j,所以每次执行j的状态都会更新,所以会输出0 1 2 3 4 5 6 7 8 9
匿名函数的缺点
arguments
.callee
引用, 比如在递归中。另一个函数需要引用自身的例子,是在事件触发后事件监听器需要解绑自身。