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

目录
相关文章
|
3月前
|
自然语言处理 JavaScript 前端开发
深入理解JavaScript中的闭包:原理与实战
【10月更文挑战第12天】深入理解JavaScript中的闭包:原理与实战
|
2月前
|
JavaScript 前端开发
js 闭包的优点和缺点
【10月更文挑战第27天】JavaScript闭包是一把双刃剑,在合理使用的情况下,它可以带来很多好处,如实现数据封装、记忆功能和模块化等;但如果不注意其缺点,如内存泄漏、变量共享和性能开销等问题,可能会导致代码出现难以调试的错误和性能问题。因此,在使用闭包时,需要谨慎权衡其优缺点,根据具体的应用场景合理地运用闭包。
123 58
|
2月前
|
JavaScript 前端开发 安全
JavaScript与TypeScript的对比,分析了两者的特性及在实际项目中的应用选择
本文深入探讨了JavaScript与TypeScript的对比,分析了两者的特性及在实际项目中的应用选择。JavaScript以其灵活性和广泛的生态支持著称,而TypeScript通过引入静态类型系统,提高了代码的可靠性和可维护性,特别适合大型项目。文章还讨论了结合使用两种语言的优势,以及如何根据项目需求和技术背景做出最佳选择。
69 4
|
2月前
|
JavaScript 前端开发 安全
ECMAScript 6(以下简称 ES6)的出现为 JavaScript 带来了许多新的特性和改进,其中 let 和 const 是两个非常重要的关键字。
ES6 引入了 `let` 和 `const` 关键字,为 JavaScript 的变量管理带来了革新。`let` 提供了块级作用域和暂存死区特性,避免变量污染,增强代码可读性和安全性;`const` 用于声明不可重新赋值的常量,但允许对象和数组的内部修改。两者在循环、函数内部及复杂项目中广泛应用,有助于实现不可变数据结构,提升代码质量。
33 5
|
2月前
|
自然语言处理 JavaScript 前端开发
ECMAScript 6 的出现为 JavaScript 带来了许多新的特性和改进
这些只是ES6的一些主要特性,它们极大地增强了JavaScript的功能和表现力,使得JavaScript在大型应用开发、前端框架等领域能够更加高效地编写复杂的应用程序。
|
2月前
|
缓存 JavaScript 前端开发
js 闭包
【10月更文挑战第27天】JavaScript闭包是一种强大的特性,它可以用于实现数据隐藏、记忆和缓存等功能,但在使用时也需要注意内存泄漏和变量共享等问题,以确保代码的质量和性能。
45 7
|
2月前
|
自然语言处理 JavaScript 前端开发
JavaScript闭包:解锁编程潜能,释放你的创造力
【10月更文挑战第25天】本文深入探讨了JavaScript中的闭包,包括其基本概念、创建方法和实践应用。闭包允许函数访问其定义时的作用域链,常用于数据封装、函数柯里化和模块化编程。文章还提供了闭包的最佳实践,帮助读者更好地理解和使用这一强大特性。
26 2
|
2月前
|
存储 缓存 自然语言处理
掌握JavaScript闭包,提升代码质量与性能
掌握JavaScript闭包,提升代码质量与性能
|
2月前
|
自然语言处理 JavaScript 前端开发
深入理解JavaScript中的闭包(Closures)
深入理解JavaScript中的闭包(Closures)
|
2月前
|
存储 自然语言处理 JavaScript
深入理解JavaScript的闭包(Closures)
深入理解JavaScript的闭包(Closures)
39 0