精讲面试题-js篇(变量提升与参数的传递)

114 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第7天,点击查看活动详情

1. 解释下什么是变量声明提升?

变量提升(hoisting),是负责解析执行代码的 JavaScript 引擎的工作方式产生的一个特性。

JS引擎在运行一份代码的时候,会按照下面的步骤进行工作:

  1. 首先,对代码进行预解析,并获取声明的所有变量
  2. 然后,将这些变量的声明语句统一放到代码的最前面
  3. 最后,开始一行一行运行代码

我们通过一段代码来解释这个运行过程:

console.log(a) 
​
var a = 1 
​
function b() { 
  console.log(a) 
}
​
b() // 1

上⾯这段代码的实际执⾏顺序为:

  1. JS引擎将 var a = 1 分解为两个部分:变量声明语句 var a = undefined 和变量赋值语句 a = 1
  2. JS引擎将 var a = undefined 放到代码的最前面,而 a = 1 保留在原地

也就是说经过了转换,代码就变成了:

var a = undefinedconsole.log(a) // undefined 
​
a = 1function b() { 
  console.log(a) 
}
​
b() // 1

变量的这一转换过程,就被称为变量的声明提升。

而这是不规范, 不合理的, 我们用的 let 就没有这个变量提升的问题

2. JS 的参数是以什么方式进行传递的?

基本数据类型和复杂数据类型的数据在传递时,会有不同的表现。

基本类型:是值传递

基本类型的传递方式比较简单,是按照 值传递 进行的。

let a = 1
​
function test(x) { 
  x = 10  // 并不会改变实参的值
  console.log(x)
}
​
test(a) // 10 
console.log(a) // 1

复杂类型: 传递的是地址! (变量中存的就是地址)

image.png 来看下面的代码:

let a = {
  count: 1 
}
​
function test(x) { 
  x.count = 10
  console.log(x)
}
​
test(a) // { count: 10 }
console.log(a) // { count: 10 }

从运行结果来看,函数内改变了参数对象内的 count 后,外部的实参对象 a 的内容也跟着改变了,所以传递的是地址。

思考题:

let a = {
  count: 1 
}; 
​
function test(x) { 
  x = { count: 20 };
  console.log(x); 
}
​
test(a); // { count: 20 }
console.log(a); // { count: 1 }

image.png 我们会发现外部的实参对象 a 并没有因为在函数内对形参的重新赋值而被改变!

因为当我们直接为这个形参变量重新赋值时,其实只是让形参变量指向了别的堆内存地址,而外部实参变量的指向还是不变的。

下图展示的是复杂类型参数传递后的状态:

image.png

下图展示的是重新为形参赋值后的状态:

image.png