今天遇到了一道关于闭包特别有意思的题

2,151 阅读3分钟

「这是我参与2022首次更文挑战的第3天,活动详情查看:2022首次更文挑战

今天遇到了一道关于闭包特别有意思的题。

图片.png 给大家一点时间,思考一下这道题输出的结果是什么?


现在我和大家一起看一下这道题应该怎样输出

首先我们要明确的是整个f1()函数是一个闭包,这个问题在我上一个闭包有关的问题里有提及。确定其是闭包以后我们就会知道,闭包每一次执行之后都会生成新的执行环境,所以三次log不会互相影响。

第二步我们先看第一个log中console.log(+f1());+ ()的执行顺序是什么。

因此我特意查了一下MDN的运算符优先级表。

图片.png

由此可知,函数调用的优先级要高于一元加法,所以在遇到+和函数调用符()时,我们应该优先运算函数运算。

下面我们先对f1()函数进行划分

图片.png

当我们执行f1()函数时,我们得到的是其返回值f2

闭包1.gif

所以最后console.log(f1())得到的是console.log(f2),再和之前的+组合成了console.log(+f2)

闭包2.gif

现在我们得到了console.log(+f2),我们就要知道+会发生什么。

根据红宝书中的一元运算符中所写

图片.png

一元运算符运算到非数值时,对象会调用它们的valueOf和/或 toString() 方法以得到可以转换的值。

所以console.log(+f2)会使f2强制变成数值,所以要执行其valueOf函数。

闭包3.gif

最后得到的便是console.log(sum),由于我们并没有执行f2函数,所以f1函数内部的sum并没有实现累加操作,因此我们所得到的便是0

经过我们对第一个打印的分析,其实后面两个也就迎刃而解了。

console.log(+f1()());
console.log(+f1()()());

对于第二个打印console.log(+f1()());我们可以将其看做console.log(+f2());

还是由于运算符优先级的关系,我们会先执行f2()

function f2 () {
  sum++
	return f2
 }

根据上面代码,我们会将sum累加一次,然后再返回f2,此时的打印依旧是console.log(+f2)

执行f2.valueOf()函数。

f2.valueOf = function () {
	return sum
}

依旧返回的是sumconsole.log(sum)。但是此时的sum已经经过了一次累加,得到的是1

同理,第三个打印console.log(+f1()()());,只不过是多执行了一次f2(),sum多进行了一次累加。得到的应该是2

function f1 () {
  var sum = 0
  function f2 () {
    sum++
    return f2
  }
  f2.valueOf = function () {
    return sum
  }
  f2.toString = function () {
    return sum++
  }
  return f2
}

console.log(+f1());      // 0
console.log(+f1()());    // 1
console.log(+f1()()());  // 2

那么既然我们每次执行的都是valueOf()那我们什么时候会执行toString()呢。

还是上面红宝书中所写,既然valueOf()是将非数字转换成数字,那么我们应该能联想到,toString()就应该是将非字符转换成字符。所以我们只需要一步操作,就会将执行f2.valueOf()转变成了执行f2.toString()

function f1 () {
  var sum = 0
  function f2 () {
    sum++
    return f2
  }
  f2.valueOf = function () {
    return sum
  }
  f2.toString = function () {
    return sum++
  }
  return f2
}

console.log(""+f1());      // "0"
console.log(""+f1()());    // "1"
console.log(""+f1()()());  // "2"

只需要将+前面加上一个空的字符串,那么JS就会默认将f2当做字符串去匹配,此时就会默认执行toString方法了。

图片.png

由上面可知,当只使用+时,得到的是数字的0,1,2当使用””+时得到的便是字符串形式的0,1,2

以上便是我对这道题的全部理解。


今天有个朋友说我对valueOf和toString的理解不对,他指出valueOf和toString同时存在时valueOf都会执行,而不是有“”时,只执行toString,是我写的不严谨了,再次改正。