ECMAScript 2015 新特性

文章内容输出来源:拉勾大前端高薪训练营

1、let 与 块级作用域

  • 作用域——某个成员能够起作用的范围。
    在ES2015之前,ES中只有两种作用域:全局作用域和函数作用域
    ES2015 新增 块级作用域,{} 包裹的范围
  • let 必须先声明,再使用

2、const 恒量/常量 跟let 的区别是 只读

const 申明的同时就必须有个初始值,它申明的成员不能修改。
它的不能修改是指:不能在申明之后重新指向一个新的内存地址,恒量中的属性成员是可以修改的

声明对象的关键词有三种:var、let、const
最佳实践:不用 var, 主用 const,配合let

var elements = [{}, {}, {}];
// for (var i = 0; i < elements.length; i++) {
//   elements[i].onclick = function(){
//      console.log(i)
//   }
// }
// elements[1].onclick(); // 3
// 传统的解决方案
// for (var i = 0; i < elements.length; i++) {
//   elements[i].onclick = (function(i){
//     return function(){
//       console.log(i)
//     }
//   })(i)
// }
// elements[1].onclick(); // 1
// es2015 解决方案:let
// for (let i = 0; i < elements.length; i++) {
//   elements[i].onclick = function(){
//     console.log(i)
//   }
// }
elements[1].onclick(); // 1

// const name = '123';
// const obj = {}
// obj.name = 'hhh';

3、数组的解构:根据位置提取数据

// const arr = [100, 200, 300];
// const [foo, bar, baz] = arr;
// console.log(foo, bar, baz); //100 200 300 
//指定位置的值
// const [, , baz] = arr;
// console.log(baz); // 300
//剩余值
// const [foo, ...rest] = arr;
// console.log(rest);// [ 200, 300 ]
//多于的变量
// const [foo, bar, baz, more] = arr;
// console.log(more); // undefined
//默认值
// const [foo, bar, baz=123, more='default'] = arr;
// console.log(baz, more); // 300 default

const path = '/foo/bar/baz';
// const tmp = path.split('/');
// const rootdir = tmp[1];

const [, rootdir] = path.split('/');
console.log(rootdir); //foo

4、对象的解构:根据属性名提取数据

const obj = {name: 'zs', age: 18};
// const { name, age } = obj;
// console.log(name, age);

//同名变量解构
// const name = 'tom';
// const { name: ObjName } = obj;
// console.log(ObjName);

//默认值
// const name = 'tom';
// const { tname: ObjName = 'jack' } = obj;
// console.log(ObjName);

const { log } = console;
log('a');

5、模板字符串

const name = 'zs';
const str = `hey \` ${name} \`, ${1 + 2} `
console.log(str); // hey ` zs ` , 3

6、带标签的模板字符串:标签就是函数,标签字符串就是调用这个函数

返回的结果是:模板字符串中内容分割过后的数组

// const str = console.log`hello word` //[ 'hello word' ]
const name = 'tom';
const gender = true;
// function myTag(str){
//   console.log(str);
// // } // [ 'hey, ', ' is a ', '' ]
// function myTag(str, name, gender){
//   console.log(str, name, gender);
// } // [ 'hey, ', ' is a ', '' ] tom true
// function myTag(str, name, gender){
//   // console.log(str, name, gender);
//   return 123;
// } 
// const result = myTag`hey, ${name} is a ${gender}`;
// console.log(result); // 123
function myTag(str, name, gender){
  const sex = gender ? 'man' : 'woman';
  return str[0] + name + str[1] + sex;
} 
const result = myTag`hey, ${name} is a ${gender}`;
console.log(result); // hey, tom is a man

7、字符串的扩展方法:includes(), startsWidth(), endsWith() : 判断字符串是否包含指定的内容

const message = 'Error: foo is not defined.';
console.log(message.includes('foo')) // true
console.log(message.startsWith('Error')) // true
console.log(message.endsWith('.')) // true

8、参数默认值: 没有实参或者实参传递的是undefined 的时候,默认值才会被使用

多个参数时,带有默认值的形参要放在参数列表的最后

//8、参数默认值
// function foo(enable){
//   // enable = enable || true; //不准确 传入false的时候,也会使用默认值
//   enable = enable == undefined ? true : false;
//   console.log(enable);
// }
function foo(bar=0, enable = true){
  console.log(bar, enable)
}
foo(); // 0, true

9、剩余参数: 因为接收的是所有的参数,所以只能放在形参的最后,而且只能使用一次

// function foo(){
//   console.log(arguments); //[Arguments] { '0': 1, '1': 2, '2': 3 }
// }
function foo(...args){
  console.log(args); // [ 1, 2, 3 ]
}
foo(1, 2, 3)

10、展开数组

const arr = ['foo', 'bar', 'baz'];
console.log.apply(console, arr); //foo bar baz
console.log(...arr); //foo bar baz

11、箭头函数

// function inc(num){
//   return num + 1;
// }
// const inc = n => n + 1;
// console.log(inc(100));
const arr = [1, 3, 2, 5, 2, 6];
// const newArr = arr.filter(function(item){
//   return item % 2;
// });
const newArr = arr.filter( i => i % 2 )
console.log(newArr);

12、箭头函数与this: 箭头函数不会改变this 的指向

const person = {
  name: 'tom',
  // sayHi: function(){
  //   console.log(`hi, my name is ${this.name}`)
  // },
  sayHi: () => {
    console.log(`hi, my name is ${this.name}`); //this.name undefined
  },
  sayHiAsync: function() {
    setTimeout( () => {
      console.log(this.name) // tom
    }, 10) 
  },
}
// person.sayHi();
person.sayHiAsync();

13、对象字面量的增强

  • 对象添加的属性名和变量名相同可以省略( :和后面属性名)
  • 对象添加的方法 可以省略(:和 function),方法内部使用this是指向当前的对象的
  • 对象动态添加属性 可以使用 [],称为 计算属性名
const bar = '124';
const obj = {
  foo: '222',
  // bar: bar,
  bar,
  // method1: function(){
  //   console.log('mothod1')
  // },
  method1() {
    console.log('mothod1')
    // console.log(this)
  },
  [Math.random()]: 123
}
console.log(obj)
obj.method1();

14、对象扩展方法:

  • Object.assign(): 将多个源对象中的属性复制到一个目的对象中,对象之间有相同的属性,源对象中的属性会覆盖掉目标对象中的属性
  • Object.is(): 同值比较,特殊使用,可以判断 Object.is(+0, -0) 是false, Object.is(NaN, NaN) 是 true
// const source1 = { a: 1, b: 2 };
// const source2 = { b: 100, d: 300 };
// const target = { a: 2, c: 3 };
// const result = Object.assign(target, source1, source2);
// console.log(result); //{ a: 1, c: 3, b: 100, d: 300 }
// console.log(result === target); // true

// function func(obj) {
//   obj.name = 'func obj';
//   console.log(obj);
// }
// const obj = { name: 'global obj'}
// func(obj); // { name: 'func obj' }
// console.log(obj); //{ name: 'func obj' }

function func(obj) {
  const funcObj = Object.assign({}, obj);
  funcObj.name = 'func obj';
  console.log(funcObj);
}
const obj = { name: 'global obj'}
func(obj); // { name: 'func obj' }
console.log(obj); //{ name: 'global obj' }


console.log(Object.is(+0, -0)); //false
console.log(NaN == NaN); //false
console.log(Object.is(NaN, NaN)) // true

15、proxy 代理对象(以前使用Object.defineProperty 监视对象的属性读写)

第一个参数,需要代理的目标对象,第二个参数,也是对象是代理的处理对象

const person = { name: 'zs', age: 20 }
const personProxy = new Proxy(person, {
  get(target, property){
    //console.log(target, property)
    return property in target ? target[property] : undefined;
  },
  set (target, property, value){
    //console.log(target, property, value)
    if(property === 'age'){
      if(!Number.isInteger(value)){
        throw new TypeError(`${value} is not an int`)
      }
    }
    target[property] = value;
  }
  deleteProperty( target, property){
    console.log(target, property);
    delete target[property]
  }
})
personProxy.gender = true;
personProxy.age = 100
console.log(personProxy.name)
delete personProxy.age;
console.log(personProxy)

proxy 对比 defineProperty 的优势:proxy 更强大一些,具体体现在:

  • defineProperty 只能监视属性的读写,proxy 可以监视到更多对象操作,例如delete等
  • Proxy 更好的支持数组对象的监视,(原始的是通过重写数组的操作方法)
  • Proxy 是以非侵入的方式监管了对象的读写
const list = []
const listProxy = new Proxy( list, {
  set(target, property, value){
    console.log('set', property, value);
    target[property] = value;
    return true; // 表示设置成功
  }
});
listProxy.push(100)

16、Reflect:统一的对象操作API,

  • 属于一个静态类,不能通过new构建实力对象,可以调用它的静态方法(相同的是 Math)
  • 它的内部封装了一系列对对象的底层操作, Reflect成员方法就是proxy处理对象的默认实现
  • 它的价值体现在:统一提供一套用于操作对象的API
const obj = { foo: '123', bar: '455', age: 23 };
// const proxy = new Proxy( obj, {
//   get(target,  property){
//     console.log('watch logic....');
//     return Reflect.get(target, property);
//   }
// })
// console.log(proxy.foo);

// console.log('foo' in obj);
// console.log( delete obj['age']);
// console.log(Object.keys(obj));
//使用Reflect 实现
console.log(Reflect.has(obj, 'foo'));
console.log(Reflect.deleteProperty(obj, 'age'));
console.log(Reflect.ownKeys(obj));

17、Promise: 一种更优的异步编程解决方案,解决了传统异步编程中回调函数嵌套过深的问题

具体详情见:Promise:一种更优的异步编程统一方案 学习笔记

18、class 类 和 类的继承 extends

// function Person(name){
//   this.name = name
// }
// Person.prototype.say = function(){
//   console.log(`hi, my name is ${this.name}`)
// }
// let per = new Person('zs');
// per.say();
class Person{
  constructor(name){
    this.name = name
  }
  say() {
    console.log(`hi, my name is ${this.name}`)
  }
}
const per = new Person('zs');
per.say();
class Student extends Person{
  constructor(name, number){
    super(name)
    this.number = number;
  }
  hello(){
    super.say();
    console.log(`${this.name}'s school number is ${this.number}`)
  }
}
let s = new Student('zs', '100');
s.hello();

19、static 静态成员, 在类型当中的方法一般分为: 实力方法 和 静态方法

  • 实例方法:通过这个类型构造的实例对象调用
  • 静态方法:直接通过类型本身调用
  • 静态方法是挂载在类型上面的,所以它的this不会指向某一个实例对象,而是当前的类型
class Person{
  constructor(name){
    this.name = name
  }
  say() {
    console.log(`hi, my name is ${this.name}`)
  }
  static create(name){
    return new Person(name)
  }
}
const tom = Person.create('tom');
tom.say();

20、Set 数据结构: 内部成员不允许重复,可以用来处理数组去重

// const s = new Set();
// s.add(1).add(3).add(2).add(4).add(2);
// console.log(s);
// s.forEach(i => console.log(i));
// for(let i of s){
//   console.log(i)
// }
// console.log(s.size); // 4
// console.log(s.has(100)); //false
// console.log(s.delete(3)) //true
// console.log(s);// Set(3) { 1, 2, 4 }
// s.clear();
// console.log(s); //Set(0) {}
const arr = [1, 3, 5, 6, 8, 1, 3];
// const result = Array.from(new Set(arr)); //数组去重
const result = [...new Set(arr)];
console.log(result);

21、Map 数据结构:类似对象,本质都是键值对集合,

但对象中的键只能是字符串类型
Map 可以存放复杂的键,可以用任意类型的数据作为键

// const obj = {}
// obj[true] = 'value1';
// obj[123] = 'value2';
// obj[{a: 1}] = 'value3';
// console.log(Object.keys(obj));//[ '123', 'true', '[object Object]' ]
// console.log(obj['[object Object]']); //value3
const m = new Map();
const tom = { name: 'tom' };
m.set(tom, 90);
console.log(m); //Map(1) { { name: 'tom' } => 90 }
console.log(m.get(tom)); // 90
m.forEach((value, key) => {
  console.log(value, key);
});// 90 { name: 'tom' }

22、Symbol 一种全新的原始数据类型,最主要的作用 就是为对象添加独一无二的属性名

  • 因为每一个symbol 都是独一无二的,不能通过属性名来取值,可以用来定义私有成员,通过对象的方法来获取值
  • 如果想全局 复用 Symbol 的值,可以使用 Symbol.for(string),相同的字符串 返回相同的Symbol值
  • Symbol.for() 在内部维护了全局注册表,为字符串和 symbol 值提供了一一对应的关系,如果传入的不是字符串,内部会自动把它转化成字符串。
  • Symbol 作为属性名,通过 for...in 是无法拿到的, Object.keys() 也拿不到,在 JSON.stringify() 中也会被忽略
  • Object.getOwnPropertySymbols(obj) 只能获取到 symbol 的属性名
  • Object.keys() 只能获取到 字符串类型的属性名
// console.log(Symbol() === Symbol()) //false
// console.log(Symbol(1) === Symbol(1)) //false
// console.log(Symbol('foo')); //Symbol(foo)
// console.log(Symbol('aa')); //Symbol(aa)
const name = Symbol();
const person = {
  [name]: 'zs',
  say(){
    console.log(this[name]);
  }
}
person.say(); // zs
console.log(Symbol.for('foo') === Symbol.for('foo')) //true
console.log(Symbol.for(true) === Symbol.for('true')) //true
console.log(Symbol.iterator); // Symbol(Symbol.iterator)
console.log(Symbol.hasInstance); //Symbol(Symbol.hasInstance)
// const obj = {
//   [Symbol.toStringTag]: 'Xobject'
// }
// console.log(obj.toString()); //[object Xobject]

const obj = {
  [Symbol()]: 'symbol value',
  foo: 'normal value'
}
for(var key in obj){
  console.log(key)
}; //foo
console.log(Object.keys(obj)); //[ 'foo' ]
console.log(JSON.stringify(obj)) //{"foo":"normal value"}
console.log(Object.getOwnPropertySymbols(obj)) //[ Symbol() ]

23、for...of 循环,作为遍历所有数据结构的统一方式。

  • 相比较于forEach可以用break 关键词随时终止循环
  • for(普通数组),for..in (键值对)
  • for...of 不能遍历普通对象,因为 实现 Iterable(可迭代)接口 是 for...of 的前提。
  • [], Set, Map, 他们的原形对象proto下 都有 Symbol(Symbol.iterator) 方法,
  • 迭代器中通过next() 实现遍历
const arr = [1, 2, 3, 101, 5, 6];
// for(const item of arr){
//   console.log(item)
// }
// arr.forEach(item => console.log(item))
// for(const item of arr){
//   console.log(item);
//   if(item > 100){
//     break;
//   }
// }
// const s = new Set(['foo', 'bar'])
// for(const item of s){
//   console.log(item);
// }
const m = new Map()
m.set('foo', 123)
m.set('bar', 344)

for(const [key, value ] of m){
   console.log(key, value)
}
let iterator = arr[Symbol.iterator](); //原形对象__proto__下面有个 next()方法
console.log(iterator.next()); // { value: 1, done: false }

24、实现可迭代接口 Iterable

const obj = {
  store: ['foo', 'bar', 'baz'],
  [Symbol.iterator]: function(){ //Iterable 可迭代接口
    let index = 0;
    const self = this;
    return { // Iterator 迭代器接口
      next: function(){
        const result = { // IterationResult 迭代器结果接口
          value: self.store[index],
          done: index >= self.store.length  //迭代是否结束
        }
        index++;
        return result;
      }
    }
  }
}
for(const item of obj){
  console.log(item);
}

迭代器模式核心:对外提供统一遍历接口,让外部不用关心数据内部的结构是怎样的。

const todos = {
  life: ['吃饭', '睡觉', '打豆豆'],
  learn: ['语文', '数学', '英语'],
  work: ['喝茶'],
  each: function(callback){
    const all = [].concat(this.life, this.learn, this.work)
    for( const item of all){
      callback(item)
    }
  },
  [Symbol.iterator]: function(){
    const all = [...this.life, ...this.learn, ...this.work];
    let index = 0;
    return {
      next: function(){
        return {
          value: all[index],
          done: index++ >= all.length
        }
      }
    }
  }
}
// todos.each( item => console.log(item));
for(const item of todos){
  console.log(item)
}

25、生成器Generator:避免异步编程中回调嵌套过深,提供更好的异步编程解决方案 (*, yield)

生成器函数自动返回生成器对象,调用对象的next()方法,才能让这个函数的函数体开始执行,一旦遇到yield关键词,函数执行就会被暂停,yield 后的值作为next()的结果返回,继续调用next(),函数从暂停位置继续执行,周而复始,直到函数完全结束, 那么 next() 返回的 done 的值就变成了 true,
最大的特性:惰性执行

function * foo(){
  console.log('11');
  yield 100;
  console.log('22');
  yield 200;
  console.log('33');
  yield 300;
}
const generator = foo();
console.log(generator.next());
console.log(generator.next());
console.log(generator.next());
console.log(generator.next());

26、生成器应用

// 案例1  发号器
// function * createIdMaker(){
//   let id = 1;
//   while(true){
//     yield id++;
//   }
// }
// const idMaker = createIdMaker();
// console.log(idMaker.next().value);
// console.log(idMaker.next().value);
// console.log(idMaker.next().value);
//案例2  使用Generator 函数实现 Iterator 方法
const todos = {
  life: ['吃饭', '睡觉', '打豆豆'],
  learn: ['语文', '数学', '英语'],
  work: ['喝茶'],
  [Symbol.iterator]: function * (){
    const all = [...this.life, ...this.learn, ...this.work];
    for( const item of all){
      yield item
    }
  }
}
// todos.each( item => console.log(item));
for(const item of todos){
  console.log(item)
}

27、ES Modules :语言层面的模块化标准

你可能感兴趣的:(ECMAScript 2015 新特性)