JavaScript进阶——匿名函数与闭包

一、匿名函数

匿名函数就是没有名称的函数,定义一个普通函数和匿名函数的方法

// 普通函数声明
fn1()              
function fn1(){
    console.log("Hello")
}
// 以下是匿名函数的创建
// 1 函数表达式 等号右边就是一个匿名函数,把匿名函数赋值给变量
var fn2 = function(){       
    console.log("Hello")
}
fn2() 

//  对应箭头函数的写法
let fn3 = () =>{
	console.log("Hello")
}
fn3()


// 2 通过表达式自我执行
 (function box() {            //封装成表达式
        alert('Lee'); 
})();                  //()表示执行函数,并且传参

// 3 函数里的匿名函数
 function box() { 
       return function(){     //函数里的匿名函数,产生闭包 
            return'Lee';
    } 
   } 
alert(box()());               //调用匿名函数

二、闭包

闭包使函数有权访问另一个函数作用域中的变量,创建闭包的常见的方式,就是在一个函数内部创建另一个函数,通过另一个函数访问这个函数的局部变量。函数与变量形成的代码块叫闭包

  • 优点:就是可以把局部变量驻留在内存中,可以避免使用全局变量,全局变量污染导致应用程序不可预测性。
  • 缺点:由于闭包里作用域返回的局部变量资源不会被立刻销毁回收,所以可能会占用更多的内存。过度使用闭包会导致性能下降,建议在非常有必要的时候才使用闭包。

使用闭包解决setTimeout异步函数console内容

for (var i = 0; i < 5; i++){
    setTimeout(function(){
        console.log(i)
    },i*1000)
}

结果输出的是5个5

下面根据执行机制和作用域链解释这一输出结果
首先,JavaScript是单线程环境,代码从上到下依次执行。这种执行方这也被称作是“同步执行”。(同一时间JavaScript只能执行一段代码,如果这段代码要执行很长时间,那么之后的代码只能尽情地等待它执行完才能有机会执行)。
  但JavaScript中引进了异步机制。于是,所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入“任务队列”(task queue)的任务,只有主线程上的任务执行完了,才通知”任务队列”,任务队列中的任务才会进入主线程执行
setTimeout是异步函数,所以会先把主线程的循环执行完毕再执行setTimeout函数,setTimeout的第二个参数则是把执行代码console.log(i)添加到任务队列需等待的毫秒数
JavaScript引擎开始执行任务队列中的代码时,会开始在当前的作用域中开始找变量i,但是当前作用域中并没有对变量i进行定义。这个时候就会在创造该函数的作用域中寻找i。创建该函数的作用域就是全局作用域,这个时候就找到了for循环中的变量i,这时的i是全局变量,并且值已经确定:5,所以输出5个5
参考
用setTimeout实现for循环中的计时器

解决办法:把i设在函数的当前作用域

  1. 使用匿名函数闭包,在函数内部创建了一个函数,并将变量i以函数参数形式传递给内层函数中变量j,j就是这个函数中的局部变量,每次i传入的值不同,局部变量j的值也不同。
    (闭包的意义就是对函数类型的值进行传递时,保留对它被声明的位置所处的作用域的值。闭包的本质就是不让变量被栈回收)
for (var i = 0; i < 5; i++){
    (function(j){
    	setTimeout(function(){
        	console.log(j)
    	},j*1000) 
    })(i)
}
  1. 使用let定义i,当for循环中的i是通过var定义的变量时,作用域是一整个封闭函数,是全局作用域;当i是通过let定义的变量时,将变量固定在在当前循环的块级作用域
for (let i = 0; i < 5; i++){
    setTimeout(function(){
        console.log(i)
    },i*1000)
}
  1. 使用setTimeout的第三个参数,
    setTimeout(function, milliseconds, param1, param2, ...),param作为传给执行函数的其他参数
for (var i = 0; i < 5; i++){
    setTimeout(function(j){
        console.log(j)
    },i*1000,i)
}

注意使用上述三种方法输出的是从0到4,而不是到5,因为到i加到5的时候,不满足i<5不进入函数。而原始的结果输出是5个5,因为函数使用的是最后的i值为5

  • 间隔时间
    delay的时间是1000 * i,等待的时间就会分别变成1000 * 1, 1000 * 2, 1000 * 3……且传入i的值就立即执行,所以每次打印都会有1000ms的时间差

例题

function mo(){
    var x = 0;
    return function(){
        console.log(++x)
    }
}
var a = mo();
var b = mo();
a();
a();
b();

输出1 2 1
函数mo()内部的闭包函数会把变量x存在内存中,再次调用同一个mo()时,会用回这个x

闭包实现private私有变量

通常私有变量约定为以下划线_开头

        var Person = function(name){
            var _name = name
            this.getName = function(){
                return _name
            }
        }
        var p1 = new Person("ls")
        console.log(p1._name)       // undefined
        console.log(p1.getName())   // ls

这种方式的优点是实现了私有属性的隐藏,Person 的实例并不能直接访问_name属性,只能通过特权函数getName获取:

参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值