小白滴认知

来源:互联网 发布:java代码 编辑:程序博客网 时间:2024/06/06 02:37

小白遇到的面试题:

for (var i = 0; i < 3; i++) {

    setTimeout(function() {
        console.log(i);
    }, 0);
    console.log(i);

}

输出的结果是0,1,2,3,3,3

分析本题:

(1)先理解本质

setTimeout(function() {
        console.log(1);
}, 0);
console.log(2);

此时先输出2,再输出1,因为setTimeout是异步执行的。setTimeout有两个参数,第一个参数是函数,第二参数是时间值。调用setTimeout时,把函数参数,放到事件队列中。等主程序运行完以后,再调用函数。就像我们平时给元素绑定事件的操作是一样的

btn.onclick = function() {
        alert('只有在点击的时候才会执行,setTimeout也是如此,只是绑定事件,等主程序运行完毕后,再去调用');
};

setTimeout(fn, time)我们可以理解为在指定的time之后,再放入事件队列中,如果此时队列为空,那么就直接调用fn。如果前面还有其他的事件,那就等待。

接下来再看下这个程序:

setTimeout(function() {
        console.log(i);
}, 0);
var i = 1;

此时输出的结果是1,不会报错,因为真正去执行console.log(i)时,var i = 1已经执行完毕了

所以我们平时进行dom操作时可以先绑定事件,然后再去写其他逻辑。

window.onload = function() {
        fn();
}
var fn = function() {
        alert('hello')
};

这么写,完全是可以的,因为异步。

(2)有了这些认知现在再回首原题

for (var i = 0; i < 3; i++) {

    setTimeout(function() {
        console.log(i);
    }, 0);
    console.log(i);

}

它等价于把for循环拆分以后的如下代码

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

由于setTimeout是在注册事件,在加载过程中并没有执行,所以可以进一步等价于以下代码

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

=>结果显然是0,1,2,3,3,3

(3)延伸到闭包

首先我们把原题封装成一个函数

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

这与我们平时经常写的事件是一样的

(*)

var fn = function() {
        var divs = document.querySelectorAll('div');
        for (var i = 0; i < 3; i++) {
                divs[i].onclick = function() {
                        alert(i);
                };
        }
};
fn();

在本例中alert(i)中的i是fn作用域中的=>这是闭包。

我理解的闭包就是能够读取其他函数内部变量的函数。由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可

以把闭包简单理解成"定义在一个函数内部的函数"。所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

function f1(){

 var n=999;

 function f2(){
  alert(n);
 }

 return f2;

}

var result=f1();

result(); // 999

在上面的代码中,函数f2就被包括在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部

变量,对f1就是不可见的。这就是Javascript语言特有的"链式作用域"结构,子对象会一级一级地向上寻找所有父对象的变量。所

以,父对象的所有变量,对子对象都是可见的,反之则不成立。既然f2可以读取f1中的局部变量,那么只要把f2作为返回值,我们就

可以在f1外部读取它的内部变量了

(4)原题拓展

让原题输出0,1,2

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

还可改装成为

for (var i = 0; i < 3; i++) {

    (function(j){

      setTimeout(function( j ) {
          return function() {
              console.log(j);
          };
       }, 0);

    })(i)
}

还可改装成为

var output = function ( i ) {

   setTimeout( function() {

            console.log( i );

    } ,  0 );

}

for ( var i = 0; i < 3 ; i++){

    output( i ); // 此时传过去的i被赋值了

}


1 0
原创粉丝点击