在报错里学习js

528 阅读2分钟

正文

今天在学js的时候,发现了一些奇奇怪怪的错误,研究了一下,发现有点意思,就记录了下来。

var apple = {
    label: 'apple',
    print: function() {
        console.log(this.label)
    }
}

var banana = {
    label: 'banana',
    print: function() {
        console.log(this.label)
   }
}

(apple.print = banana.print)()

这道题输出的结果是什么呢?
如果你在第一层,会认为结果是 banana,如果稍加思考分析,会认为是undefined, 但是其实结果是 Uncaught TypeError: Cannot read property 'print' of undefined

image.png

引起报错的原因是分号导致的,在js里面,如果行的开头是(, 会作为上一个语句的前缀,即上面等价为

var banana = {
    label: 'banana',
    print: function() {
        console.log(this.label)
   }
}(apple.print = banana.print)()

这时候apple.print = banana.print会被当作成入参去执行,但是这时候banana这个变量对象在执行上下文中还没赋值,只是进行了声明,所以bananaundefined,所以就会报上述的错。解决方案很简单,加个就好了

let banana = {
    label: 'banana',
    print: function() {
        console.log(this.label)
   }
}

(apple.print = banana.print)()

我们换种姿势,把banana换成let的声明方式,这时候就会变成另外一个错误, Uncaught ReferenceError: Cannot access 'banana' before initialization

这里涉及的知识是变量提升和暂死区,通过var声明的变量banana,会在创建上下文的阶段进行变量提升,即把banana放进上下文的活动对象里面去,而通过let声明的变量,直到它们的定义被执行时才初始化,在之前都存在暂死区,如果在暂死区的时候被访问,就会报错ReferenceError


再改一下

var banana = {
    label: 'banana',
    print: function() {
        console.log(this.label)
   }
}
(apple.print = apple.print)()

同样没有分号,这时的报错换了一个花样 Uncaught TypeError: {(intermediate value)(intermediate value)} is not a function

image.png

这是因为相对于第一次的错误,apple已经赋值过了,所以不会报undefined的错误,所以这时候程序接着执行,发现

{
    label: 'banana',
    print: function() {
        console.log(this.label)
   }
}

不是一个函数,所以就报错了。

总结

JavaScript是世界上最好的语言。

image.png

参考