es6 —— let和const命令以及变量的解构赋值

一、 let和const命令
1. let命令

① let命令的用法类似于var,但其所声明的变量只在let命令所在的代码块内有效

{
let a=0;
var b=1;
for(let i=0;i<5;i++){}
console.log(i);//i is not defined
}
console.log(a);//a is not defined
console.log(b);

② 用var定义的变量在for循环的函数中每次只能取得最终值,而用let定义的变量在for循环的函数中可以取得不同的i值

var a=[];
for(let i=0;i<5;i++){
	a[i] = function(){
		console.log(i);
	}
}
a[3]();//3

③ 不存在变量提升

console.log(foo);//undefined
var foo = 2;
console.log(bar);//报错ReferenceError
let bar  = 1;

④ let不允许在同一作用域内重复声明同一个变量(var和let分别声明也不行

function func(arg){
	let arg;
}
func();//报错
function func(arg){
	{
		let arg;
	}
}
func();//不报错

⑤ let不允许在声明之前使用变量。只要块级作用域内存在let命令,它所声明的变量就"绑定"在这个区域(死区),不会受外部的影响

let temp = 11;
if(true){
	temp = 234;//temp is not defined
	let temp;
	temp = 2;
	console.log(temp);//2
}
function bar(x=1,y=x){
	return [x,y];
}
bar();//[1,1]

function abb(x=y,y=1){
	return [x.y];
}
abb();//报错(y未声明,故不能使用)

⑥ 若在声明前检测一个变量的类型则会报错。但整个块级作用域都没有声明该变量则检测其类型为undefined。
⑦ 若在一个块级作用域内未声明所需变量,此时会去父块级作用域中查找该变量,若有,则该变量的取值为父级块级作用域中该变量的值
⑧ 块级作用域:在es5中,无块级作用域会有两种麻烦出现:a,内存变量可能会覆盖外层变量。b,用来计数的循环变量泄漏为全局变量

//es5中的两种情况
var tmp = new Date();

function f() {
  console.log(tmp);
  if (false) {
    var tmp = 'hello world';
  }
}

f(); // undefined(由于变量提升,故tmp值为undefined)

var s = 'hello';

for (var i = 0; i < s.length; i++) {
  console.log(s[i]);
}

console.log(i); // 5
//es6
function f1(){
	let n=5;
	if(true){
	let n=10;
	}
	console.log(n);//5
}

⑨ 块级作用域与函数声明:ES5规定函数只能在顶级作用域和函数作用域之中声明。不能在块级作用域声明。在ES6中,明确允许在块级作用域之中声明函数。但是函数声明语句的行为类似于let,在块级作用域以外不可引用。(注意:ES6浏览器的执行和规定有出入,具体看文档。也是因为这方面故不推荐在块级作用域中声明函数。若确实需要,应写成函数表达式(即将函数赋值给一个变量),而不是函数声明语句(即一般函数声明)
⑩ ES6块级作用域必须有大括号,如果没有大括号,js引擎则认为不存在块级作用域(不存在块级作用域便不可声明变量

if(true) let x = 1;//报错
if(true){//不报错
	let x=1;
}
2. const命令

本质:const实际上保证的并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不能动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址。而对于复合类型的数据(主要是对象和数组),变量指向的是内存地址,保存的只是一个指向实际数据的指针。const只是保证该内存地址是不变的,而对于内存地址中保存的数据是否可变就不能完全控制了

① const声明一个只读常量,一旦声明,常量的值不能改变
② const声明的变量不得改变值,即const一旦声明变量就必须立即初始化,不能留到以后赋值
③ const的作用域与let命令相同,只在声明所在的块级作用域内有效。
④ const命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用。
⑤ 不可重复声明
⑥ 对于下述例子中,若想真正冻结对象,则使用object.freeze()方法,此时不可为对象添加属性/方法。除了冻结对象,也可冻结对象的属性

//对于本质中复合类型的例子
const foo={};
foo.prop = 123;
console.log(foo.prop);//123
foo = {};// TypeError: "foo" is read-only
const a=[];
a.push("hello");//正确
a.length = 0;//正确
a=["1"];//错误
//针对⑥的例子
const foo = Object.freeze({});
// 常规模式时,下面一行不起作用;
// 严格模式时,该行会报错
foo.prop = 123;


//冻结对象的属性
var constantize = (obj) => {
  Object.freeze(obj);
  Object.keys(obj).forEach( (key, i) => {
    if ( typeof obj[key] === 'object' ) {
      constantize( obj[key] );
    }
  });
};
3. 顶层对象的属性

顶层对象中,在浏览器环境指的是window对象,在 Node 指的是global对象。ES5 之中,顶层对象的属性与全局变量是等价的
实际上,顶层对象的属性和全局变量挂钩是有问题的。① 没法在编译时就报出变量未声明的错误,只有运行时才能知道(因为全局变量可能是顶层对象的属性创造的,而属性的创造是动态的)② 程序员很容易不知不觉地就创建了全局变量(比如打字出错)③ 顶层对象的属性是到处可以读写的,这非常不利于模块化编程
ES6中,为了保持兼容性,var命令和function命令声明的全局变量,依旧是顶层对象的属性;let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性

var a = 1;
// 如果在 Node 的 REPL 环境,可以写成 global.a
// 或者采用通用方法,写成 this.a
window.a // 1

let b = 1;
window.b // undefined
二、 变量的解构赋值
  1. ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值。这被称为解构。本质上,其属于"模式匹配"。只要等号两边的模式相同,左边的变量就会被赋予对应的值。若解析不成功,则变量的值为undefined
  2. 在解构赋值中,扩展运算符必须放在最后一个
//模式匹配例子
//1-1
let [a,b,c] = [1,2,3];
console.log(a,b,c);//1,2,3

//1-2
let [foo,[[bar],baz]] = [1,[[2],3]]
console.log(foo,bar,baz);//1,2,3

//1-3
let [ , , third] = ["foo", "bar", "baz"];
third // "baz"

//1-4
let [x, , y] = [1, 2, 3];
x // 1
y // 3

//1-5
let [x,...y] = [1,2,3,4]
x//1
y//[2,3,4]

//1-6
let [x, y, ...z] = ['a'];
x // "a"
y // undefined
z // []

① 数组的解构赋值

① 等号的右边不是可遍历的结构会报错(数组、对象)
② 若左边是多个变量,则右边必须以数组进行赋值
③ 不完全解构:等号左边的模式只匹配一部分的等号右边的数组。(即若右边与左边不匹配也可以解析成功(但格式必须相同,例如:左边是数组,右边是数字,此时无法匹配成功)

let [x,y] = [1,2,3];
x//1
y//2
let [a,[b],c] = [1,[2,3],4]
a//1
b//2
c//4

④ 解构赋值允许指定默认值。只有当赋值为undefined时会采用默认值

let [foo=true] = []
foo//true
let [x=1] = [undefined];
x//1
let [x=1] = [null];
x//null

⑤ 如果默认值是一个表达值,那么这个表达值是惰性求值的,只有在真正用到时才会求值。

function f(){
//该方法只有在用到时才会执行
	return "1111"
}
let [x=f()] = [1];

⑥ 默认值可以引用解构赋值的其他变量,但该变量必须已经声明过。

let [x=1,y=x] = []//x=1,y=1
let [x=y,y=1]=[]; // ReferenceError: y is not defined

② 对象的解构赋值

① 对象的解构与数组有一个重要的不同:数组的元素是按次序排列的,但是对象的属性没有次序,变量必须与属性同名,才能取到正确的值

let {foo,bar,baz}={bar:"123",foo:"345"};
foo//345
bar//123
baz//undefined

② 对象的解构赋值,可以将先有对象的方法赋值给某个变量

let {sin,cos} = Math();
sin(1);//可以求出1的sin值
let {log} = console;
log("123");//输出123

③ 如果变量名与属性名不一致,必须写成下述例子中的样子。

//1-1
let {foo:baz} = {foo:'aaa',bar:'sss'};
baz//aaa
foo//foo is not defined

//1-2
let {a:c,b:d} = {b:1,a:3}
 console.log(c,d)//3,1

//1-3
 let {a:c,b:d} = {b:1,c:3}
 console.log(c,d)//undefined,1

④ 解构也可以用于嵌套结构的对象。但下述1-1例中不能对p赋值。若相对p赋值必须要如1-2写(注意:要给某一个模式赋值,该模式必须要和等号右边对象中模式相同,否则无法赋值成功)

//1-1
let obj = {
	p:[
		'hello',
		{y:"world"}
	]
}
let {p:[x,{y}]} = obj;
x//hello
y//world
p// p is not defined

//1-2
let {p,p:[x,{y}]} = obj;
x//hello
y//world
p//['hello',{y:"world"}]

//1-3
const node = {
  loc: {
    start: {
      line: 1,
      column: 5
    }
  }
};

let { loc, loc: { start }, loc: { start: { line }} } = node;
line // 1
loc  // Object {start: Object}
start // Object {line: 1, column: 5}

⑤ 对象的解构赋值可以取到继承的属性

const obj1 = {};
const obj2 = { foo: 'bar' };
Object.setPrototypeOf(obj1, obj2);

const { foo } = obj1;
foo // "bar"

⑥ 对象的结构可以指定默认值.默认值生效的条件是:对象的属性的取值为undefined

var {x=3} = {}
x//3
var {y=4}={null}
y//null
var {x:y=3}={}
y//3
var {x:y=3}={x:5}
y//5
x//x is not defined

若要将一个声明的变量用于解构赋值,必须在赋值时带()。若不带括号,js引擎会将{x}理解为一个代码块。

let x;
{x} = {x:1}//错误
({x} = {x:1})//正确

⑧ 解构赋值允许等号左边的模式中不放置任何变量。该写法语法没有错误,但没有意义。
由于数组本质是特殊的对象,因此可以对数组进行对象属性的解构。

let arr=[1,2,3];
let {0:first,[arr.length-1]:last}=arr;
first;//1
last;//3

⑩ 字符串可以解构赋值,是因为字符串被转换成了一个类似数组的对象。类似数组的对象都有一个length属性,因此还可以对这个属性进行操作

const [a,b,c,d,e] = "hello";
a//h
b//e
c//l
d//l
e//o
let {length:len} = "hello";
len//5

解构赋值时,如果等号右边是数值和布尔值,则会先转为对象。

let {toString: s} = 123;
s === Number.prototype.toString // true

let {toString: s} = true;
s === Boolean.prototype.toString // true

函数的参数可以使用解构赋值。函数的参数的解构也可以使用默认值。默认值中两个例子需要注意(1-1,1-2)。在默认值例子中,传来的值替换了等号右边的对象,而默认值在左右不同方。故两个例子有不同的结果

//虽然add函数的参数是一个数组,但在传入参数的时候被解构成变量x和y
function add([x, y]){
  return x + y;
}
add([1, 2]); // 3
//默认值
//1-1
function move({x=0,y=0}={}){
	return [x,y];
}
move({x:2,y:8});//[2,8]
move({x:3})//[3,0]
move({});//[0,0]
move();//[0,0]

//1-2
function move({x,y}={x:0,y:0}){
	return [x,y];
}
move({x:2,y:8});[2,8]
move({x:3});[3,undefined]
move({});//[undefined,undefined]
move();//[0,0]
发布了72 篇原创文章 · 获赞 72 · 访问量 6324

猜你喜欢

转载自blog.csdn.net/weixin_43314846/article/details/102671453