Javascript的setTimeout()使用闭包特性时需要注意的问题

简介: 这道题考察JavaScript的运行机制。首先先碰到一个 setTimeout,于是会先设置一个定时,在定时结束后将传递这个函数放到任务队列里面,因此开始肯定不会输出 1 。然后是一个 Promise,里面的函数是直接执行的,因此应该直接输出 2 3 。
function createFunction1(){
    for(var i=0;i<5;i++){
        function s(){
            console.log(i);
        }
        s();
    }
}
createFunction1();   //0 1 2 3 4;

这是一个正常的函数

function createFunction2(){
    for(var i=0;i<5;i++){
        setTimeout(function timer(){
            console.log(i);
        },i*1000);
    }
}
createFunction2();  //每隔1秒输出‘5’、共输出5次

并不会按照我们预想的每隔1秒分别输出0、1、2、3、4

原因:此函数在for循环的第一层是setTimeout函数,他的执行和createFunction1中的s函数一样,将按分别在1秒后、2秒后、3秒后执行。但这儿需要注意的是,setTimeout的内部函数timer并没有立即执行,for循环中的i将会把值分别赋给setTimeout外部参数中的i,但其内部函数timer()则只会引用包含函数setTimeout()中的变量的最后一个值。因为闭包所保存的是整个变量对象,而不是某个特殊的变量。当然其中的这些处理变化,都是瞬间完成的,与执行时间并无关系,即使把1000改成0效果还是一样的。

重写一下这个函数:

function createFunction3(){
    for (var i=0;i<5;i++){
        (function(i){
            setTimeout(function timer(){
                console.log(i);
            },i*1000);
        })(i);
    }
}
createFunction3();   //每隔1秒分别输出0 1 2 3 4

这个例子,给外部包装了一个立即执行的匿名函数,setTimeout里面的匿名函数不再引用外部函数的参数,而是直接引用外部匿名函数的参数,这时,一切就会按照我们预想的来执行了

另外ES6为我们提供了另一种解决方案:

for (let i=1; i<5; i++) {
    setTimeout( function timer(){
        console.log( i );
    }, 1000 );
}   //每隔1秒分别输出0 1 2 3 4

这两种方法都不是解决异步问题的,而是解决变量作用域的问题的。因为函数 timer() 属于一个新的域,通过 var 定义的变量是无法传入到这个函数执行域中的,于是第一种是通过传入参数,间接的把变量传入到 timer 中;第二种是通过使用 let 来声明块变量,这时候变量就能作用于这个块,所以 timer 就能使用 i 这个变量了

setTimeout经常被用于延迟执行某个函数

setTimeout(function(){}, timeout);

有时为了进行异步处理,而使用setTimeout(function…,0);

function f(){
    // get ready
    setTimeout(function(){
        // do something
    }, 0);  
    return …;
}

在setTimeout设定的函数处理器之前,函数f返回;
在使用异步处理时,尤其是使用闭包特性时,要特别小心

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

对于初次使用这种方式的同学来说,很可能会认为程序会打印0…9,可结果确实打印10个10;问题就在于,当循环完成时,function得到执行,而i已经变成10,console.log(i)中使用的是10!

那么可以换一种方式,用函数参数来保存0….9(其实也是利用了闭包):

for(var i = 0 ; i < 10; i++){
    setTimeout((function(i){
        return function(){
            console.log(i);
        }
    })(i), 0);
}

另外再来看一个例子:

function test() {
    for (var i = 0; i < 10; i++) {
        setTimeout(function() {
            console.log(i)
        }, 1000);
    }
}
test();  //9 9 9 9 9 9 9 9 9 9 

把他修改一下:

function test() {
    for (var i = 0; i < 10; i++) {
        setTimeout(console.log(i), 1000);
    }
}
test();  //1 2 3 4 5 6 7 8 9

来看一道考察JavaScript运行机制的题目

setTimeout(function() {
    console.log(1)
}, 0);
new Promise(function executor(resolve) {
    console.log(2);
    for( var i=0 ; i<10000 ; i++ ) {
        i == 9999 && resolve();  
    }
    console.log(3);
}).then(function() {
    console.log(4);
});
console.log(5); //2 3 5 4 1

这道题考察JavaScript的运行机制。首先先碰到一个 setTimeout,于是会先设置一个定时,在定时结束后将传递这个函数放到任务队列里面,因此开始肯定不会输出 1 。然后是一个 Promise,里面的函数是直接执行的,因此应该直接输出 2 3 。然后,Promise 的 then 应当会放到当前 tick 的最后,但是还是在当前 tick 中。因此,应当先输出 5,然后再输出 4 。最后在到下一个 tick,就是 1

目录
相关文章
|
18天前
|
自然语言处理 JavaScript 前端开发
JavaScript中闭包:概念、用途与潜在问题
【4月更文挑战第22天】JavaScript中的闭包是函数及其相关词法环境的组合,允许访问外部作用域,常用于数据封装、回调函数和装饰器。然而,不恰当使用可能导致内存泄漏和性能下降。为避免问题,需及时解除引用,减少不必要的闭包,以及优化闭包使用。理解并慎用闭包是关键。
|
18天前
|
JavaScript 前端开发 开发者
JavaScript中setInterval与setTimeout的异同及使用
【4月更文挑战第22天】JavaScript的`setInterval`和`setTimeout`都用于定时执行任务,但有区别。`setInterval`会按指定间隔反复执行,直到被`clearInterval`停止,可能导致函数堆积;`setTimeout`只执行一次,延迟后执行,适合递归调用来模拟间隔。选择使用时要考虑任务的重复性、执行依赖及可能的性能影响。
|
4天前
|
前端开发 JavaScript
闭包在JavaScript中有许多应用场景
闭包在JavaScript中发挥关键作用,如封装私有变量和函数提升安全性,维护变量生命周期,实现高阶函数,模拟块级作用域,支持回调函数以处理异步操作,以及促进模块化编程,增强代码组织和管理。闭包是理解和掌握JavaScript高级特性的重要一环。
20 7
|
10天前
|
存储 JavaScript 前端开发
【JavaScript技术专栏】ECMAScript 6+新特性详解
【4月更文挑战第30天】ES6(ES2015)标志着JavaScript的重大更新,引入了类和模块、箭头函数、模板字符串、解构赋值、Promise、新数据类型Symbol、Set和Map集合等特性,提高了语言表达力和开发效率。后续版本继续添加新特性,如ES2016的`Array.prototype.includes`,ES2017的`async/await`,以及ES2018的`Object/rest`。学习和掌握这些特性对于提升开发质量和效率至关重要。
|
10天前
|
自然语言处理 JavaScript 前端开发
【JavaScript技术专栏】深入理解JavaScript作用域与闭包
【4月更文挑战第30天】了解JavaScript的关键在于掌握作用域和闭包。作用域决定变量和函数的可访问范围,分为全局(在`window`或`global`对象中)和局部(函数内部)。闭包则允许函数访问其创建时的作用域,即使在其他地方调用。它通过作用域链保存对外部变量的引用,常用于实现私有变量、模块化和柯里化。然而,不当使用闭包可能导致内存泄漏和性能下降。理解这些概念能提升代码质量,但也需谨慎处理潜在问题。
|
11天前
|
自然语言处理 前端开发 JavaScript
【Web 前端】什么是JS闭包?
【4月更文挑战第22天】【Web 前端】什么是JS闭包?
|
13天前
|
自然语言处理 JavaScript 前端开发
闭包对于javascript中有什么作用
JavaScript中的闭包有多种用途,如数据封装和私有化、函数工厂及保持状态。闭包能创建私有变量和函数,防止外部访问,实现清晰的代码接口。
|
16天前
|
测试技术
js_防抖与节流(闭包的使用)
js_防抖与节流(闭包的使用)
18 0
|
18天前
|
设计模式 JavaScript 前端开发
js开发:请解释闭包(closure)是什么,以及它的用途。
【4月更文挑战第23天】闭包是JavaScript中的一个重要概念,允许函数访问并操作外部作用域的变量,常用于实现私有变量、模块化和高阶函数。私有变量示例展示了如何创建只在特定函数内可访问的计数器。模块化示例演示了如何封装变量和函数,防止全局污染。最后,高阶函数示例说明了如何使用闭包创建接受或返回函数的函数。
16 0
|
18天前
|
JavaScript 前端开发
JavaScript中高阶函数与闭包的实际应用
【4月更文挑战第22天】JavaScript中的高阶函数和闭包是强大的工具,常用于抽象、复用和构建组合逻辑。高阶函数如回调、数组方法和函数组合能提高代码灵活性。闭包则用于封装私有变量、创建函数工厂和在异步编程中保留上下文。两者结合使用,如`createAccumulator`函数,能创建更灵活的代码结构。