javascript 变量的声明与提升
变量声明
- 不使用关键字(作用域链机制)
- 使用
var
,function
关键字声明变量- 使用
let
,const
关键字声明变量变量提升
- 在栈内存(作用域)形成,JS代码自上而下执行之前,JS引擎首先会将所有使用
var
/function
关键字的变量进行提前声明或定义- 浏览器中,
var
/function
声明的全局变量会与window的属性存在映射机制(会在变量提升阶段在window对象中创建相应的属性,在赋值的同时对window对象对应的属性进行赋值)var
只声明未定义function
声明 + 定义(变量赋值,值为代码字符串在堆中地址(指针))条件判断下(或块级作用域内)的变量提升
老版本(IE10及之前):
- 全局(或函数)作用域顶部:
function
声明 + 定义 提升新版本(IE11及之后):
- 全局(或函数)作用域顶部:
function
只有声明提升,也就是function
和var
一样了- 块级作用域顶部:
function
声明 + 定义 提升ES6变量重复检测机制
- 在栈内存(作用域)形成,JS代码自上而下执行之前,JS引擎会对使用ES6语法(
let
,const
)声明的变量进行变量重复检测(函数声明时也会报错,而不是延迟到函数执行时才报错)ES6的变量声明方法解决了旧版本JS的暂时性死区问题
一、不使用关键字“操作”变量
- 此时不应该叫变量声明,而应该叫变量的“操作”
2. 不使用关键字来“操作”的变量不是私有变量,会向它的上级作用域查找,看是否为上级作用域的变量,一直找到全局作用域的window对象上的属性。(这种查找机制称为作用域链机制)全局作用域下本质是操作 window 的属性
- 例如
a = 10
本质是window.a = 10
的简写- 例如
document.getElementById()
本质是window.document.getElementById()
的简写- 其本质是属性,不是变量,无变量提升(区分与
var
,function
声明的变量)- 注意这种情况会导致全局环境的污染(内存泄漏)
若查找到 window 的属性都未找到该变量,直接使用会报错
/* 栗子1 全局作用域中 */
console.log('a' in window); // false 其本质是属性,不是变量,无变量提升(区分与 `var`, `function` 声明的变量)
a = 0;
window.a; // 0
/* 栗子2 全局函数中 */
function func(){
b = 1;
}
func();
console.log(window.b); // 1
/* 栗子3 嵌套函数 */
function func1(){
function func2(){
c = 2;
}
func2();
}
func1();
console.log(window.c); // 2
/* 栗子4 若查找到 window 的属性都未找到该变量,直接使用会报错 */
console.log(d); // Uncaught ReferenceError: d is not defined
function func3(){
console.log(d);
}
func3(); // Uncaught ReferenceError: d is not defined
二、var
- 没有块的概念(可以跨块访问,不能跨函数访问)
- 未初始化时默认为
undefined
- 变量提升
- 可重复定义(使用变量提升解释)
- 全局作用域下声明的变量会被挂载到window上(在变量提升阶段完成键的创建,也就是挂载也会提升)
注: 区分
var a = 10, b = 10
与var a = b = 10
变量提升
var
声明的变量在js中会经历了两个阶段- 编译阶段进行变量声明提升
- 执行阶段进行赋值
下面举几个栗子
/* 栗子1 */
console.log(value);
var value = 8;
console.log(value);
// js进行变量提升后实际的执行顺序是
var value;
console.log(value);
value = 8;
console.log(value);
// undefined 8
/* 栗子2 */
console.log(v1);
var v1 = 100;
function foo() {
console.log(v1);
var v1 = 200;
console.log(v1);
}
foo();
console.log(v1);
// js进行变量提升后实际的执行顺序是
var v1;
console.log(v1);
v1 = 200;
function foo(){
var v1; // var 声明的变量不能跨函数,且函数内的变量会覆盖掉函数外层的同名变量
console.log(v1);
v1 = 200;
console.log(v1);
}
foo();
console.log(v1);
// 打印结果:undefined undefined 200 100
全局作用域下声明的变量会被挂载到window上(在变量提升阶段完成键的创建,也就是挂载也会提升)
// window
console.log('a' in window); // true 挂载会提升
var a = 10;
console.log(window.a); // 10
注: 区分
var a = 10, b = 10
与var a = b = 10
/* var a = 10, b = 10 这里的 a, b 都是使用 var 声明的变量 var a = b = 10 这里的 a 是 var 声明的变量,b 没有使用 var 声明,本质是window上的属性 var a = b = 10 的本质其实是 { b = 10; var a = b; } */
function func(){
var a1 = 10, b1 = 10;
var a2 = b2 = 10;
}
func();
console.log(window.a1); // undefined
console.log(window.b1); // undefined
console.log(window.a2); // undefined
console.log(window.b2); // 10
三、function
- 没有块的概念(可以跨块访问,不能跨函数访问)
- 函数提升(
function
会进行 声明+定义 提升)- 可重复定义(重写)
- 全局作用域下声明的函数会被挂载到window上(在变量提升阶段完成键的创建,也就是挂载也会提升)
- 条件判断下(或块级作用域内)的变量提升
- js 函数不可重载
函数提升
函数声明有两种形式
- 变量字面量式:
var func = function() {}
- 函数声明式:
function func() {}
变量字面量式
与
var
的变量提升结果相同/* 变量字面量式 */
console.log(foo);
var foo = function(){
console.log(1)
}
// undefined
函数声明式
会进行 声明、定义 提升,且不会被变量声明覆盖(因为已有变量不会被重复声明),但会被变量赋值覆盖
/* 函数声明式 栗子1 */
console.log(foo)
function foo(){
console.log(1)
}
// ƒ foo(){console.log(1)}
/* 函数声明式 栗子2 */
// 函数声明+函数定义 提升
console.log(foo) // f foo(){ return 1 }
var foo
function foo(){ return 1 }
// 不会被变量声明覆盖(因为已有变量不会被重复声明)
function bar(){ return 2 } // 先声明定义
var bar // 后声明同名变量
console.log(bar) // f bar(){ return 2 } ,函数不会被变量声明覆盖
// 会被变量赋值覆盖
bar = 10
console.log(bar) // 10 函数会被变量赋值覆盖
- 变量字面量式:
全局作用域下声明的函数会被挂载到window上(在变量提升阶段完成键的创建,也就是挂载也会提升)
// window
console.log('func' in window); // true 挂载也会提升
function func(){ return 1 }
console.log(window.func); // f func(){ return 1 }
条件判断下(或块级作用域内)的变量提升
/* 栗子1 */
func();
if(true){
function func(){
console.log(1)
}
}
else{
function func(){
console.log(2)
}
}
// IE11及以后: error: foo is not a function
// IE10及之前: 2
/* 栗子2 进阶 */
foo = function() { return true;}
bar = function() { return false;}
~function(){
if(bar() && [] == ![]){
foo = function() { return false;}
function bar(){ return true};
}
}()
console.log(foo);
console.log(bar);
// IE11及以后: Uncaught TypeError: bar is not a function
// IE10及以前: f foo() {return false;}
// f bar() {return false;}
/* 栗子3 块级作用域顶部 */
console.log('块级作用域外:', func2);
{
console.log('块级作用域内顶部', func2); //
function func2() { return true;}
}
// IE11及以后
// 块级作用域外:undefined
// 块级作用域内顶部:f func2() {return true;}
// IE10及以前
// 块级作用域外:f func2() {return true;}
// 块级作用域内顶部:f func2() {return true;}
js 函数不可重载
重载:函数名相同,函数参数不同(Java中函数可以重载)
重写:多用于类中的继承,重写父类的函数JS并没有重载,JS可以这样理解, 万物皆对象。
var func = function() { alert(1) }
这里的func
只是对function() { alert(1) }
的引用。当你重写时,func = function() { alert(2) }
,这时func
的引用指向了function() { alert(2) }
,调用不到alert(1)
了,所以JS只有重写。Java是强类型语言, 根据传入的参数类型,个数不同等可以找到不同的方法, 所以Java有重载;
四、let
- 有块的概念(不可跨块访问)
- 未初始化时默认为
undefined
- 无变量提升(所以不能在声明前使用,否则会报错)
- 不可重复定义(重复定义会报错)
无变量提升(所以不能在声明前使用,否则会报错)
/* 栗子1 */
a = 1;
let a;
// Uncaught ReferenceError: Cannot access 'a' before initialization
/* 栗子2 */
function func(){
a = 1;
let a;
}
func(); // Uncaught ReferenceError: Cannot access 'a' before initialization
不可重复定义(重复定义会报错(在所有代码执行前报错))
在栈内存(作用域)形成,JS代码自上而下执行之前,JS引擎会对使用ES6语法(
let
,const
)声明的变量进行变量重复检测(函数声明时也会报错,而不是延迟到函数执行时才报错)/* 栗子1 */
let a = 10;
console.log(a);
let a; // Uncaught SyntaxError: Identifier 'a' has already been declared
/* 栗子2 */
let b = 10;
console.log(b);
{
let b;
let b; // Uncaught SyntaxError: Identifier 'b' has already been declared
}
/* 栗子3 函数声明时也会报错,而不是延迟到函数执行时才报错 */
let c = 10;
console.log(c);
function f(){
let c = 10;
let c; // Uncaught SyntaxError: Identifier 'c' has already been declared
}
/* 栗子4 */
let can = 1;
console.log(can); // 1
console.log(d); // Uncaught SyntaxError: Identifier 'd' has already been declared
let d = 10;
五、const
- 有块的概念(不可跨块访问)
- 必须初始化,且初始化后其值不能修改
- 无变量提升(同
let
,不能在声明前使用,否则会报错)- 不可重复定义(重复定义会报错)
必须初始化
const a; // Uncaught SyntaxError: Missing initializer in const declaration
无变量提升(同
let
,不能在声明前使用,否则会报错)/* 栗子1 */
console.warn(a);
const a = 1;
// Uncaught ReferenceError: Cannot access 'a' before initialization
/* 栗子2 */
function func(){
console.warn(a);
const a = 1;
}
func(); // Uncaught ReferenceError: Cannot access 'a' before initialization
还没有评论,来说两句吧...