1、let和const

ES6新增了let命令,用来声明变量,用法类似于var ,但是声明的变量只在let命令所在代码块内有效。

例如:

{
let a = 10;
var b = 1;
} a // ReferenceError: a is not defined.
b //
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);
};
}
a[6](); // 10
//上面的i是全局范围内有效,就是只有一个变量i,每次循环i的值都会改变。左移最后console.log(i),输出的就是最后的一轮的i值,即10;
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 6
//let 声明的i只在本轮循环中有效,最后输出的是6

for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。下面代码输出了 3 次abc。这表明函数内部的变量i与循环变量i不在同一个作用域,有各自单独的作用域

for (let i = 0; i < 3; i++) {
let i = 'abc';
console.log(i);
}
// abc
// abc
// abc

ES6 明确规定,如果区块中存在letconst命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。

总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(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); //
}

let 不允许在相同的作用域内,重复声明同一个变量。

// 报错
function fun() {
let a = 3;
let a = 1;
}

const用来声明一个常量,一旦声明,常量的值就不能改变。

const声明的变量不得改变值,这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值。

const的作用域与let命令相同:只在声明所在的块级作用域内有效。

if (true) {
const MAX = 5;
} MAX // Uncaught ReferenceError: MAX is not defined

const声明的常量,也与let一样不可重复声明。

const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指针,const只能保证这个指针是固定的,至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。

const foo = {};

// 为 foo 添加一个属性,可以成功
foo.prop = 123;
foo.prop // // 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError: "foo" is read-only
const a = [];
a.push('Hello'); // 可执行
a.length = 0; // 可执行
a = ['Dave']; // 报错

2、变量的解构赋值

let [a, b, c] = [1, 2, 3];

上面代码表示,可以从数组中提取值,按照对应位置,对变量赋值。

本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。下面是一些使用嵌套数组进行解构的例子。

let [foo, [[bar], baz]] = [1, [[2], 3]];
foo //
bar //
baz // let [ , , third] = ["foo", "bar", "baz"];
third // "baz" let [head,...tail] = [1, 2, 3, 4];
head //
tail // [2, 3, 4] let [x, y, ...z] = ['a'];
x // "a"
y // undefined
z // []

如果等号的右边不是数组(或者严格地说,不是可遍历的结构),那么将会报错。

// 报错
let [foo] = 1;
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {};

解构赋值允许指定默认值。

let [foo = true] = [];
foo // true let [x, y = 'b'] = ['a']; // x='a', y='b'
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'

注意,ES6 内部使用严格相等运算符(===),判断一个位置是否有值。所以,如果一个数组成员不严格等于undefined,默认值是不会生效的。

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

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

function f() {
console.log('aaa');
} let [x = f()] = [1];

上面代码中,因为x能取到值,所以函数f根本不会执行。上面的代码其实等价于下面的代码。

let x;
if ([1][0] === undefined) {
x = f();
} else {
x = [1][0];
}

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

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

解构也可用于对象。

let { foo, bar } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"
let { baz } = { foo: "aaa", bar: "bbb" };
baz // undefined

对象解构必须属性同名才能取到值。如果没有对应的同名属性,则为undefinded

如果变量名和数星星不一致,需写成下面这样

let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa" let obj = { first: 'hello', last: 'world' };
let { first: f, last: l } = obj;
f // 'hello'
l // 'world'

这实际上说明,对象的解构赋值是下面形式的简写。

也就是说,对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。

let { foo: foo, bar: bar } = { foo: "aaa", bar: "bbb" };
let { foo: baz } = { foo: "aaa", bar: "bbb" };
baz // "aaa"
foo // error: foo is not defined

解构也可用于嵌套解构的对象。

let obj = {
p: [
'Hello',
{ y: 'World' }
]
}; let { p: [x, { y }] } = obj;
x // "Hello"
y // "World"

注意,这时p是模式,不是变量,因此不会被赋值。如果p也要作为变量赋值,可以写成下面这样。

let obj = {
p: [
'Hello',
{ y: 'World' }
]
}; let { p, p: [x, { y }] } = obj;
x // "Hello"
y // "World"
p // ["Hello", {y: "World"}]
const node = {
loc: {
start: {
line: 1,
column: 5
}
}
}; let { loc, loc: { start }, loc: { start: { line }} } = node;
line //
loc // Object {start: Object}
start // Object {line: 1, column: 5}

上面代码有三次解构赋值,分别是对locstartline三个属性的解构赋值。注意,最后一次对line属性的解构赋值之中,只有line是变量,locstart都是模式,不是变量。

同数组一样,解构对象也可使用默认值。

var {x = 3} = {};
x // var {x, y = 5} = {x: 1};
x //
y // var {x: y = 3} = {};
y // var {x: y = 3} = {x: 5};
y // var { message: msg = 'Something went wrong' } = {};
msg // "Something went wrong"

对象的解构赋值,可以很方便地将现有对象的方法,赋值到某个变量。

let { log, sin, cos } = Math;

上面代码将Math对象的对数、正弦、余弦三个方法,赋值到对应的变量上,使用起来就会方便很多。

由于数组本质是特殊的对象,因此可以对数组进行对象属性的解构。

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

字符串的解构赋值

const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"

类似数组的对象都有一个length属性,因此还可以对这个属性解构赋值。

let {length : len} = 'hello';
len //

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

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

上面代码中,数值和布尔值的包装对象都有toString属性,因此变量s都能取到值。

解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefinednull无法转为对象,所以对它们进行解构赋值,都会报错。

let { prop: x } = undefined; // TypeError
let { prop: y } = null; // TypeError

函数参数的解构赋值

function add([x, y]){
return x + y;
} add([1, 2]); //

函数add的参数表面上是一个数组,但在传入参数的那一刻,数组参数就被解构成变量xy。对于函数内部的代码来说,它们能感受到的参数就是xy

function move({x = 0, y = 0} = {}) {
return [x, y];
} move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, 0]
move({}); // [0, 0]
move(); // [0, 0]

函数参数的解构也可以使用默认值。

解构方式的用途:

1.交换变量的值:

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

2.函数返回多个值

// 返回一个数组

function example() {
return [1, 2, 3];
}
let [a, b, c] = example(); // 返回一个对象 function example() {
return {
foo: 1,
bar: 2
};
}
let { foo, bar } = example();

3.函数参数的定义

// 参数是一组有次序的值
function f([x, y, z]) { ... }
f([1, 2, 3]); // 参数是一组无次序的值
function f({x, y, z}) { ... }
f({z: 3, y: 2, x: 1});

4.提取JSON数据

let jsonData = {
id: 42,
status: "OK",
data: [867, 5309]
}; let { id, status, data: number } = jsonData; console.log(id, status, number);
// 42, "OK", [867, 5309]

5.函数参数的默认值

jQuery.ajax = function (url, {
async = true,
beforeSend = function () {},
cache = true,
complete = function () {},
crossDomain = false,
global = true,
// ... more config
}) {
// ... do stuff
};

指定参数的默认值,就避免了在函数体内部再写var foo = config.foo || 'default foo';这样的语句。

最新文章

  1. 递推 hdu 1396
  2. [Android Pro] Dangerous permissions and permission groups.
  3. 夺命雷公狗—angularjs—17—angularjs的静态库
  4. [转]如何:在设备上安装 SQL Server Compact 3.5
  5. JAVA设计模式之【简单工厂模式】
  6. andorid 文字颜色selector的使用
  7. 设计一个算法,输出从u到v的全部最短路径(採用邻接表存储)
  8. Android 基本控件
  9. CSS Sprite小图片自动合并工具
  10. vmware 几种联网的方式,怎样实现虚拟机上网
  11. Linux ssh密钥自动登录(转)
  12. 基本数据类型TypeScript
  13. 关于jQuery表单选择中prop和attr的区别。
  14. Java零碎知识点
  15. ajax参数解析
  16. CentOS7.5脱机安装SQL Server 2017(NEW)
  17. centos卸载mysql
  18. LVS的DR模式测试案例&lt;仅个人记录&gt;
  19. SharePoint REST 服务获取讨论版问题
  20. MapReduce-深度剖析

热门文章

  1. container(容器),injection(注入)
  2. Learning-Python【24】:面向对象之组合
  3. 通过Python计算一个文件夹大小
  4. Unity3D外包(u3d外包)—就找北京动点软件(我们长年承接U3D外包、Maya、3DMax项目外包)
  5. VR外包—长年承接虚拟现实项目和AR外包游戏、软件(北京动点飞扬软件)
  6. 『Python CoolBook』C扩展库_其六_线程
  7. Bupt归队赛, gunfight
  8. Java-Mail邮件开发
  9. ndarray对象的使用方法
  10. 完美解决xhost +报错: unable to open display &quot;&quot; 装oracle的时候总是在弹出安装界面的时候出错