持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第7天,点击查看活动详情
1. 解释下什么是变量声明提升?
变量提升(hoisting),是负责解析执行代码的 JavaScript 引擎的工作方式产生的一个特性。
JS引擎在运行一份代码的时候,会按照下面的步骤进行工作:
- 首先,对代码进行预解析,并获取声明的所有变量
- 然后,将这些变量的声明语句统一放到代码的最前面
- 最后,开始一行一行运行代码
我们通过一段代码来解释这个运行过程:
console.log(a)
var a = 1
function b() {
console.log(a)
}
b() // 1
上⾯这段代码的实际执⾏顺序为:
- JS引擎将
var a = 1分解为两个部分:变量声明语句var a = undefined和变量赋值语句a = 1 - JS引擎将
var a = undefined放到代码的最前面,而a = 1保留在原地
也就是说经过了转换,代码就变成了:
var a = undefined
console.log(a) // undefined
a = 1
function 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
复杂类型: 传递的是地址! (变量中存的就是地址)
来看下面的代码:
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 }
我们会发现外部的实参对象
a 并没有因为在函数内对形参的重新赋值而被改变!
因为当我们直接为这个形参变量重新赋值时,其实只是让形参变量指向了别的堆内存地址,而外部实参变量的指向还是不变的。
下图展示的是复杂类型参数传递后的状态:
下图展示的是重新为形参赋值后的状态: