ES6/7/8新特性总结

字数 1779阅读 313

一、变量let、const:

var存在的问题:1、重复声明;2、函数级

经典案例:

 <body>
  <input type="button" value="1">
  <input type="button" value="2">
  <input type="button" value="3">
</body>
<script>
     window.onload = function () {
      //var aBtns = document.getElementsByTagName('input');
      // 点击每个按钮都弹出“3”
      // for (var i = 0, len = aBtns.length; i < len; i ++) {
      //   aBtns[i].onclick = function () {
      //     alert(i) 
      //   }
      // }
      // 闭包处理点击分别弹出“0、1、2”
      // for (var i = 0, len = aBtns.length; i < len; i ++) {
      //   (function(i) {
      //     aBtns[i].onclick = function () {
      //       alert(i) 
      //     }
      //   })(i);
      // }
      // let 分别弹出“0、1、2”
      // for (let i = 0, len = aBtns.length; i < len; i ++) {
      //   aBtns[i].onclick = function () {
      //     alert(i) 
      //   }
      // }
    }
</script>

let : 不能重复声明、块级、变量
const: 不能重复声明、块级、常量

二、箭头函数=>

1.方便,简写规则:

(1)如果只有一个参数,()可以省
(2)如果只有一个return,{}也可以省

2.修正this

this相对正常点,指向问题、“箭头函数”的this,总是指向定义时所在的对象,而不是运行时所在的对象。

三、参数扩展:...

例子:

function show(a, b, ...arg) {
      console.log(a);// 1
      console.log(b); // 2
      console.log(arg); // [3,4,5,6,7,8,89]
    }
show(1, 2, 3, 4, 5, 6, 7, 8, 89);

let arr = [12, 5, 8];
let arr2 = [...arr, ...arr];
console.log(arr2); // [12, 5, 8, 12, 5, 8]

//解构 可以是嵌套数组、对象

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

四、字符串

传统上,JavaScript 只有indexOf方法,可以用来确定一个字符串是否包含在另一个字符串中。ES6 又提供了三种新方法。

includes():返回布尔值,表示是否找到了参数字符串。
startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。

let s = 'Hello world!';

s.startsWith('Hello') // true
s.endsWith('!') // true
s.includes('o') // true

这三个方法都支持第二个参数,表示开始搜索的位置。

let s = 'Hello world!';

s.startsWith('world', 6) // true
s.endsWith('Hello', 5) // true
s.includes('Hello', 6) // false

上面代码表示,使用第二个参数n时,endsWith的行为与其他两个方法有所不同。它针对前n个字符,而其他两个方法针对从第n个位置直到字符串结束

ES2017 引入了字符串补全长度的功能。如果某个字符串不够指定长度,会在头部或尾部补全。padStart()用于头部补全,padEnd()用于尾部补全。

'x'.padStart(5, 'ab') // 'ababx'
'x'.padStart(4, 'ab') // 'abax'

'x'.padEnd(5, 'ab') // 'xabab'
'x'.padEnd(4, 'ab') // 'xaba'

五、数组

1、map 映射 一个对一个

let arr = [1, 5, 7];
    let result = arr.map(function(item) {
      return item * 2;
    })
// let result=arr.map(item=>item*2); // 简写
    alert(result);  // 2,10,14

 let score = [30, 60, 100, 10, 80];
    let result  = score.map(item => item >= 60? '及格': '不及格');
    console.log(score); // [30, 60, 100, 10, 80]
    console.log(result) // ["不及格", "及格", "及格", "不及格", "及格"]

2、reduce 总汇:一堆 -> 一个(算总数、平均数等)

参数说明:*第一个参数是上次reduce函数运算的结果
*第二个参数是下次要参与运算的元素

let arr = [12, 69, 180, 8763];
    let result = arr.reduce(function (tmp, item, index) {
      console.log(`${tmp},${item},${index}`);// 12,69,1    81,180,2    261,8763,3
      return tmp + item;
    });
    console.log(result); // 9024

3、filter 过滤: 一堆-> 剩下的

 let arr = [
      {title: '男士衬衫', price: 75},
      {title: '女士包', price: 57842},
      {title: '男士包', price: 65},
      {title: '女士鞋', price: 27531}
    ];
    let result = arr.filter(item => item.price > 10000)
    console.log(result);// [{title: '女士包', price: 57842},{title: '女士鞋', price: 27531}]

4、forEach 循环(迭代)

 let arr = [12, 5, 8];
    arr.forEach((item, index) => {
      if (index > 1) {
        // break; // 报错 不能用在function中
        // continue; // 报错 不能用在function中
        //return; // 0:12 1:5
      }
      console.log(`${index}:${item}`);// 0:12 1:5 2:8
    });

5、includes数组是否包含某个东西

该方法的第二个参数表示搜索的起始位置,默认为0。如果第二个参数为负数,则表示倒数的位置,如果这时它大于数组长度(比如第二个参数为-4,但数组长度为3),则会重置为从0开始。

[1, 2, 3].includes(3, 3);  // false
[1, 2, 3].includes(3, -1); // true

6、 keys/values/entries

它们都返回一个遍历器对象,可以用for...of循环进行遍历,唯一的区别是keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历。

for (let index of ['a', 'b'].keys()) {
  console.log(index);
}
// 0
// 1

for (let elem of ['a', 'b'].values()) {
  console.log(elem);
}
// 'a'
// 'b'

for (let [index, elem] of ['a', 'b'].entries()) {
  console.log(index, elem);
}
// 0 "a"
// 1 "b"

五、Promise(封装异步操作);

// 1、基本用法
   let promise = new Promise((resolve, reject) => {
      $.ajax({
        url: 'url',
        dataType: 'json',
        success(res) {
          resolve(res);
        },
        error(err) {
          reject(err);
        }
      })
    })
    promise.then(res => {
      console.log(res); // 成功
    }, err => {
      console.log(err);// 失败
    })
//2、 Promise.all,在一个接口中用到另一个接口中的数据
    Promise.all([
      $.ajax({url: 'url', dataType: 'json'}),
      $.ajax({url: 'url', dataType: 'json'})
    ]).then(results => {
      let [data1, data2] = results;
      console.log(data1, data2);
    }, () => {
      alert('失败了');
    });

六、generator生成器 yield表达式;

 function* show(num1, num2) {
      console.log(`${num1}, ${num2}`); // 6,2
      let n1 = num1 + num2;
      let a = yield n1; 
      console.log(a); // 2
      let n2 = num1 - num2;
      let b = yield n2;
      console.log(b) // 3
      let n = n1 * n2;
      return n;
    }
    let gen = show(6, 2);
    console.log(gen.next(1)); // {value: 8, done: false}
    console.log(gen.next(2));  // {value: 4, done: false}
    console.log(gen.next(3)); // {value: 32, done: true}
    console.log(gen.next(4)); // {value: undefined, done: true}
    
    function *show() {
      yield $.ajax({url: 'url', dataType: 'json'});    
      return $.ajax({url: 'url', dataType: 'json'})
    }
    let genObj = show();
    console.log(genObj.next()); // {value: {…}, done: false}
    console.log(genObj.next()); // {value: {…}, done: true}

七、async await

与generator对比:1.不依赖于外部的runner了——统一、性能2.可以用箭头函数;

八、面向对象 类 Class

 /* 类Class */
    // 先看 原型-面向对象
    // 定义一个用户类
    function User (name, phone) { 
      this.name = name || 'user';  // 属性
      this.phone = phone || '000'; // 属性
      this.showName = function () {
        console.log(`姓名:${this.name}`);
      }
    }
    User.prototype.showUserInfo = function (id) {
      console.log(`ID:${id}/姓名:${this.name}/手机号:${this.phone}`);
    }
    let user = new User('小明', '110');
    console.log(user);
    user.showName();
    user.showUserInfo('666');
    // 定义一个vip用户子类
    function VipUser(name, phone, level) {
      User.call(this, name, phone, level);
      this.level = level;
    }
    VipUser.prototype = new User(); // 将父级实例作为子类的原型
    VipUser.prototype.constructor = VipUser; // 需要修复构造函数指向

    VipUser.prototype.showVipInfo = function() { // 箭头函数会造成this指向问题
      console.log(`姓名:${this.name}/会员等级:${this.level}颗星/手机号:${this.phone}`)
    }
    let v1 = new VipUser ('老王', '12345678', '10')
    console.log(v1);
    v1.showUserInfo(123);
    v1.showVipInfo();

//对比
// es6 写法
    class User {
      constructor(name, phone) {
        this.name = name || 'user';  // 属性
        this.phone = phone || '000'; // 属性
      }
      showName () {
        console.log(`姓名:${this.name}`);
        // this.showPhone(); 
        // User.showPhone(this.phone); 
      }
      static showPhone(phone) {  // 只能内部使用,不能通过this/实例化方法调用
        // console.log(`手机号:${this.phone}`);
        console.log(`手机号:${phone}`);
      }
      showUserInfo (id) {
        console.log(`ID:${id}/姓名:${this.name}/手机号:${this.phone}`);
      }
    }
    let user = new User('小明', '110');
    console.log(user);
    user.showName();
    // user.showPhone();
    user.showUserInfo('666');
    class VipUser extends User {
      constructor(name, phone, level) {
        super(name, phone);
        this.level = level;
      }
      showVipInfo () { 
        console.log(`姓名:${this.name}/会员等级:${this.level}颗星/手机号:${this.phone}`)
      }
    }
    let v1 = new VipUser ('老王', '12345678', '10')
    console.log(v1);
    v1.showUserInfo(123);
    v1.showVipInfo();

九、JSON

  JSON.stringify({a:12,b:5})  =>  '{"a":12,"b":5}' // 从一个对象中解析出字符串
  JSON.parse('{"a":12,"b":5}')=>  {a:12,b:5} // 从一个字符串中解析出JSON对象

十、Symbol

ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,前六种是:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。作用:作为对象属性的唯一标识符,防止对象属性冲突发生。

  // let a = Symbol();
    // let b = Symbol();
    // console.log(a === b); // false
    // console.log(typeof(a)); // symbol

// let obj = {};
    // let isOk = Symbol();
    // obj[isOk] = 'ok';
    // console.log(obj[isOk]);   // ok
    // console.log(obj.isOk);   // undefined  不能用点

   //Symbol.for会根据给定的键 key,来从运行时的 symbol 注册表中找到对应的 symbol,
    //如果找到了,则返回它,否则,新建一个与该键关联的 symbol,并放入全局 symbol 注册表中。
    //  let sym = Symbol('abc');
    //  let sym1 = Symbol.for('abc');
    //  let sym2 = Symbol.for('abc');
    //  console.log(sym === sym1); // false
    //  console.log(sym1 === sym2);// true

十一、Iterator

JavaScript 原有的表示“集合”的数据结构,主要是数组(Array)和对象(Object),ES6 又添加了Map和Set。这样就有了四种数据集合,用户还可以组合使用它们,定义自己的数据结构,比如数组的成员是Map,Map的成员是对象。这样就需要一种统一的接口机制,来处理所有不同的数据结构。

遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。

Iterator 的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是 ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费。

Iterator 的遍历过程是这样的。
(1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
(2)第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
(3)第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
(4)不断调用指针对象的next方法,直到它指向数据结构的结束位置。

每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含value和done两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。

for...of...不能循环对象是因为

Iterator 接口的目的,就是为所有数据结构,提供了一种统一的访问机制,即for...of循环(详见下文)。当使用for...of循环遍历某种数据结构时,该循环会自动去寻找 Iterator 接口。

原生具备 Iterator 接口的数据结构如下。
Array
Map
Set
String
TypedArray
函数的 arguments 对象
NodeList 对象

没有提供Object的数据机构接口

十二、Set 和 Map 数据结构

ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。Set 本身是一个构造函数,用来生成 Set 数据结构。

    let a = new Set([1, 2, 3]);
    let b = new Set([5, 3, 2]);
    // 并集
    let union = new Set([...a, ...b]);
    console.log(union);// Set {1, 2, 3, 4}
    // 交集
    let intersect = new Set([...a].filter(x => b.has(x)));
    console.log(intersect)// set {2, 3}

JavaScript 的对象(Object),本质上是键值对的集合(Hash 结构),但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。

const data = {};
const element = document.getElementById('myDiv');

data[element] = 'metadata';
data['[object HTMLDivElement]'] // "metadata"

上面代码原意是将一个 DOM 节点作为对象data的键,但是由于对象只接受字符串作为键名,所以element被自动转为字符串[object HTMLDivElement]。

为了解决这个问题,ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。

const m = new Map();
    const o = {p: 'Hello World'};
    m.set(o, 'content')
    console.log(m); // Map(1) {{…} => "content"}
    console.log(m.get(o)); // "content"
    console.log(m.has(o)); // true
    m.delete(o);
    console.log(m.has(o)) // false

上面代码使用 Map 结构的set方法,将对象o当作m的一个键,然后又使用get方法读取这个键,接着使用delete方法删除了这个键。

推荐阅读更多精彩内容