ES6

ECMAScript 6.0(简称 ES6)是 JavaScript 语言的下一代标准,在 2015 年 6 月正式发布。它的目标是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。
ECMAScript 和 JavaScript 的关系,大体上前者是后者的规格,后者是前者的一种实现(另外的 ECMAScript 方言还有 Jscript 和 ActionScript)。在日常场合这两个词是可以互换的。

从软件工程角度来看,ES6就相当于当年的Java5,是历史性的发展,从此可以用js做大型项目了。事实上各大主流浏览器现在已经支持大部分新特性了,后端的Node.js更是可以直接使用ES6的绝大多数语法。

Babel

Babel 是一个广泛使用的 ES6 转码器,可以将 ES6 代码转为 ES5 代码,从而在现有环境执行。这意味着可以用 ES6 的方式编写程序,又不用担心现有环境是否支持。

// 转码前,箭头函数
input.map(item => item + 1);

// 转码后,普通函数
input.map(function (item) {
  return item + 1;
});

变量声明方式 let/const

ES6中全面使用let/const替换var。使用let声明一个值会被改变的变量,使用const声明一个值不会被改变的变量,即常量。

与var不同,新的变量声明方式带来了一些不一样的特性,其中最重要的两个特性就是提供了块级作用域与不再具备变量提升。let或const变量一定要在声明后面使用,否则报错is not defined。在实际使用中,尽量避免使用变量提升的特性带来的负面影响。只有在面试题中,才会对变量提升不停的滥用。
var声明的变量仍旧是函数级作用域。

var和let在for循环中的应用:
下面的代码如果使用var,最后输出的是10。

var a = [];
for (var i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 10
上面代码中最后一行输出的结果是10。变量i是var命令声明的,在全局范围内都有效,所以全局只有一个变量i。每一次循环,变量i的值都会发生改变,而循环内被赋给数组a的函数内部的console.log(i),里面的i指向的就是全局的i。也就是说,所有数组a的成员里面的i,指向的都是同一个i,导致运行时输出的是最后一轮的i的值,也就是 10。

如果使用let,声明的变量仅在块级作用域内有效,最后输出的是 6。
var a = [];
for (let i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 6

上面代码中,变量i是let声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以最后输出的是6。

既然使用let使的每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。
for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。

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

箭头函数

当函数直接被return时,可以省略函数体的括号。

// es5
var fn = function(a, b) {
    return a + b;
}
// es6 6
const fn = (a, b) => a + b;

箭头函数可以替换函数表达式,但是不能替换函数声明。
箭头函数中没有this。在ES6中,默认采用严格模式,this不会自动指向window对象了,this就只能是undefined。

模板字符串

模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。在其中使用 ${} 来包裹一个变量或者一个表达式。

const a = 20;
const b = 30;
const str = `${a}+${b}=${a+b}`;

解构赋值

获取对象属性值都可以使用解析结构来减少代码量。本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。如果解构不成功,变量的值就等于undefined。

const props = {
    className: 'tiger-button',
    loading: false,
    clicked: true
}
//取对象中的值到变量
const { loading, clicked } = props;

//数组以序列号一一对应,这是一个有序的对应关系。
const arr = [1, 2, 3];
const [a, b, c] = arr;
let [a, b, c] = [1, 2, 3];

如果变量名与属性名不一致,必须使用完整写法。也就是说,对象的解构赋值的内部机制是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。

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

上面代码中,foo是匹配的模式,baz才是变量。真正被赋值的是变量baz,而不是模式foo。

遍历 Map 结构:

任何部署了 Iterator 接口的对象,都可以用for…of循环遍历。Map 结构原生支持 Iterator 接口,配合变量的解构赋值,获取键名和键值就非常方便。

const map = new Map();
map.set('first', 'hello');
map.set('second', 'world');

for (let [key, value] of map) {
  console.log(key + " is " + value);
}
// first is hello
// second is world

// 只获取键名
for (let [key] of map) {
  // ...
}
// 只获取键值
for (let [,value] of map) {
  // ...
}

默认值

变量默认值

const { loading = false, clicked } = props;

函数参数默认值
在实际开发中给参数添加适当的默认值,可以让我们对函数的参数类型有一个直观的认知。

function add(x = 20, y = 30) {
    return x + y;
}

ES6 内部使用严格相等运算符(===),判断只有当参数严格等于undefined,对会取值为默认值。

展开运算符

在ES6中用…来表示展开运算符。展开运算符可以将数组方法或者对象进行展开。

const arr1 = [1, 2, 3];
const arr2 = [...arr1, 10, 20, 30];//arr2 结果变成了[1, 2, 3, 10, 20, 30];
const obj1 = {
  a: 1,
  b: 2, 
  c: 3
}
const obj2 = {
  ...obj1,
  d: 4,
  e: 5,
  f: 6
}// 结果类似于 const obj2 = Object.assign({}, obj1, {d: 4})

展开运算符还可以声明为变量并接收数据

const props = {
  size: 1,
  src: 'xxxx',
  mode: 'si'
}
const { size, ...others } = props;

展开运算符还用在函数的参数中表示函数的不定参。只有放在最后才能作为函数的不定参,否则会报错。

const add = (a, b, ...more) => {
    return more.reduce((m, n) => m + n) + a + b
}
console.log(add(1, 23, 1, 2, 3, 4, 5)) // 39

对象字面量写法

ES6针对对象字面量做了许多简化语法的处理。
当属性名与值的变量名相同时,在对象结构中可以只写属性名。对象中的方法也可以使用字面量写法。

const name = 'Jane';
const age = 20
// es5
var person = {
  name: name,
  age: age,
  getName: function getName() {
    return this.name;
  }
};
// es6
const person = {
  name,
  age,
  getName() { // 只要不使用箭头函数,this就还是我们熟悉的this
    return this.name
  }
}

还可以使用变量作为对象的键。

const field = 'rock';
const heat = '50%';
const music = {
  [field]: heat
}
console.log(music); // Object {rock: "50%"}

class

ES6为创建对象提供了新的语法糖,就是Class语法。

// ES5
// 构造函数
function Person(name, age) {
  this.name = name;
  this.age = age;
}
// 原型方法
Person.prototype.getName = function() {
  return this.name
}

// ES6
class Person {
  constructor(name, age) {  // 构造函数
    this.name = name;
    this.age = age;
  }
  getName() {  // 原型方法
    return this.name
  }
}

babel会将ES6的写法编译成利用Object.defineProperty实现的方式,这个方法的具体用处大家可以在《JavaScript高级编程3》中学习了解,包括get,set,等都有详细的说明

除此之外,我们还需要特别注意在实际使用中的几种写法方式的不同,在下面的例子注释中,我说明了他们分别对应的ES5中的含义。

class Person {
constructor(name, age) { // 构造函数
this.name = name;
this.age = age;
}

getName() { // 这种写法表示将方法添加到原型中
return this.name
}

static a = 20; // 等同于 Person.a = 20

c = 20; // 表示在构造函数中添加属性 在构造函数中等同于 this.c = 20

// 箭头函数的写法表示在构造函数中添加方法,在构造函数中等同于this.getAge = function() {}
getAge = () => this.age

}
箭头函数需要注意的仍然是this的指向问题,因为箭头函数this指向不能被改变的特性,因此在react组件中常常利用这个特性来在不同的组件进行传值会更加方便。

继承 extends

只需要一个extends关键字,可以实现继承了,不用像ES5那样去担心构造函数继承和原型继承。

//es6
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  getName() {
    return this.name
  }
}

// Student类继承Person类
class Student extends Person {
  constructor(name, age, gender, classes) {
    super(name, age);
    this.gender = gender;
    this.classes = classes;
  }
  getGender() {
    return this.gender;
  }
}

还需要关注super方法。在继承的构造函数中必须调用一次super方法,它表示构造函数的继承,与ES5中利用call/apply继承构造函数是一样的功能。

// 构造函数中
// es6 ,super还可以直接调用父级的原型方法,如super.getName。
super(name, age);

// es5
Person.call(this);

Promise

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。
http://www.jianshu.com/p/fe5f173276bd

模块 Modules

1.引入模块

// src/index.js
import test1 from './test'

上面示例中,import表示引入一个模块,test 暂时理解为引入模块的名字。
引入这个动作执行时,test.js中的代码也执行了。如果在test.js中没有对外暴露任何接口,那么test1为一个空对象。
2.对外提供接口
ES6 modules使用export关键字对外提供接口。
export default对包暴露了一个对象。import 后跟的对象就默认是export default暴露的对象。

export default {
    num,
    arr,
    obj,
    foo
}

还可以通过export暴露更多方法与属性。

export const bar = () => {}
export const zcar = 12345;

可以一次引入获得模块暴露的所有接口。

import * as test1 from './test';

上面示例中的 * 表示所有,这是比较常用的通配符,as表示别名,* as test1的意思是将test.js暴露的所有接口组成的对象,命名为test1。
在引入中使用解析结构,下面示例中test1仍然表示为export default暴露的对象,而 { bar, zcar }则表示从整个返回对象中去取得对应的接口。

import test1, { bar, zcar } from './test';

Symbol

ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,前六种是:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。

Symbol 值通过Symbol函数生成。这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的 Symbol 类型。凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。

let s = Symbol();

typeof s
// "symbol"

上面代码中,变量s就是一个独一无二的值。typeof运算符的结果,表明变量s是 Symbol 数据类型,而不是字符串之类的其他类型。
注意,Symbol函数前不能使用new命令,否则会报错。这是因为生成的 Symbol 是一个原始类型的值,不是对象。也就是说,由于 Symbol 值不是对象,所以不能添加属性。基本上,它是一种类似于字符串的数据类型。

Set和Map

ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。

const s = new Set();
[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));
for (let i of s) {
  console.log(i);
}
// 2 3 5 4

WeakSet 结构与 Set 类似,也是不重复的值的集合。但是,它与 Set 有两个区别。

首先,WeakSet 的成员只能是对象,而不能是其他类型的值。
其次,WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中。

JavaScript 的对象(Object),本质上是键值对的集合(Hash 结构),但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键,是一种更完善的 Hash 结构实现。
Map 遍历有一个forEach方法,与数组的forEach方法类似。

map.forEach(function(value, key, map) {
  console.log("Key: %s, Value: %s", key, value);
});

WeakMap结构与Map结构类似,也是用于生成键值对的集合。

Generator 函数

Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。

前端基础进阶系列
https://www.jianshu.com/p/cd3fee40ef59
ECMAScript 6 入门
http://es6.ruanyifeng.com/

JS基础

字符串和整数混合运算

‘1’+1 结果 ’11’
‘1’-1 结果 0
1+1+’1′ 结果 ’21’
‘1’+1+1 结果 ‘111’

类型转换

1.转Number
数字:转换后还是数字
字符串:如果可以被解析为数值,则为相应的数值,如果不能,则是 NaN,如果是空字符串那就是0
布尔值:true为1,false为0
undefined:NaN
null:0
object:先执行valueOf,看是否能转换,如果不可以再执行toString,看是否可以转换,如果不可以报错
2.转String
数字:转换成对应的字符串
字符串:还是对应的字符串
布尔值:true为’true’,false为’false’
undefined:undefined
null:null
object:先执行toString,看是否能转换,如果不可以再执行valueOf,看是否可以转换,如果不可以报错
3.转Boolean
下面这几个是false,其他都是true
NaN
null
undefined
0
“”
false

4.隐式类型转换
四则运算、判断语句中在隐式类型转换。

逻辑判断

=== 会先判断类型是否相同,再判断内容是否相同
== 只判断内容是否相同
1==’1′ 结果true

逻辑判断

给字符串添加属性并赋值,再打印此属性,值为undefined。这说明并不能给字符串增加属性。

JS基础知识(覆盖JS基础面试题)
https://yq.aliyun.com/articles/608880