shaolin

悟已往之不谏,知来者之可追!

0%

函数声明和变量声明重名

引子

这篇文章源于几道面试题。

//demo 01
{
  function test() {}
  test = 123
}
console.log("test", test)
{
  test00 = 123
  function test00() { }
}
console.log("test00", test00)
// demo 02
{
  function test01() {

  }
  var test01 = 123
}
console.log("test01", test01)

// demo 03
{
  var test02 = function () {}
  var test02 = 123
}
console.log("test02", test02)
// demo 04
{
  function test03() {}
  test03 = 123
  function test03() {}
}
console.log("test03", test03)
// demo 05
{
  function test04() {}
  test04 = 123
  function test04() {}
  test04 = 234
}
console.log("test04", test04)

以上是第一道面试题,打印的结果把我打印懵逼了都,虽然现实中没人这样写奇葩的代码,但是不好意思,最传统的极度灵活的js需要这种面试题😡

就此引进了函数声明和变量声明、独特的声明提升Hositing、块级作用域、默认变量(即没有用let const var等关键字就初始化的变量)。

函数声明、变量声明、js作用域入门

ES6之前,变量声明有且只有两种方式,varfunction开头的函数声明,而且没有块级作用域的概念,只有函数作用域和全局作用域。

作为一个严谨的程序员,我们应该秉持变量先声明再使用的顺序,但是早期js过于动态的特点导致了很多奇奇怪怪的面试题,我们既要去其糟粕,取其精华,也要追本溯源,了解那些黑暗的故事。

不声明会怎样

先看最基础的变量声明:

console.log(a) // undefined
var a = 1
console.log(a) // a

以上是最基本的测试,结果对于任何一个前端来说,闭着眼睛都能答对。

说明js变量可以声明提升

//等价于
var a 
console.log(a)
a = 1
console.log(a)

而且此时的a变量也被挂载到了顶级对象window中,也就是说此时的全局变量和顶级对象耦合很紧密。

我们看看函数声明和变量声明一起使用会发生什么:

var a = 1
function test() {
  console.log(a) //undefined
  var a = 10
  console.log(a) // 10
}
test()
console.log(a) // 1

可以发现,函数声明有单独的作用域,里面的同名变量a只能在局部作用域中使用,函数声明之外的位置不能访问,函数体内的变量声明会被提升到当前局部作用域的顶部,但是只有声明提升,赋值语句保持原位置

即:

function test() {
  var a
  console.log(a)
  a = 10
  console.log(a)
}

那么问题来了:

var a = 1
function test() {
  console.log(a) // 1
  a = 10
  console.log(a) // 10
}
test()

以上函数体内没有变量声明,只有a = 10,打印时按照怎么个顺序去获得a的值呢,答案在作用域链和默认变量

作用域链

对于

默认变量是什么

作用域解析的四大金刚

全新的块级作用域

let

const

块作用域能进行函数声明吗

顶层对象与全局变量解耦