ES6特性详解

296 阅读2分钟

增强的 Object 直接量

  • 在直接量中定义 __proto__ 指向父对象
  • foo:foo,可简写为 foo
  • 调用 super
  • 表达式动态生成属性名
var father = {
  name: 'tellyourmad',
  age: 21,
  test: function () {
    console.log(this.age);
  },
};
function intr() {
  for (var key in this) {
    console.log(key + ':' + this[key]);
  }
}
var child = {
  __proto__: father, // child 继承 father
  intr, // 代替了 intr:intr
  saySomething() {
    // 不用写成 saySomething:function(){...}
    console.log(`my name:${this.name}`);
    super.test(); // 调用父类方法
  },
};
child.intr();
child.saySomething();
class Father {
  constructor() {
    this.a = '123';
  }
  output() {
    console.log(this.a);
  }
}

var child_1 = {
  __proto__: Father.prototype,
  say() {
    super.output(); // 输出 undefined
  },
};

var child_2 = {
  __proto__: new Father(),
  say() {
    super.output(); // 输出123
  },
};

解构

  • 同时给多个变量赋值的简化写法
  • 数组与对象同样适用
  • 模式匹配
  • 对象的合并
/*数组*/
var [a, b, c] = [1, 2, 3]; // 同时声明多个变量,左侧的中括号不是数组的意思,同理于var a=1,b=2,c=3
// 如果写成 var [a,,c]=[1,2,3] 那么 a=1,c=3 按下标赋值

var [head, ...tail] = [1, 2, 3, 4]; // head为1,tail为[2,3,4]
var [foo, [[bar], baz]] = [1, [[2], 3]]; // “模式匹配”,只要等号两边的模式相同,左边的变量会被赋予对应的值
var [a, [b], d] = [1, [2, 3], 4]; // a为1,b为2,d为4,解构依然成功,但是属于不完全解构

/*对象*/
var { m: month, y: year } = { y: 2016, m: 11 }; // month为11,year为2016,同理也是按下标赋值	//不是按顺序哦~~
var { a, b, ...others } = { a: 'aaa', b: 'bbb', c: 'ccc', d: 'ddd' }; // a为'aaa',b为'bbb',c为{c:'ccc',d:'ddd'}

function func({ name: x, age: y }) {
  console.log(x, y);
}
var lilei = { name: 'lilei', age: 11 };
func(lilei);

var obj = { ...others, ...{ a: '123', b: '456' }, ...['a', 'b', 'c'] }; // 拼接,还可以将数组也拼进来
// {c:'ccc',d:'ddd',a:'123',b:'456',0:'a',1:'b',3:'c'}

默认值,rest,spread

  • 函数设置参数默认值
function func(gender, name = 'tellyourmad') {
  return `性别:${gender},名称:${name}`;
}
func('男'); // `性别:男,名称:tellyourmad`
  • 使用 rest 简化批量传参
function add(...values) {
  let sum = 0;
  for (var val of values) {
    sum += val;
  }
  return sum;
}
add(1, 2, 3); // 6
  • spread(广播)(不用 apply 也能使函数可以接收数组参数)
function func(a, b) {
  return a + b;
}
const data = [1, 4];
myFunction(...data); // 5

模块化

通过使用 import 和 export 运算符来实现

  • 只导出一个
export default { a: 'aaa', b: 'bbb' };
import obj from '模块名称.js';
  • 导出多个
export var port = 3000;
export function getAccounts(url) {
    ...
};
import { port, getAccounts } from '模块名称.js';
console.log(port); // 3000
  • 冠名(易名)
import * as service from '模块名称.js';
console.log(service.port); // 3000

箭头函数

/*无需bind(this)*/
var a = 'abc';
var obj = {
  a: 'aaa',
  b: 'bbb',
  func: function () {
    var test = () => {
      console.log(this.a);
    };
    test(); // 'aaa'
  },
};

/*当函数体只有一条语句时,可以省略花括号*/
var func = () => console.log('111');

/*当省略花括号时,该单条语句则作为函数的返回值*/
var f1 = () => a++;
// 等价于
var f2 = function () {
  return a++;
}.bind(this);

模板字符串

// 两个撇号'和${}
var a = 'aaaa',
  b = 'bbbb';
var str = `
    a的值是:${a}
    b的值是:${b}
`;

/*声明一个类,作为父类*/
class BaseModel {
  constructor(options = {}, data = []) {
    // 类的构造方法
    this.name = 'Base';
    this.url = 'http://azat.co/api';
    this.data = data;
    this.options = options;
  }

  getName() {
    // 类的方法
    console.log(`Class name: ${this.name}`);
  }
}

/*声明一个类,作为子类,继承BaseModal*/
class AccountModel extends BaseModel {
  constructor(o) {
    super(o, ['32113123123', '524214691']); //使用super调用父类构造方法
    this.name = 'Account Model';
    this.url += '/accounts/';
  }
}

var obj = new AccountModel('oooo');
console.log(obj.options); // 'oooo'
console.log(obj.data); // []
obj.getName(); // 'Class name: Account Model'    // 继承了父类的方法

Promise

  • 写法
/*写法一*/
function connect() {
  return new Promise(function (resolve, reject) {
    if (true) {
      resolve('456');
    } else {
      reject('123');
    }
  });
}
connect()
  .then(function (a) {
    console.log(a);
  }) // resolve方法会进入then
  .fail(function (a) {
    console.log(a);
  }) // reject方法会进入fail
  .catch(function (err) {
    console.log(err);
  }); // 代码报错会进入catch
  • 栗子
function connect() {
  console.log('正在连接');
  return new Promise(function (callback, onError) {
    // 要返回Promise对象,才能用then
    onError(new Error('连接出错')); // 设置错误
    window.setTimeout(callback, 3000);
  });
}
function query() {
  console.log('正在查询');
  return new Promise(function (callback) {
    window.setTimeout(callback, 2000);
  });
}
function update() {
  console.log('正在更新');
  return new Promise(function (callback) {
    window.setTimeout(callback, 1000);
  });
}
connect()
  .then(function () {
    // 将function(){console.log("链接成功");return query()}传给connect里面的callback
    console.log('链接成功');
    return query(); //return下一个Promise
  })
  .then(function () {
    console.log('返回查询结果');
    return update();
  })
  .then(function () {
    console.log('更新成功');
  })
  .catch(function (err) {
    // 在最后加catch的话,如果then中某处出现了错误,这不再继续执行下面的语句,直接执行catch,并且将错误信息传给catch
    console.log(String(err));
  });

let 和 const

let 为变量,const 为常量,其他特性一样,下面只拿 let 来阐述

  • 块级作用域
/*通过let声明的变量只在代码块(也就是花括号)内有效*/
{
  let test = 'aaaa';
}
console.log(test); // 报错

/*不遵循作用链*/
let a = 1;
+function () {
  console.log(a); // 报错
};
  • 不会有变量提升(通过 let 声明的变量不会发生“声明提前”)
console.log(a); // undefined
console.log(b); // 报错
var a = 1;
let b = 2;
  • 在同一个代码块中,不能重复声明同一个变量
let a = 1;
let a = 2; // 报错
  • 声明的变量仅在块级作用域内有效
var a = [];
for (let i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); //6
  • 在 for 循环中,设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域
for (let i = 0; i < 3; i++) {
  let i = 'abc'; // 函数内部的变量i与循环变量不在同一个作用域
  console.log(i); // 输出三次abc
}
  • 暂时性死区
// 只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。
// ES6明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
var tmp = 123;
if (true) {
  tmp = 'abc'; // ReferenceError
  let tmp;
}

for...of 循环

var arr = [10, 50, 10];
for (let i of arr) {
  // 获得数组值
  console.log(i); // 10,50,10
}

// for-of只适用于对数组进行遍历(ps:有些浏览器可能适用于对象,但这是不规范的)
// 若要使用for-of来遍历对象的话,可以先将对象转化为数组,如:
var obj = { a: 'aaa', b: 'bbb' };
for (let [k, v] of Object.entries(obj)) {
  console.log('对象的key值是:' + k);
  console.log('对象的value是:' + value);
}
// 又或者先将对象的n个属性组合成一个数组
for (let k of Object.keys(obj)) {
  console.log('对象的key值是:' + k);
  console.log('对象的value是:' + obj[k]);
}

for of 不能用来遍历对象,因为对象没有迭代器,即 Symbol.iterator

可以通过自己实现一个迭代器来使得对象能通过 for of 进行循环

Object.prototype[Symbol.iterator] = function() {
    const keys = Reflect.ownKeys(this)
    let pointer = 0
    // 返回遍历器对象
    return {
        next: () => pointer < keys.length ? { value: this[keys[pointer++]] } : { done: true },
        return: () => ({ done: true })
    }
}

新 api

二进制和八进制

Symbols

尾部调用优化

generator

更新的数据结构

Set

Map