这是我参与8月更文挑战的第7天,活动详情查看:8月更文挑战git
ES6 以前 JS 没有块级做用域。github
if (true) {
var name = 'zhangsan'
}
console.log(name) // zhangsan
复制代码
从上面的例子能够体会到做用域的概念,做用域就是一个独立的地盘,让变量不会外泄、暴露出去。上面的 name 就被暴露出去了,所以,ES6 以前 JS 没有块级做用域,只有全局做用域和函数做用域。markdown
var a = 100
function fn() {
var a = 200
console.log('fn', a) // fn 200
}
console.log('global', a) // global 100
fn()
复制代码
ES6 出来以后,let 和 const 都可以声明块级做用域app
使用 var 声明的变量,不管是在代码的哪一个地方声明的,都会提高到当前做用域的最顶部,这种行为叫作变量提高(Hoisting)。ide
function test() {
console.log("1: ", a); //undefined
if (false) {
var a = 1;
}
console.log("3: ", a); //undefined
}
test();
复制代码
实际执行时,上面的代码中的变量 a 会提高到函数顶部声明,即便 if 语句的条件是 false,也同样不影响 a 变量提高。函数
function test() {
var a;
//a 声明没有赋值
console.log("1: ", a); //undefined
if (false) {
a = 1;
}
//a声明没有赋值
console.log("3: ", a); //undefined
}
test();
复制代码
在函数嵌套函数的场景下,变量只会提高到最近的一个函数顶部,而不会提高到外部函数。oop
// b 提高到函数a顶部,但不会提高到函数 test
function test() {
function a() {
if (false) {
var b = 2;
}
}
console.log("b: ", b);
}
test(); // b is not defined
复制代码
let 和 const 都可以声明块级做用域,用法和 var 是相似的,let 的特色是不会变量提高,而是被锁在当前块中。惟一正确的使用方法:先声明,再访问。post
function test() {
if (true) {
console.log(a); //TDZ,俗称临时死区,用来描述变量不提高的现象
let a = 1;
}
}
test(); // a is not defined
function test() {
if (true) {
let a = 1;
}
console.log(a);
}
test(); // a is not defined
复制代码
声明常量,一旦声明,不可更改,并且常量必须初始化赋值。ui
const 虽然是常量,不容许修改默认赋值,但若是定义的是对象 Object,那么能够修改对象内部的属性值包括新增删除键值对也是能够的。lua
函数有一个内部属性
[[scope]]
自由变量一层一层向上寻找,直到找到全局做用域仍是没找到,就宣布放弃。这种一层一层的关系,就是做用域链。
let a = 100;
function F1() {
let b = 200;
function F2() {
let c = 300;
console.log(a); // 自由变量,顺做用域链向父做用域找
console.log(b); // 自由变量,顺做用域链向父做用域找
console.log(c); // 本做用域的变量
}
F2();
}
F1();
复制代码