let命令特点
- let 声明的变量只在其所在的代码块有效
# demo1
{
let a = 10;
var b = 1;
}
console.log(a); //ReferenceError: a is not defined
console.log(b); // 1
# demo2
for (let i = 0; i< 10;i++){}
console.log(i); //ReferenceError: i is not defined
# demo3
var a = [];
for(var i = 0; i< 10;i++){
a[i] = function () {
console.log(i);
}
}
a[6](); //10
上述代码,i是var声明,作用于全局变量,每循环一次,新的i值都会覆盖旧值,导致最后输出的都最后一轮的i值
# demo4
var a = [];
for(let i = 0; i< 10;i++){
a[i] = function () {
console.log(i);
}
}
a[6](); //6
上述代码,i是let声明,声明的变量仅在块级作用域中有效,当前i只在本轮循环中有效,所以每次循环的i都是一个新的变量,最后将输出6
- 不存在变量提升
console.log(foo); //undefined
let foo = 2;
console.log(typeof x); //undefined
let x;
上述代码中,let不像var一样发生‘变量提升’现象,so,变量一定要在声明后使用,否则会报错。
ps: 自己亲测的时候,并没有出现ReferenceError,只是打印出来了undefined,
书籍上记载为报错,这块存在疑义。
- 暂时性死区
# demo1
var temp = 123;
if(true) {
temp = 'abc';
let temp;
console.log(temp); //undefined
}
ps: 只要块级区域内存在let命令,它所声明的变量就‘绑定’在该区域,不再受外界影响
S6 明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,
从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
总之,代码块中使用let命令声明变量之前,该变量不可用,语法称为‘暂时性死区’
# demo2
if(true){
temp = 'abc';
console.log(temp); // 亲测:abc 书上:ReferenceError
let temp;
console.log(temp); //亲测:undefined 书上:undefined
temp = '123';
console.log(temp); // 亲测:123 书上:123
}
ps: 上述代码,在let声明之前,都属于temp的‘死区’
# demo3
# 有些‘死区’较为隐蔽,不易发现
function bar(x = y, y = 2) {
return [x,y];
}
console.log(bar()); // 亲测: [ undefined, 2 ] 书上:报错
ps:bar函数报错,参数x默认值等于参数y,此时的参数y并没有声明,属于‘死区’
function bar(x = 2, y = x) {
return [x, y];
}
console.log(bar()); // 亲测:[2, 2] 书上:[2, 2]
总结:暂时性死区的本质为,只要进入当前作用域,所要使用的变量就已经存在,但是不可以获取,
只有等声明变量那行代码出现,才可以获取和使用该变量。
-不允许重复声明
// 亲测:报错 Duplicate declaration "a"
function b() {
let a = 10;
var a = 1;
}
// 亲测:报错 Duplicate declaration "a"
function b() {
let a = 10;
let a = 1;
}
//亲测:报错 Duplicate declaration "args"
function func(args) {
let args;
}
//亲测:不报错
function func(args) {
{
let args;
}
}
ps:不能在函数内部重新声明参数
const命令
- const 用来声明常量,一旦声明,其值就不能改变
# demo1
const PI = 301415;
PI = 3;
// 亲测 报错:"PI" is read-only
# demo2
const foo;
// SyntaxError: Missing initializer in const declaration
// 对const而言,只声明不赋值就会报错
- 不存在变量提升
if (true) {
const MAX = 5;
}
console.log(MAX);
// Uncaught ReferenceError: MAX is not defined
ps: const的作用域与let命令相同:只在声明所在的块级作用域内有效。
- 暂时性死区
if (true) {
console.log(MAX); // 亲测: undefined 书上:ReferenceError
const MAX = 5;
}
ps: const命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用。
- 不允许重新声明
var message = "Hello!";
let age = 25;
const message = "Goodbye!";
const age = 30;
ps: 会报错,const也不可以重复声明常量
- 复合类型的变量,变量名不指向数据,而是指向数据所在的地址,
# demo1
const foo = {}
foo.prop = 123;
console.log(foo.prop); //123
foo = {} // "foo" is read-only
ps: 上面代码中,常量foo储存的是一个地址,这个地址指向一个对象。不可变的只是这个地址,
即不能把foo指向另一个地址, 但对象本身是可变的,所以依然可以为其添加新属性。
# demo2
const a = [];
a.push('luopangpang');
console.log(a); //[ 'luopangpang' ]
a.length = 0;
console.log(a); // []
ps: 上面代码中,常量a是一个数组,这个数组本身是可写的,但是如果将另一个数组赋值给a,就会报错。
# 对象冻结,应该使用Object.freeze方法
const foo = Object.freeze({});
// 常规模式时,下面一行不起作用, 严格模式时,该行会报错
foo.prop = 123;
ps: 上述代码中,常量foo指向一个冻结的对象,所以添加新属性不起作用,严格模式时还会报错。
# 对象本身冻结,对象的属性也应该冻结
var constantize = (obj) => {
Object.freeze(obj);
Object.keys(obj).forEach( (key, i) => {
if ( typeof obj[key] === 'object' ) {
constantize( obj[key] );
}
});
};
跨模块常量
//myModule.js
export const A = 1;
export const B = 2;
export const C = 3;
//test1.js
import * as constants from './myModule.js';
console.log(constants.A); //1
console.log(constants.B); //2
//test2.js
import {A,B} from './myModule.js';
console.log(A); //1
console.log(B); //2
全局对象的属性
全局对象是最顶层的对象
浏览器环境: window对象
Node.js:global对象
ES5中,全局对象的属性与全局变量是等价的
window.a = 1;
console.log(a); //1
a = 2;
console.log(window.a); //2
ps: 全局对象的属性与全局变量的赋值是同一件事,
ES5中var命令和function命令声明的全局变量依旧为全局对象的属性
ES6中let命令、const命令、class命令声明的全局变量不属于全局对象的属性
var a = 1;
//假设在Node.js的REPL环境,可以写成global.a 或者 采用通用的方法,写成this.a
window.a //1
let b = 2;
window.b; //undefined
块级作用域
- 场景一: 内层变量可能覆盖外层变量
var temp = new Date();
function f() {
console.log(temp); //undefined
if (false){
var temp = 'hello world';
}
}
console.log(f()); //undefined
ps: 变量提升导致内层的temp变量覆盖外层变量
- 场景二: 用作计数循环变量泄漏为全局变量
var a = [1,2,3,4,5];
for(var i = 0 ; i<a.length;i++ ){
console.log(a[i]); //5
}
console.log(i); //5
ES6新增了块级作用域
function f1() {
let n = 5;
if(true){
let n = 10;
}
console.log(n); //5
}
ps: 外层代码块不受内层代码块影响,最终输出5,假设为var命令声明,最终输出为10。
ES6规定,函数本身的作用域在其所在的块级作用域之内
function f() {
console.log("我在外面");
}
(function () {
if (false) {
// 重新声明
function f() {
console.log("我在里面");
}
}
f(); //我在外面
}());
ps: ES5结果为 ‘我在里面’,原因为变量提升,不过进不进人到if中,函数声明都会提升到当前作用域的顶部而执行
ES6支持块级作用域,不管是否进入if,其内部声明的函数都不会影响外部。
{
let a = 123;
function f(){
return a;
}
}
f(); //ReferenceError: f is not defined
ps: 块级作用域外部无法调用块级作用域内部定义的函数,如若必须调用,需像下面处理
let f;
{
let a = 123;
f = function () {
return a;
}
}
console.log(f()); //123