JavaScript-变量提升
JavaScript 仅提升声明,而不提升初始化
Javascript 中执行上下文 (特别是创建和执行阶段)工作方式的一种认识
JavaScript 引擎的工作方式是,先解析代码,获取所有被声明的变量,然后再一行一行地运行。这造成的结果,就是所有的变量的声明语句,都会被提升到代码的头部,这就叫做变量提升(hoisting)
JavaScript 是单线程语言,所以执行肯定是按顺序执行。但是并不是逐行的分析和执行,而是一段一段地分析执行,会先进行编译阶段然后才是执行阶段。
函数提升
sayHi(); // Hi there!
function sayHi() {
console.log("Hi there!");
}
var 变量提升
console.log(name); // 'undefined'
var name = "John Doe";
console.log(name); // John Doe
只有声明操作 var name 会被提升,而赋值这个操作并不会被提升.
当 JavaScript 在编译阶段会找到 var 关键字声明的变量会添加到词法环境中,并初始化一个值 undefined,在之后执行代码到赋值语句时,会把值赋值到这个变量。
let&const
console.log(a); // ReferenceError: a is not defined
let a = 3;
事实上所有的声明(function, var, let, const, class)都会被“提升”。但是只有使用 var 关键字声明的变量才会被初始化 undefined 值,而 let 和 const 声明的变量则不会被初始化值
- let 声明的变量的作用域是块级的;
- let 不能重复声明已存在的变量;
- let 有暂时死区,不会被提升
/ case1
var liList = document.querySelectorAll('li') // 共5个li
for( var i=0; i<liList.length; i++){
liList[i].onclick = function(){
console.log(i)
}
}
// case2
var liList = document.querySelectorAll('li') // 共5个li
for( let i=0; i<liList.length; i++){
liList[i].onclick = function(){
console.log(i)
}
}
// 依次点击会出现5个 5 。如果把var 改为了let 就是分别打印0 , 1 , 2, 3, 4 了。
可以在 let 和 const 声明之前使用他们,只要代码不是在变量声明之前执行
const,其实 const 和 let 只有一个区别,那就是 const 只有「创建」和「初始化」,没有「赋值」过程。
for(let i = 0; i< 5; i++) 这句话的圆括号之间存在有一个隐藏的作用域
再每次执行循环体的时候都会在循环体上下文中重新初始化一次
class
同 let 和 const 一样,class 在 JavaScript 中也是会被“提升”的,在被真正赋值之前都不会被初始化值,
let peter = new Person("Peter", 25); // ReferenceError: Person is not defined
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
let John = new Person("John", 25);
console.log(peter); // Person { name: 'John', age: 25 }
function
//函数声明式
function bar() {}
//函数字面量式
var foo = function() {};
函数字面量式的声明和变量提升的结果是一样的,函数只是一个具体的值
console.log(bar);
function bar() {
console.log(1);
}
//打印结果:ƒ bar () {
// console.log(1);
//}
f2(); // 2
function f2() {
console.log(2);
}
函数提升 优于变量
var getName = function() {
console.log(1);
};
function getName() {
console.log(2);
}
getName();
test();
console.log(test);
function test() {
console.log("我是函数");
}
console.log(test);
var test = "我是变量";
console.log(test);
var test = function(params) {
console.log("我是函数表达式");
};
console.log(test);
test();
// 第一个console.log(test)会输出函数test(),test()输出'我是函数'。
// 而变量只有当代码执行到该语句的时候才会被赋值并且覆盖内存。
// 所以第三个console.log(test)输出的是变量'我是变量'。
// 最后函数表达式又覆盖了前面的代码,
// 所以最后一个console.log(test)输出函数表达式test(),
// test()输出'我是函数表达式
// VM488:4 我是函数
// VM488:2 ƒ test() {
// console.log('我是函数');
// }
// VM488:6 ƒ test() {
// console.log('我是函数');
// }
// VM488:8 我是变量
// VM488:12 ƒ (params) {
// console.log('我是函数表达式');
// }
// VM488:10 我是函数表达式
如果两个函数名一样,后面的会覆盖前面的(认为是在函数解析的时候覆盖,即提升的时候就覆盖了)
test();
console.log(test);
var test = "我是变量";
console.log(test);
var test = function(params) {
console.log("我是函数表达式");
};
console.log(test);
function test() {
console.log("我是函数");
}
console.log(test);
test();
// 我是函数
// VM1251:2 ƒ test() {
// console.log("我是函数");
// }
// VM1251:4 我是变量
// VM1251:8 ƒ (params) {
// console.log("我是函数表达式");
// }
// VM1251:12 ƒ (params) {
// console.log("我是函数表达式");
// }
// VM1251:6 我是函数表达式