Z02_let 和 const 命令
let 和 const 命令
for 循环中的特别之处,设置循环变量的那部分是一个父作用域,循环体内部是一个单独的子作用域
for (let i = 0; i < 3; i++) {
let i = "aaa";
console.log(i); //打印三次都是aaa说明和上面不在同一个作用域
}
//原始es5
const arr = [];
for (var i = 0; i < 10; i++) {
arr[i] = function() {
console.log(i);
};
}
arr[5](); //10
arr[9](); //10
//改写
const arr2 = [];
for (let i = 0; i < 10; i++) {
arr2[i] = function() {
console.log(i);
};
}
arr2[5](); //5
arr2[3](); //3
let
1. 不存在变量提升
2. 暂时性死区 TZD
只要块级作用域内存在 let 命令,他声明的变量就 binding 了这个区域,不受外部影响
,在 let 声明变量之前都是死区,意味着typeof
不再是一个百分之百的操作
var temp = 2;
if (true) {
temp = "222"; //报错
console.log(typeof temp); //ReferenceError
let temp; //此处以上的代码声明temp,都属于temp变量的暂时性死区
}
有些暂时性死区比较隐蔽
function bar(x = y, y = 2) {
//此时参数x的默认值等于y,但y还没有声明,属于死区
return [x, y];
}
console.log(bar(1, 2)); //[1,2]
bar(); //ReferenceError: y is not defined
//如果 y的默认值是x就不会报错
function bar2(x = 2, y = x) {
console.log(x, y);
}
bar2(); // 2,2
3. 不允许在相同作用域内重复声明同一个变量
function func(args) {
let args; //不熬在函数内部重新声明参数
}
块级作用域
1. 为什么需要块级作用域?
- 内层变量可以覆盖外层变量
- 用来计数的循环变量泄露成全局变量
使得广泛应用的立即执行匿名函数(IIFE)不在必要了
(function() {
var temp = "aaa";
})();
//块级作用域
{
let temp = "aaa";
}
2. 块级作用域和函数
ES5 规定函数只能在顶层作用域和函数作用域之中声明,不能在块级作用域中声明
//情况 1
if (true) {
function fn() {}
}
//情况 2
try {
function fn() {}
} catch (e) {}
以上两个函数声明在 ES5 中是非法的,在 es6 中应该避免在块级作用域中声明函数,如果需要可以应该写成函数表达式的形式
es6 允许在块级作用域中声明函数,在块级作用域中,函数声明语句的行为类似于 let,在块级作用域之外不可引用
//以下代码会报错
function f() {
console.log("外面函数f");
}
(function() {
if (false) {
function f() {
console.log("里面函数f");
}
}
f();
})();
//实际运行的情况如下
function f() {
console.log("外面函数f");
}
(function() {
var f = undefined;
if (false) {
function f() {
console.log("里面函数f");
}
}
f();
})();
考虑到环境导致的行为差异太大,应该避免在块级作用域中声明函数,确实需要的话,应该写成函数表达式的形式,而不是函数声明语句
{
let a=123;
function f(){return a}
}
//确实需要的话
{
let a=123;
let f=function(return a)
}
3. do 表达式
本质上块级作用与是一个语句,将多个操作封装在一起,没有返回值,可以使用 do
//以下块级作用域中不返回值,此作用域之外无法得到t的值,除非t是全局变量
{
let t = f();
t * t * t + 1;
}
新的提案,在块级作用域前加上 do,使他变为 do 表达式
let x = do {
let t = f();
t * t * t + 1;
}; //x 会得到整个块级作用域的返回值
const 命令
const 声明一个只读的常量,一旦声明,常量的值就不能改变
1. 注意只声明不赋值会报错
const MAX; //报错
2. const 和 let 的作用域一样,只在声明所在的块级作用域内有效
if (true) {
const MAX = 555;
}
console.log(MAX); //报错
3. const 命令也不存在变量提升,同样存在暂时性死区
if (true) {
console.log(MAX); //报错
const MAX = 5555;
}
4. 和 let 一样不可以重复声明
var a = 1;
let b = 2;
//下面两行报错
const a = 3;
const b = 4;
const
的本质实际是保证变量指向的那个内存的地址不得改动,对于引用数据类型来讲,变量指向内存的那个指针,const
只能保证这个指针是固定的,至于他指向的数据结构是不可变的,这完全不能控制,所以将一个对象声明为一个常量要格外小心
const foo = {};
foo.name = "foo";
foo = {}; //指向新的对象会报错
const arr = [];
arr.push("a");
arr = [1, 2, 3]; //赋值为新的数组报错
如果想冻结对象,应该使用 Object.freeze({})
const obj = Object.freeze({
name: "cc",
age: 12
});
console.log(obj); //{ name: 'cc', age: 12 }
obj.name = 123; //严格模式下会报错
console.log(obj); //{ name: 'cc', age: 12 }
//常量指向一个冻结的对象,所以给他添加新的属性是不起作用的,严格模式还会报错
除了对象本身冻结,对象的属性也应该冻结,下面是将对象彻底冻结的函数
var constantize = obj => {
Object.freeze(obj);
Object.keys(obj).forEach((key, i) => {
if (typeof obj[key] === "object") {
constantize(obj[key]);
}
});
};
5. ES6 中声明变量的 6 中方法
ES5 声明变量只有 var
和 function
ES6 声明 const
let
import
class
顶层对象的属性
顶层对象在浏览器环境中是指 window,在 node 中是指 global 对象
//ES5中,顶层对象的属性和全局变量是等价的
window.a=1 ===== var a=1
//ES6开始全局变量将逐步与顶层对象的属性隔离
let b=1 !==== window.b //undefined
global 对象
ES5 的顶层对象本身也是一个问题,因为他在各种实现中是不统一的
- 浏览器中顶层对象就是 windoqm,但是 Node 和 Web Worker 中没有 window
- 浏览器和 Web Workder 中,self 也指向顶层对象,但是 Node 没有 self
- 在 Node 中,顶层对象是 global,但是其他环境都不支持
同一端代码为了能够在各种环境中都能获取顶层对象,目前一般是采用 this 变量,但是也有局限性
- 全局环境中,this 返回顶层对象,但是 ES6 模块和 node 模块中,this 返回的是当前模块 2.对于函数 this,如果函数不是以对象的方式运行,而是单纯作为函数运行,this 会指向顶层对象,但是严格模式下,this 指向 undefined
- 不管是严格模式还是普通模式,
new Function('return this')()
总是返回全局对象
所以很难有一种方法可以适合上面所有场景,取得顶层对象,以下是两种勉强可以使用的方法
//方法一
var a =
typeof window !== "undefined"
? window
: typeof process === "object" &&
typeof require === "function" &&
typeof global === "object"
? global
: this;
// console.log(a);
//方法二
var getGlobal = function() {
if (typeof self !== "undefined") {
return self;
}
if (typeof window !== "undefined") {
return window;
}
if (typeof global !== "undefined") {
return global;
}
throw new Error("unable to locate global object");
};
console.log(getGlobal());