1.var和let/const
JS中作用域有:全局作用域、函数作用域。没有块作用域的概念。ECMAScript 6(简称ES6)中新增了块级作用域 { }, 块作用域由 { } 包括,if语句和for语句里面的{ }也属于块作用域
{
var a = 1;
console.log(a); // 1
}
console.log(a); // 1
// 可见,通过var定义的变量可以跨块作用域访问到。
(function A() {
var b = 2;
console.log(b); // 2
})();
// console.log(b); // 报错,
// 可见,通过var定义的变量不能跨函数作用域访问到
if(true) {
var c = 3;
}
console.log(c); // 3
for(var i = 0; i < 4; i++) {
var d = 5;
};
console.log(i); // 4 (循环结束i已经是4,所以此处i为4)
console.log(d); // 5
// if语句和for语句中用var定义的变量可以在外面访问到,
// 可见,if语句和for语句属于块作用域,不属于函数作用域。
{
var a = 1;
let b = 2;
const c = 3;
{
console.log(a); // 1 子作用域可以访问到父作用域的变量
console.log(b); // 2 子作用域可以访问到父作用域的变量
console.log(c); // 3 子作用域可以访问到父作用域的变量
var aa = 11;
let bb = 22;
const cc = 33;
}
console.log(aa); // 11 // 可以跨块访问到子 块作用域 的变量
// console.log(bb); // 报错 bb is not defined
// console.log(cc); // 报错 cc is not defined
}
来一些举例
function b(){
'use strict';
var a=0;
{
var a=1;
console.log(a);
}
console.log(a);
}
b();
function c(){
'use strict';
let a=0;
{
let a=1;
console.log(a);
}
console.log(a);
}
c();
// 结果输出1,1,1,0
function b(){
'use strict';
var a=0;
var a=1;
{
console.log(a);
}
console.log(a);
}
b()
// 输出1,1
function b(){
'use strict';
var a=0;
{
let a=1;
console.log(a);
}
console.log(a);
}
b()
// 输出1,0
function b(){
'use strict';
let a=0;
{
let a=1;
console.log(a);
}
console.log(a);
}
// 输出1,0
function b(){
'use strict';
let a=0;
{
var a=1;
console.log(a);
}
console.log(a);
}
// 报错,Uncaught SyntaxError: Identifier 'a' has already been declared,不允许重复声明a
- 允许块级作用域任意嵌套
{{{let tmp = "hello world"}}}
- 外层作用域无法读取内层作用域的变量;
{{{
{let tmp = "hello world";}
console.log(tmp); //error
}}}
- 内层作用域可以定义外层作用域的同名变量
{{{
let tmp = "hello world";
{let tmp = "hello world";}
}}}
2.var变量和函数function的提升
var temp = new Date();
function f(){
console.log(temp);
if(false){
var temp = "hello";
}
}
f(); //undefined var变量的提升
(function(){
if(false){
function f(){
console.log("inside");
}
}
console.log('f',f)
}())
// 打印undefined, 正常来说要打印出function f(){},
if 条件句中的 function 会被编译成 函数表达式, 声明会被提升到当前作用域的最顶部,
但是赋值会被留在原地. 这也就解释了为什么我们打印 f 的时候会是 undefined 而不是报错,
当然, 如果 if 条件改为 true, f 函数会完成赋值, 最终也是可以调用的
3.var变量和函数function提升示例
// 代码段1
function foo() {
var a = 1;
function a() {}
console.log(a);
}
foo();
// 代码段2
function foo() {
var a;
function a() {}
console.log(a);
}
foo();
答案是:代码段1打印的是1,代码段2打印的是 a() 函数
function foo() {
console.log(a);
var a = 1;
console.log(a);
function a() {}
console.log(a);
}
foo();
上面这段代码实际看起来是下面这样的
function foo() {
var a;
function a() {}
console.log(a); // a()
a = 1;
console.log(a); // 1
console.log(a); // 1
}
foo();
函数的提升会在变量声明的后面,只是变量先声明后再赋值,而函数是整个移上去了。
只有声明的变量和函数才会进行提升,隐式全局变量不会提升
下面的栗子中,b不会进行变量提升。
function foo() {
console.log(a);
console.log(b); // 报错
b = 'aaa';
var a = 'bbb';
console.log(a);
console.log(b);
}
foo();
4.let,const为什么会具有这样的特性
let,const声明在词法环境上,词法环境、变量环境、见上一篇文章--# js 执行上下文
词法环境会形成一个小型的栈结构,当前代码块内的let/const生命的变量,会遵循“后进先出”的原则被推入首位,查询变量的时候,会先从栈的首位开始查找,执行完后从栈顶依次弹出,进而我们无法在代码块外访问到我们在代码块内声明的变量,因而这就是let/const命令能形成块级作用域的原因