前端面试题ECMAScript 6 入门

ECMAScript 6 入门--let、const和变量解构

2019-07-15  本文已影响0人  Demonskp

ECMAScript 6 入门--let、const和变量解构

主要是阅读阮一峰老师的《ES6标准入门》这一本书所做的读书笔记。作为掌握ES6相关特性的一个过程。同时也作为之后速查的一个手册。

《ECMAScript 6 入门》 购买

ES6简介

  ECMAScript和JavaScript的关系ES6是JavaScript的一个标准,是由ECMA组织制定的。而JavaScript是ECMAScript的其中一种实现。
  ES6是ECMAScript的一个版本,而ES6包含了ES2015和ES2016两个版本(在这之后ECMA决定,每年六月份发一个版本以后都按照年份来命名)

let命令

let:用来生成只能在当前代码块有效的变量

当前有效

let只在当前的代码块当中有效

{
  let a = 10;
  var b = 1;
}

a // ReferenceError: a is not defined.
b // 1

变量提升

let不会发生变量提升的现象,使用必须在声明之后。而不是在声明前使用视为undefined。

// var 的情况
console.log(foo); // 输出undefined
var foo = 2;

// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;

暂时性死区

只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。

var tmp = 123;

if (true) {
  tmp = 'abc'; // ReferenceError
  let tmp;
}

如果在代码块当中声明了一个变量,那么在当前代码块声明前的区域则会变成死区,无法在这个区域使用这个变量,即使有一个同名的全局变量。除非用this指明。

对于某一些死区,则不是很明显。这也是容易出现错误的位置。

function bar(x = y, y = 2) {
  return [x, y];
}

bar(); // 报错

这里在指定给bar函数的参数设置了默认值(具体请参照ES6函数新增部分),而在声明y之前就已经使用了x=y。这显然违反了暂时性死区的规则。(由此可见,可以认为在设置默认值的时候使用的是let)

块级作用域

块级作用域是ES6提出的一个新概念,表明用{}花括号包裹起来的区域作为一个一个域。

解决了什么问题?

内层变量可能会覆盖外层变量

var tmp = new Date();

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

f(); // undefined

由于var tmp = 'hello world';导致的变量提升则console.log(tmp)当中的tmp被当成了之后声明的。于是变成了undefined。

用来计数的循环变量泄露为全局变量

var s = 'hello';

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

console.log(i); // 5

本来只应该在内部循环使用的计数器泄漏到了外部。

块级作用域这个概念就是为了let这种声明方式而提出的,let只能在其声明的作用域当中起作用。程序中只要你清楚let会在哪个位置起作用就行了,而不必纠结块级作用域这个概念。

ES6 引入了块级作用域,明确允许在块级作用域之中声明函数。ES6 规定,块级作用域之中,函数声明语句的行为类似于let,在块级作用域之外不可引用。

const命令

const:静态变量,只能赋值一次,声明时必须赋值

const的作用域和let是一致的,只能在声明他的作用域当中起作用。同样存在暂时性死区,不可重复性声明。

const的本质

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

// 为 foo 添加一个属性,可以成功
foo.prop = 123;
foo.prop // 123

// 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError: "foo" is read-only

const声明的是一个地址指向,而这个地址实际存储了什么,是不被限定的。因此你能够给对象添加属性,给数组添加成员。

变量解构

ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。

数组解构

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

let [foo, [[bar], baz]] = [1, [[2], 3]];
foo // 1
bar // 2
baz // 3

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

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
z // []

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

相对应的位置有值则能够解构,没有值则无法结构,注意前一项比后一项多也可以结构。

数组能够解构的本质是其转为对象以后具备 Iterator 接口或本身具备 Iterator 接口

对象

对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。

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

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

如果变量名与属性名不一致,必须写成下面这样。

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'

字符串

字符串也可以解构赋值。这是因为此时,字符串被转换成了一个类似数组的对象。

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

函数

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

add([1, 2]); // 3

默认值

解构赋值允许指定默认值,与函数参数一样

let [foo = true] = [];
foo // true

let [x, y = 'b'] = ['a']; // x='a', y='b'
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'
...
// 其他类型都可以
上一篇下一篇

猜你喜欢

热点阅读