ES6读书笔记——变量声明、解构赋值

一、定义变量 let、const

ES6声明变量的方法6种:var、function、let、const、import、class

1、 let、const

特点:
① 块级作用域;
② 不存在变量提升;
③ 暂时性死区;(代码块内,使用let命令声明变量之前,该变量都是不可用的)
④ 相同作用域重复声明同一个变量,会报错;

const 声明只读的常量,必须赋值并且不能改变;(否则会报错)
对象声明为常量要小心

const变量指向内存地址保存数据不得改动:

  • 对于简单数据类型,等同于常量;
  • 对于复合数据类型,只能保证指针固定,不能控制指针指向的数据结构。
	//********块级作用域
	for (let i = 0; i < 10; i++) {
	  // ...
	}
	console.log(i);// ReferenceError: i is not defined
	
	var a = [];
	for (var i = 0; i < 10; i++) {
	  a[i] = function () {
	    console.log(i);//i都是同一个变量i
	  };
	}
	a[6](); // 10
	
	//i只在本轮循环有效,每一次循环都是一个新变量
	var a = [];
	for (let i = 0; i < 10; i++) {
	  a[i] = function () {
	    console.log(i);
	  };
	}
	a[6](); // 6


	//********变量提升
	// var 的情况
	console.log(foo); // 输出undefined
	var foo = 2;
	
	// let 的情况:不存在变量提升,会报错
	console.log(bar); // 报错ReferenceError
	let bar = 2;

	//**********暂时性死区(temporal dead zone,TDZ)
	if (true) {
	  // TDZ开始
	  tmp = 'abc'; // ReferenceError
	  console.log(tmp); // ReferenceError
	
	  let tmp; // TDZ结束
	  console.log(tmp); // undefined
	
	  tmp = 123;
	  console.log(tmp); // 123
	}
	
	let x = x; // ReferenceError: x is not defined
	//在变量x的声明语句还没有执行完成前,就去取x的值,导致报错”x 未定义“。

	//*********不允许重复声明
	function func(arg) {
	  let arg;
	}
	func() // 报错
	
	function func(arg) {
	  {
	    let arg;
	  }
	}
	func() // 不报错
	
	//const声明的复合类型数据,指针固定,数据可以改变
	const foo = {};
	foo.prop = 123; // 为 foo 添加一个属性,可以成功
	foo.prop // 123
	foo = {}; // TypeError: "foo" is read-only  将 foo 指向另一个对象,就会报错
		
	
2、块级作用域

ES5只有全局和函数作用域,带来不合理场景:
第一种场景,内层变量可能会覆盖外层变量;
第二种场景,用来计数的循环变量泄露为全局变量。
作用:块级作用域可以任意嵌套;内层可以定义外层作用域的同名变量

	var tmp = new Date();
	function f() {
	  console.log(tmp);
	  if (false) {
	    var tmp = 'hello world';//变量提升
	  }
	}
	f(); // undefined

	var s = 'hello';
	for (var i = 0; i < s.length; i++) {
	  console.log(s[i]);
	}
	console.log(i); // 5
3、顶层对象的属性

顶层对象,在浏览器环境指的是window对象,在 Node 指的是global对象。
ES5 中,顶层对象的属性与全局变量是等价的;ES6 开始,全局变量将逐步与顶层对象的属性脱钩。
ES6中:

  • var、function声明的全局变量,依旧是顶层对象的属性;
  • let、const、class声明的全局变量,不属于顶层对象的属性。
	var a = 1;
	// 如果在 Node 的 REPL 环境,可以写成 global.a,或者采用通用方法,写成 this.a
	window.a // 1
	
	let b = 1;
	window.b // undefined

二、变量的解构赋值

用途:(多思考)
① 交换变量的值;
② 从函数返回多个值;
③ 函数参数与变量名对应;
④ 快速提取JSON数据;
⑤ 设置函数参数的默认值;
⑥ 遍历Map结构;
⑦ 输入模块的指定方法。

1、数组:变量取值由位置决定
  • 模式匹配(两边相等);
  • 不完全解构;
  • 等号右边不是数组,报错;
  • 只要某种数据结构具有 Iterator 接口,都可以采用数组形式的解构赋值;
  • 等号左边可指定默认值;(ES6 内部使用严格相等运算符(===),判断一个位置是否有值);如果默认值是一个表达式,那么这个表达式是惰性求值的,即只有在用到的时候,才会求值。

注意:rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。

	//模式匹配
	let [foo, [[bar], baz]] = [1, [[2], 3]];
	foo // 1
	bar // 2
	baz // 3

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

	// ...变量名:是一个数组,将其展开
	let [head, ...tail] = [1, 2, 3, 4];
	head // 1
	tail // [2, 3, 4]

	let [x, y, ...z] = ['a'];
	x // "a"
	y // undefined   如果解构不成功,变量的值就等于undefined
	z // []


	//不完全解构
	let [a, [b], d] = [1, [2, 3], 4];
	a // 1
	b // 2
	d // 4

	let [foo] = undefined;
	let [foo] = null;
	foo //报错

	//Set结构
	let [x, y, z] = new Set(['a', 'b', 'c']);
	x // "a"

	//指定默认值:只有当一个数组成员严格等于undefined,默认值才会生效。
	let [x, y = 'b'] = ['a']; // x='a', y='b'
	let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'

	let [x = 1] = [undefined];
	x // 1只有严格等于undefined,默认值才生效

	let [x = 1] = [null];
	x // null
2、对象:变量要与属性同名
  • 没有对应的同名属性,导致取不到值,最后等于undefined;
  • 对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。
  • 嵌套赋值
  • 默认值生效的条件是,对象的属性值严格等于undefined;解构失败,变量的值等于undefined
  • 已声明的变量用于解构赋值,会报错;
	let { foo, bar, baz: fun } = { foo: "aaa", bar: "bbb", baz: "ccc" };
	//实际可写作let { foo: foo, bar: bar },真正被赋值的是后面的变量(属性值位置)
	foo // "aaa"
	bar // "bbb"
	fun // "ccc" 变量名与属性名不一致。真正被赋值的是baz(变量), foo是匹配的模式
	baz // error: foo is not defined

	let obj = {
	  p: [
	    'Hello',
	    { y: 'World' }
	  ]
	};
	let { p, p: [x, { y }] } = obj;
	x // "Hello"
	y // "World"
	p // ["Hello", {y: "World"}]    注意:p: [x, { y }] 这里的p是模式,不是变量,不会被赋值,要取值,必须如本例中对象里面给出p

	// 嵌套赋值
	let obj = {};
	let arr = [];
	({ foo: obj.prop, bar: arr[0] } = { foo: 123, bar: true });
	obj // {prop:123}
	arr // [true]

	//指定默认值
	var {x = 3} = {};
	x // 3
	var {x: y = 3} = {};
	y // 3

	//解构模式是嵌套的对象,子对象所在的父属性不存在,报错
	let {foo: {bar}} = {baz: 'baz'};// 报错

	let x;
	{x} = {x: 1};//js引擎会将{}理解为代码块
	// SyntaxError: syntax error
	// 正确的写法:({x} = {x: 1});

	//数组本质是特殊对象
	let arr = [1, 2, 3];
	let {0 : first, [arr.length - 1] : last} = arr;
	first // 1
	last // 3
3、字符串:此时编程类似数组的对象
	const [a, b, c, d, e] = 'hello';
	a // "h"
	b // "e"
	c // "l"
	d // "l"
	e // "o"
	let {length : len} = 'hello';
	len // 5   类似数组的对象都有一个length属性
4、数值和布尔值

解构赋值时,如果等号右边是数值和布尔值,则会先转为对象;
undefined、null无法转为对象,会报错。

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

	let { prop: x } = undefined; // TypeError
	let { prop: y } = null; // TypeError
5、函数参数
  • 为函数参数指定默认值,而不是为变量指定默认值
	[[1, 2], [3, 4]].map(([a, b]) => a + b);
	// [ 3, 7 ]

	//使用默认值:为函数参数指定默认值,而不是为变量指定默认值
	function move({x = 0, y = 0, z} = { z = 10 }) {
	  return [x, y, z];
	}
	move({x: 3, y: 8, z}); // [3, 8, undefined]
	move({x: 3}); // [3, 0, undefined]

	[1, undefined, 3].map((x = 'yes') => x);
	// [ 1, 'yes', 3 ]
6、圆括号问题

不能使用的情况:

  • 变量声明语句
  • 函数参数
  • 赋值语句的模式

可以使用的情况:只有一种,赋值语句的非模式部分

	//会报错的情况:
	let [(a)] = [1];//变量声明
	let {x: (c)} = {};//变量声明
	function f([z,(x)]) { return x; }//函数参数
	({ p: a }) = { p: 42 };//赋值语句的模式

	//可以使用:赋值语句的非模式部分
	[(b)] = [3]; // 正确
	({ p: (d) } = {}); // 正确

猜你喜欢

转载自blog.csdn.net/weixin_43955911/article/details/88816916