一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第17天,点击查看活动详情。
首先看看下面这段代码,自己先心里确定一个答案,然后复制到浏览器看看,结果是不是出乎你的预料?
var name = [1, 2, 3]
name.length
初次看,name = [1,2,3],什么?三个元素,结果居然是 5,不是 3 ?
别急,接着在浏览器输入以下代码:
let name = [1, 2, 3]
name.length // 3
结果又变成了 3,重复声明了,结果怎么又正常了?
两次结果不同,先看两者代码的不同之处。第一次使用 var 声明,第二次使用 let 声明。我们回忆下两者的区别:
var声明的变量的作用域是它 当前的执行上下文- 可以是一个全局作用域,例如浏览器的
window下、nodejs 中全局包裹的global等等。 - 可以是一个(嵌套)函数作用域,也就是说
var声明的变量只能在当前函数以及内层嵌套函数内可访问。
- 可以是一个全局作用域,例如浏览器的
let声明的是块级作用域,let声明的变量只在其声明的块或子块中可用。 和var的区别是:var声明的变量的作用域是 整个封闭函数(函数作用域),受到函数作用域限制,不受{}块的限制。
(1)var受函数作用域限制(2)function f() { var a = 123 } f() a // ReferenceError: a is not definedvar没有块级作用域,不受{}块限制{ var b = 123 } b // 123let声明的变量的作用域是 块级作用域,具体表现为一个{}块,例如if(){}语句块、for(){}语句块、{}块,也包括函数块function(){}。{ let c = 123 } // ReferenceError: c is not definedfunction f() { let d = 123 } f() d // ReferenceError: d is not defined
现在来看这道题:
var name = [1, 2, 3]
name.length // 5
前面提到过,var 在浏览器全局环境下声明的变量会放到 window 对象上,但是,有一个容易忽略的问题,那就是 window 上本身就有一个 name 属性,window.name 是浏览器窗口的名称属性。使用 var 可以合法的重复声明一个已经存在的变量,起作用的就只有赋值,也就是说,上述代码等价于:
window.name = [1, 2, 3]
给一个窗户名称属性 name 赋值,会发生什么呢?MDN 上这样说:
window.name会调用toString将赋给它的值转换成对应的字符串表示。 ——MDN window.name
也就是说会执行 [1, 2, 3].toString() 方法,数组的 toString() 默认行为是把数组中的元素使用 , 拼接返回,那么 window.name 的值为 '1,2,3'。'1,2,3'.length 的结果自然是 5。
至于 let name = [1, 2, 3],let 在浏览器全局声明的变量不会在 window 上,而是底层实现的全局环境下(这里我也说不清,总之就是挂载到一个特殊的全局环境下,非 window)。那么 [1,2,3].length 结果自然为 3。
综上所述,不要再全局中轻易的以 name 命名!
怎么样,看起来奇怪的表现,其实都是些基础知识,所以基础还是很重要的!
本文完。