写代码啦
JS 函数的执行时机
回复数(0) 浏览数(34)
{{topic.upvote_count || 0}} 编辑 回复

今天学习了JS的函数内容。知道每个JS函数都有调用时机。一些简单代码还是比较好理解的,但是如果碰到一些稍微复杂的代码,就需要结合闭包等其他概念来进行理解了。下面看一个例子:

let i = 0
for(i = 0; i < 6; i++){
  setTimeout(()=>{
    console.log(i)
  },0)
}

这个例子会输出6个6。为什么?
setTimeout()这个函数的意思是过一会儿执行。也就是说,在上面这一段代码里的意思是,过一会儿打印出i的值。但是一共执行了6次,而过一会儿i就变成了6,所以会打印出6个6。但是为什么过一会儿取的是6这个值呢?这就要从如何实现闭包说起了。

JS 的闭包

JS 实现闭包的时候,是capture variable的。意思就是,当一个闭包引用了外部变量,它就把这个变量名记下来,等到需要用到的时候,用这个变量名,去查这个变量值是多少。

其他某些语言的闭包

而有一些语言,是另外一种策略,是capture value的。意思就是,当一个闭包引用了外部变量,当这个闭包创建的那一刻,立刻查询那个外部变量的值是多少。把这个值固化下来。那么在那之后,到下一次闭包建立之前,不管这个外部变量变成了多少,都跟这个闭包无关了。

区别

他们本质上最大的区别,就是这些闭包是否可以通过某一个相同的“变量”来共享状态。capture variable 可以,但是capture value 不行。

所以,我们上面这段代码,就会输出6个6。那么,怎么让它输出0,1,2,3,4,5呢?

方法一

for(let i = 0; i < 6; i++){
  setTimeout(()=>{
    console.log(i)
  },0)
}

我们只需要将let声明放到里面,那么输出的就是,0,1,2,3,4,5。原因上面已经解释了。

既然如此,我们还有没有其他除了用let+for循环以外的方法让我们得到这个结果呢。

方法二

let i
for(i = 0; i<6; i++){
    let x = i
    setTimeout(()=>{
      console.log(x)
    })
}

引入另外一个变量,将i的值赋值给这个变量,之后这个变量值怎么变化就与i之后的变化无关了。

方法三

let i
for(i = 0; i<6; i++){
    setTimeout((value)=>{
      console.log(value)
    },0,i)
}

利用setTimeout()函数的第三个参数,将i的值传入到函数中,那么之后就是输出i当时传进来时候的值,并不会跟随她之后的值而改变。

方法四

let i 
for(i = 0; i<6; i++){
  !function(j){
      setTimeout(()=>{
        console.log(j)
      },0)
  }(i)
}

使用立即执行函数,每次循环,就会立马执行这个匿名函数。所以匿名函数执行完之后,里面的j就确定了,之后就是过一会儿输出这个值的问题,就与之前的i怎么变化没有关系了。

这些就是我能找到的方法了,至于还有没有其他的,暂时是没找到,如果找到了再进行补充。

{{topic.upvote_count || 0}}

今天学习了JS的函数内容。知道每个JS函数都有调用时机。一些简单代码还是比较好理解的,但是如果碰到一些稍微复杂的代码,就需要结合闭包等其他概念来进行理解了。下面看一个例子:

let i = 0
for(i = 0; i < 6; i++){
  setTimeout(()=>{
    console.log(i)
  },0)
}

这个例子会输出6个6。为什么?
setTimeout()这个函数的意思是过一会儿执行。也就是说,在上面这一段代码里的意思是,过一会儿打印出i的值。但是一共执行了6次,而过一会儿i就变成了6,所以会打印出6个6。但是为什么过一会儿取的是6这个值呢?这就要从如何实现闭包说起了。

JS 的闭包

JS 实现闭包的时候,是capture variable的。意思就是,当一个闭包引用了外部变量,它就把这个变量名记下来,等到需要用到的时候,用这个变量名,去查这个变量值是多少。

其他某些语言的闭包

而有一些语言,是另外一种策略,是capture value的。意思就是,当一个闭包引用了外部变量,当这个闭包创建的那一刻,立刻查询那个外部变量的值是多少。把这个值固化下来。那么在那之后,到下一次闭包建立之前,不管这个外部变量变成了多少,都跟这个闭包无关了。

区别

他们本质上最大的区别,就是这些闭包是否可以通过某一个相同的“变量”来共享状态。capture variable 可以,但是capture value 不行。

所以,我们上面这段代码,就会输出6个6。那么,怎么让它输出0,1,2,3,4,5呢?

方法一

for(let i = 0; i < 6; i++){
  setTimeout(()=>{
    console.log(i)
  },0)
}

我们只需要将let声明放到里面,那么输出的就是,0,1,2,3,4,5。原因上面已经解释了。

既然如此,我们还有没有其他除了用let+for循环以外的方法让我们得到这个结果呢。

方法二

let i
for(i = 0; i<6; i++){
    let x = i
    setTimeout(()=>{
      console.log(x)
    })
}

引入另外一个变量,将i的值赋值给这个变量,之后这个变量值怎么变化就与i之后的变化无关了。

方法三

let i
for(i = 0; i<6; i++){
    setTimeout((value)=>{
      console.log(value)
    },0,i)
}

利用setTimeout()函数的第三个参数,将i的值传入到函数中,那么之后就是输出i当时传进来时候的值,并不会跟随她之后的值而改变。

方法四

let i 
for(i = 0; i<6; i++){
  !function(j){
      setTimeout(()=>{
        console.log(j)
      },0)
  }(i)
}

使用立即执行函数,每次循环,就会立马执行这个匿名函数。所以匿名函数执行完之后,里面的j就确定了,之后就是过一会儿输出这个值的问题,就与之前的i怎么变化没有关系了。

这些就是我能找到的方法了,至于还有没有其他的,暂时是没找到,如果找到了再进行补充。

34
回复 编辑