Fork me on GitHub

ES6

ES6 ES2015

所有属性

详情见 简书 ES6

ES6

  • let const
  • =>箭头函数
    没有 arguments,可以使用扩展运算符(…)传递不定参数
    不能作为构造函数
    适应于那些本来就需要匿名函数的地方
  • Set,Map
  • 解构赋值
  • for of 遍历数组、Set、Map 结构、类数组,字符串
  • es6 Module
  • 扩展操作符
  • class,extends
  • Symbol
  • Proxy 代理,第一代理监听对象的操作
    用于定义基本操作的自定义行为(如属性查找,赋值,枚举,函数调用等)
    Reflect是一个内置对象,提供拦截 JS 操作的方法。这些方法与Proxy的方法相同。Reflect不是一个函数对象,因此它是不可构造的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    const observe = (data, callback) => {
    return new Proxy(data, {
    get(target, key) {
    return Reflect.get(target, key)
    },
    set(target, key, value, proxy) {
    callback(key, value);
    target[key] = value;
    return Reflect.set(target, key, value, proxy)
    }
    })
    }
    const FooBar = {
    open: false
    };
    const FooBarObserver = observe(FooBar, (property, value) => {
    property === 'open' && value ? console.log('FooBar is open!!!') : console.log('keep waiting');
    })
    console.log(FooBarObserver.open) //false
    FooBarObserver.open = true; //FooBar is open!!!
    console.log(FooBarObserver.open) //true

    如果对象带有configurable: falsewritable: false属性,则代理失效。

  • Promise 见JS-Promise

  • 函数参数默认值
  • 模板字符串
  • 对象属性简写
  • 迭代器、生成器
1
2
3
4
5
6
7
8
9
10
11
12
function* makeRangeIterator(start = 0, end = Infinity, step = 1) {
for (let i = start; i < end; i += step) {
yield i
}
}
var a = makeRangeIterator(1, 10, 2);
console.log(a.next()); //{value: 1, done: false}
console.log(a.next()); //{value: 3, done: false}
console.log(a.next()); //{value: 5, done: false}
console.log(a.next()); //{value: 7, done: false}
console.log(a.next()); //{value: 9, done: false}
console.log(a.next()); //{value: undefined, done: true}
  • Set/WeakSet
    Set对象允许存储任何类型的惟一值,无论是原始值还是对象引用。可以使用Set去重。
    WeakSetSet的区别:

    • WeakSet对象只存放对象的引用,不存放值。
    • WeakSet对象中存储的对象值都是被弱引用的, 如果没有其他的变量或属性引用这个对象值, 则这个对象值会被当成垃圾回收掉. 正因为这样, WeakSet对象是无法被枚举的, 没有办法拿到它包含的所有元素。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    let ws = new WeakSet();
    let obj = {};
    let foo = {};
    ws.add(window);
    ws.add(obj);
    console.log(ws.has(window)); //true 是否含有对象
    console.log(ws.has(foo)); //false
    ws.delete(window); //删除
    console.log(ws.has(window));
    ws.clear() // 清空整个 WeakSet 对象
  • Map/WeakMap
    Map对象保存键值对。任何职(对象或原始值)都可以作为一个键或一个值。

    1
    2
    3
    4
    5
    var myMap = new Map();
    myMap.set(NaN, 'Not a number');
    console.log(myMap.get(NaN)); //'not a number'
    let otherNaN = Number('foo');
    console.log(myMap.get(otherNaN)) //'not a number'

    WeakMap对象是一组键/值对的集合,其中的键是弱引用的。其键必须是对象,而值可以是任意的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    let wm1 = new WeakMap();
    let wm2 = new WeakMap();
    let o1 = {},
    o2 = function () {},
    o3 = window;
    wm1.set(o1, 37);
    wm1.set(o2, 'aaa');
    wm2.set(o1, o2); //value 可以是任意值,包括对象
    wm2.set(o3, undefined);
    console.log(wm1.get(o1));
    console.log(wm1.get(o2));
    console.log(wm2.get(o2));
    console.log(wm2.get(o3));
    console.log(wm1.has(o2)); // true
    console.log(wm2.has(o2)); // false
    wm1.delete(o1);
    console.log(wm1.get(o1)); //undefined
  • Math对象的扩展

    • Number.EPSILON:数值最小精度
    • Number.isFinite() : 是否为有限数值
    • Number.isNaN() : 是否为 NaN
    • Number.isInteger() : 是否为整数
    • Number.isSafeInteger() : 是否在数值安全范围内
  • Array对象的扩展

    • Array.from() 转换具有Interator接口的数据结构为真正的数组,返回新数组。
    • Array.of() 将参数转换为数组,返回新数组

      1
      2
      3
      4
      Array.of(7)       // [7]
      Array.of(1, 2, 3) // [1, 2, 3]
      Array(7) // [empty, empty, empty, empty, empty, empty]
      Array(1, 2, 3) // [1, 2, 3]
    • Array.prototype.copyWithin(target,start[,end]) 把指定位置的成员复制到其他位置,返回新数组

      1
      2
      3
      const array1 = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
      console.log(array1.copyWithin(0, 3, 4));//["d", "b", "c", "d", "e", "f", "g"]
      console.log(array1.copyWithin(1, 3));//["d", "d", "e", "f", "g", "f", "g"]
    • Array.prototype.find():返回第一个符合条件的成员

    • Array.prototype.findIndex():返回第一个符合条件的成员索引值
    • Array.prototype.fill(value, start,end):根据指定值填充整个数组,修改并返回原数组
      • value,数组的填充值
      • start, 开始下标,默认 0
      • end,结束下标,默认数组长度,缺省或者结束下标大于数组长度-开始坐标时填充到数组最后一个元素
    1
    2
    3
    4
    5
    6
    7
    const array1 = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
    array1.fill(0, 2, 4);
    console.log(array1); //["a", "b", 0, 0, "e", "f", "g"]
    array1.fill(5, 4);
    console.log(array1);//["a", "b", 0, 0, 5, 5, 5]
    array1.fill(7, 4, 10);
    console.log(array1);// ["a", "b", 0, 0, 7, 7, 7]

面试题

1. let、const、var的区别

  • var声明的变量存着变量提升,letconst声明的变量存在暂时性死区不会提升。
  • var 声明的变量只有全局作用域和函数作用域,没有块级作用域,但是letconst声明的变量存着块级作用域(一对{}之间);
  • 全局作用域下letconst什么的变量不会被挂载到 window 上,但是var声明的全局变量会挂载到 window 上
  • var可以重复声明相同变量,letconst会报错
  • const声明后必须赋值,否则报错。改变const变量也会报错

2. 暂存性死区

在变量初始化前访问该变量会导致 ReferenceError。该变量处在一个自块顶部到初始化处理的“暂存死区”中。

1
2
3
4
5
6
7
function test(){
var foo = 33;
{
let foo = (foo + 55); // ReferenceError
}
}
test();

3. 模块化

为什么要模块化?好处

  • 解决命名冲突
  • 提供复用性
  • 提供代码可维护性

立即执行函数

1
2
3
4
(function(globalVariable){
globalVariable.test = function() {}
// ... 声明各种变量、函数都不会污染全局作用域
})(globalVariable)

AMD 和 CMD

1
2
3
4
5
6
7
8
9
10
11
12
//AMD
define(['./a','./b'],function(a,b){
//加载模块完毕可以使用
a.do();
b.do()
})
//CMD
define(function(require,exports,module){
//加载模块
var a=require('./a.js');
a.do()
})

CommonJS

1
2
3
4
5
6
7
8
//a.js
let a=1;
module.exports={a};
//or
exports.a=1;
//b.js
var module=require('./a.js');
module.a;//1

详见《JS-CommonJS》
ES Module
ES Module 是原生实现的模块化方案,与 CommonJS 有以下几个区别:

  • CommonJS支持动态导入,就是require(${path}/xx.js)ES Module不支持,但是已有提案。
  • CommonJS是同步导入,因为用于服务端,文件都在本地,同步导入即使卡住主线程影响也不是很大。ES Module是异步导入,因为浏览器需要下载文件,如果也采用同步导入就会对渲染有很大影响。
  • CommomnJS在导出时是值拷贝,导入之后就跟脚本的变化无关,如果需要更新就要清除缓存,重新导入。ES Module采用实时绑定的方式,导入导出的值指向同一内存地址,,所以导入值会跟随导出值变化。
1
2
3
4
5
6
7
8
9
// 引入模块 API
import XXX from './a.js'
import { XXX } from './a.js'
// 导出模块 API
export let a=1;
export function a() {}
export default function() {}
let a=1;
export {a as arg1}

注意:

  1. 重命名使用as
  2. import 命令输入的变量都是只读的,因为它的本质是输入接口。也就是说,不允许在加载模块的脚本里面改写接口,报错。
  3. import 命令具有提升效果,会提升到整个模块的头部,首先执行。
  4. import 是静态执行,所以不能使用表达式和变量,这些只有在运行时才能得到结果的语法结构。

4.使用箭头函数应该注意什么

  • 箭头函数里面的this不在指向window,而是父级
  • 不可以作为构造函数,不能使用new命令
  • 不可以使用 arguments 对象,不存在,可以使用 rest 代替
  • 不可以使用 yield 命令,因此箭头函数不能用作 Generator 函数

5.ES6 模板字符串有哪些新特性?并实现一个类模板字符串的功能

  • 基本的字符串格式化。
  • 字符串拼接
1
2
3
4
5
6
7
let name = 'web';
let age = 10;
let str = '你好,${name} 已经 ${age}岁了'
str = str.replace(/\$\{([^}]*)\}/g,function(){
return eval(arguments[1]);
})
console.log(str);//你好,web 已经 10岁了

6.Set 和 Map 的区别

  • Set
    • 成员不能重复
    • 键名和键值相同,类似数组
    • 可以遍历
    • 所有值按照设置顺序排列
    • 无法通过迭代器直接改变值(因为键值就是键名)
  • Map:
    • 本质上是健值对的集合,类似集合
    • 键名不允许重复
    • 可以遍历,可以跟各种数据格式转换
    • 键是不能修改的,但是其键对应的值是可以修改的

7.使用结构赋值,实现两个变量的值的交换

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

8.设计一个对象,键名的类型至少包含一个 symbol 类型,并且实现遍历所有 key

1
2
3
4
5
6
let obj={
name:'Lily',
[Symbol('Lily')]:'student'
}

let keys = Reflect.ownKeys(obj);

对象遍历方法

9.set

1
2
3
4
5
6
7
8
9
let s = new Set();
s.add(1);
s.add(2);
let a={foo:1}
s.add(a)
console.log(s.size);//2
s.has(1) //true
s.delete(1)//true
s.clear()

注意:数组(对象)是引用类型,所以两个是不相等的。另外在 Set 内部,两个 NaN 是相等

10.理解 async/await 以及对 Generator 的优势

async 函数是 Generator 函数的语法糖。
当函数执行的时候,一旦遇到 await 就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。可以理解,await 后面的语句是放到 Promise.then()去执行的。
async 较 Generator 的优势:
(1)内置执行器。Generator 函数的执行必须依靠执行器,而 Aysnc 函数自带执行器,调用方式跟普通函数的调用一样
(2)更好的语义。async 和 await 相较于 * 和 yield 更加语义化  
(3)更广的适用性。yield 命令后面只能是 Thunk 函数或 Promise 对象,async 函数的 await 后面可以是 Promise 也可以是原始类型的值
(4)返回值是 Promise。async 函数返回的是 Promise 对象,比 Generator 函数返回的 Iterator 对象方便,可以直接使用 then() 方法进行调用

11. forEach、for in、for of 三者区别

  • forEach更多的用来遍历数组
  • for key in 一般常用来遍历对象或 json
  • for of数组,可以通过 Object.keys()获取对象 key 遍历对象

12. Proxy 来实现一个数据响应式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
let onWatch = (obj, setBind, getLogger) => {
let handler = {
get(target, property, receiver) {
getLogger(target, property)
return Reflect.get(target, property, receiver)
},
set(target, property, value, receiver) {
setBind(value, property)
return Reflect.set(target, property, value)
}
}
return new Proxy(obj, handler)
}
let obj = {
a: 1
}
let p = onWatch(obj,
(v, property) => {
console.log(`监听到属性'${property}'改变为${v}`)
},
(target, property) => {
console.log(`获取属性'${property}'的值为${target[property]}`)
}
)
p.a; //获取属性'a'的值为1
p.a = 2; //监听到属性'a'改变为2
p.a //获取属性'a'的值为2

搭建环境

利用 gulp 将 es6 转为 es5
目录结构
目录结构
全局模块安装

1
sudo npm install babel-cli -g

package.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"name": "learn-es6",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "babel src/index.js -o dist/index.js"
},
"author": "",
"license": "MIT",
"dependencies": {},
"devDependencies": {
"babel-core": "^6.26.3",
"babel-preset-es2015": "^6.24.1",
"browser-sync": "^2.26.3",
"gulp": "^4.0.0",
"gulp-babel": "^7.0.1"
}
}

babel 配置

1
2
3
4
5
6
{
"presets": [
"es2015"
],
"plugins":[]
}

gulp 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var gulp = require('gulp');
var babel = require('gulp-babel');
var browserSync = require('browser-sync').create();
var reload = browserSync.reload;
gulp.task('js',() => {
return gulp.src('./src/index.js')
.pipe(babel({
presets:['es2015']
}))
.pipe(gulp.dest('./dist/'))
.pipe(reload({stream:true}))
})
gulp.task('default',() => {
browserSync.init({
server:{
baseDir:'./'
},
port:'8080'
});
gulp.watch('./src/*.js',gulp.series(['js']))
})
-------------本文结束感谢阅读-------------