ES6那些事


详细:

1. let , const都是块级作用域

如果你声明:

const a = 10; 
const a=20;
console.log(a)

页面报错


支持这种写法

const a = 10, b = 12;

2.模板字符串

字符串拼接

const name = 'lux'
console.log(`hello ${name}`) //hello lux
const template = `<div>
        <span>hello world</span>
    </div>`

3. 字符串的扩展

    repeat:

'七哥'.repeat(3);//打印值 "七哥七哥七哥"
includes & startsWith & endsWith

都是返回布尔值

var str = '我就是你小七哥'
str.startsWith('我就是')//true
str.startsWith('我')//true
str.startsWith('我',2)//false
str.includes('七')//true
str.includes('八')//false
str.endsWith('小七哥')//true

4. 数值

判断是否整数,isInteger:

Number.isInteger(1)//true
Number.isInteger(1.0)//true
Number.isInteger(1.1)//false

判断正负数,Math.sign

Math.sign(-10)// -1
Math.sign(10)// +1
Math.sign(0)// +0
Math.sign(-0)// -0
Math.sign('小七')// NaN

5.函数的扩展

    函数参数

            ES6为参数提供了默认值。在定义函数时便初始化了这个参数,以便在参数没有被传递进去时使用。

        function action(num = 200) {
            console.log(num)
        }
        action() //200
        action(0) //0

             对比下ES5

      function action(num) {
          num = num || 200;
          //当传入num时,num为传入的值,当没传入参数时,num即有了默认值200
          return num;
      }    
      action()//200
      action(0)//200,然鹅我们希望的是没参数时返回200,有参数返回参数

    箭头函数

  • 不需要function关键字来创建函数
  • 省略return关键字
  • 继承当前上下文的 this 关键字
    [1,2,3].map( x => x + 1 )
    
    //等同于:
    [1,2,3].map((function(x){
        return x + 1
    }).bind(this))

        注意,当参数只有一个的时候可以省略(),当返回值只有一个表达式,可以省略{},但是加上{}就需要return了,

        也可以这样写: 

    arr.map((x)=>{
        return x+1
    })

        由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。        

    // 报错
    let getTempItem = id => { id: id, name: "Temp" };

    // 不报错
    let getTempItem = id => ({ id: id, name: "Temp" });

    getTempItem上没有prototype,有__proto__

    箭头函数的一个用处是简化回调函数。

 // 正常函数写法
 var result = values.sort(function (a, b) {
   return a - b;
 });

 // 箭头函数写法
 var result = values.sort((a, b) => a - b);

    this指向的固定化,并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。正是因为它没有this,所以也就不能用作构造函数。

    箭头函数this指向固定化,有利于封装回调函数。下面是一个例子,DOM 事件的回调函数封装在一个对象里面。

 var handler = {
   id: '123456',

   init: function() {
     document.addEventListener('click',
       event => this.doSomething(event.type), false);
   },

   doSomething: function(type) {
     console.log('Handling ' + type  + ' for ' + this.id);
   }
 };

    这个箭头函数的this总是指向handler对象的。而es5的一般函数中,this是指向document的。

    看下面例子:

function foo() {
  return () => {
    return () => {
      return () => {
        console.log('id:', this.id);
      };
    };
  };
}

var f = foo.call({id: 1});

var t1 = f.call({id: 2})()(); // id: 1
var t2 = f().call({id: 3})(); // id: 1
var t3 = f()().call({id: 4}); // id: 1

这里只有一个this,就是foo的,f变量的打印值如下:

    () => {
        return () => {
          return () => {
            console.log('id:', this.id);
          };
        };
     }

可知,不论是f,还是f(),f()(),它们都是没有this的,所以最终打印的this.id一直都是foo的this。

除this外,argumentssupernew.target call()也不存在,指向外层函数,call()apply()bind()也不能用于改变this指向。

如果想使用类似call的用法,ES6提供了双冒号运算符:

foo::bar;
// 等同于
bar.bind(foo);

6.对象的扩展

    键值对重名,属性简写:

function people(name, age) {
     return {
          name,
          age
     };
}

    字面量赋值函数,方法简写:

    省略了function关键字

const people = {
        name: 'lux',
        getName () {
            console.log(this.name)
        }
    }

    允许中括号直接定义:

let obj = {
  [propKey]: true,
  ['a' + 'bc']: 123
};

    Object.is(参数1,参数2)

        类似===,只有两个不同:

+0 === -0 //true
NaN === NaN // false

Object.is(+0, -0) // false
Object.is(NaN, NaN) // true

    Object.keys, Object.values, Object.entries

var info = {name:'小七哥',age:3,love:'吃'}
Object.entries(info);//[["name", "小七哥"],["age", 3],["love", "吃"]]

 Object.assign(),浅拷贝,源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。

                          它的参数只针对可枚举属性.

                                   Object.defineProperty也会影响对象属性是否可枚举。

Object.assign([1, 2, 3], [4, 5])
// [4, 5, 3]

    作用:

    为对象添加方法/属性

class Point {
  constructor(x, y) {
    Object.assign(this, {x, y});
  }
}
Object.assign(SomeClass.prototype, {
  someMethod(arg1, arg2) {
    ···
  }
});

// 等同于下面的写法
SomeClass.prototype.someMethod = function (arg1, arg2) {
  ···
};

    克隆对象

function clone(origin) {
  return Object.assign({}, origin);//只能克隆原始对象自身的值(构造函数),不能克隆它继承的值(原型)
}

    Object.keys(),Object.values(),Object.entries()

Object.entries({ [Symbol()]: 123, foo: 'abc' ,baz: 42 });// [ ["foo", "abc"], ["baz", 42] ]

扩展运算符...用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。       

let z = { a: 3, b: 4 };
let n = { ...z };
n // { a: 3, b: 4 }
let aClone = { ...a };
// 等同于
let aClone = Object.assign({}, a);

    上面的例子只是拷贝了对象实例的属性,如果想完整克隆一个对象,还拷贝对象原型的属性,可以采用下面的写法。

// 写法一
const clone1 = {
  __proto__: Object.getPrototypeOf(obj),
  ...obj
};

// 写法二
const clone2 = Object.assign(
  Object.create(Object.getPrototypeOf(obj)),
  obj
);

// 写法三
const clone3 = Object.create(
  Object.getPrototypeOf(obj),
  Object.getOwnPropertyDescriptors(obj)
)

    扩展运算符修改现有对象部分的属性就很方便了。

let newVersion = {
  ...previousVersion,
  name: 'New Name' // Override the name property
};

    用途:

        合并数组/对象,添加新对象:state.obj = { ...state.obj, newProp: 123 }

        vux引入一些方法...mapMutations()

 methods: {
    ...mapMutations([
      'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`

      // `mapMutations` 也支持载荷:
      'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)`
    ]),

7.解构赋值

    //对象
    const people = {
        name: 'lux',
        age: 20
    }
    const { name, age } = people
    console.log(`${name} --- ${age}`)
    //数组
    const color = ['red', 'blue','black']
    const [first, ...second] = color
    console.log(first) //'red'
    console.log(second) //["blue", "black"]

    解构赋值允许默认值

let [foo = true] = [];
foo // true

// 报错,右边为不可遍历结构
let [foo] = 1;
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {};

    对象的解构赋值

var {x, y = 5} = {x: 1};
x // 1
y // 5

var {x: y = 3} = {};//这里的y=3是默认值
y // 3
x//undifined

    函数参数解构赋值

function move({x = 0, y = 0} = {}) {
  return [x, y];
}

move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, 0]
move({}); // [0, 0]
move(); // [0, 0]

    用途:

        交换变量值;

        函数返回多个值;    

        函数参数定义/默认值;

        提取JSON数据;

        加载模块时,指定需要的方法,解构赋值使得输入语句非常清晰!!

const { SourceMapConsumer, SourceNode } = require("source-map");

8.Promise

它不是新的语法功能,而是一种新的写法,允许将回调函数的嵌套,改成链式调用。

如果使用回调,因为多个异步操作形成了强耦合,只要有一个操作需要修改,它的上层回调函数和下层回调函数,可能都要跟着修改。这种情况就称为"回调函数地狱"(callback hell)。

    说白了就是用同步的方式去写异步代码。

setTimeout(function() {
      console.log(1)
    }, 0);
    new Promise(function executor(resolve) {
      console.log(2);
      for( var i=0 ; i<10000 ; i++ ) {
        i == 9999 && resolve();
      }
      console.log(3);
    }).then(function() {
      console.log(4);
    });
    console.log(5);

    //运行结果:2,3,5,4,1

    最后一题 Promise 的 4 在 1 前面输出是因为 Promise.then()里面的回调属于 microtask, 会在当前 Event Loop 的最后执行, 而 SetTimeout 内的回调属于 macrotask, 会在下一个 Event Loop 中执行.

https://www.zhihu.com/question/36972010


Promise 的写法只是回调函数的改进,使用then方法以后,异步任务的两段执行看得更清楚了,除此以外,并无新意。

Promise 的最大问题是代码冗余,原来的任务被 Promise 包装了一下,不管什么操作,一眼看去都是一堆then,原来的语义变得很不清楚。

9.generator

所谓"异步",简单说就是一个任务不是连续完成的,可以理解成该任务被人为分成两段,先执行第一段,然后转而执行其他任务,等做好了准备,再回过头执行第二段。

比如,有一个任务是读取文件进行处理,任务的第一段是向操作系统发出请求,要求读取文件。然后,程序执行其他任务,等到操作系统返回文件,再接着执行任务的第二段(处理文件)。这种不连续的执行,就叫做异步。

相应地,连续的执行就叫做同步。由于是连续执行,不能插入其他任务,所以操作系统从硬盘读取文件的这段时间,程序只能干等着。

const gen = function* () {
  const f1 = yield readFile('/etc/fstab');
  const f2 = yield readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};
Generator 函数,需要调用next方法,或者用co模块,才能真正执行


10.async

async 函数,使得异步操作变得更加方便。Generator 函数的语法糖。

进一步说,async函数完全可以看作多个异步操作,包装成的一个 Promise 对象,而await命令就是内部then命令的语法糖。

(1)内置执行器。

Generator 函数的执行必须靠执行器,async函数自带执行器,它的执行,与普通函数一模一样。

(2)更广的适用性。

async函数的await命令后面,可以是 Promise 对象和原始类型的值(但这时等同于同步操作)。yield命令后面只能是 Thunk 函数或 Promise 对象。

(3)返回值是 Promise。

async函数的返回值是 Promise 对象,你可以用then方法指定下一步的操作。

当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。

想要执行并行,配合Promise.all([...])

对比Promise:

1.简洁,不需要定义多余的data,不要一串then,还避免了嵌套代码

2.可以使用try,catch来处理报错

3.如果你在Promise的then回调中遇到if,if里面还有Promise,再嵌套then...容易造成Promise式的地狱回调,太多的return比较迷茫

4.调试,使用async时,不需要那么多箭头函数,你就可以像调试同步代码一样跳过await语句。而Promise的then中设断点,往下执行会跳过整个异步代码

5.如果你需要跨层取数据,Promise就会让你有点头疼了,而async不会,随便取


11.数组

Array.from

from用于将两类对象转为真正的数组:类似数组的对象和可遍历的对象。

Array.from配合Set可以做到数组去重

let newSet = new Set(['a', 'b','a','c'])
Array.from(newSet) // ['a', 'b','c'] 

find方法,用于找出第一个符合条件的数组成员

[1, 2, 3, 4].find((n) => n > 2)//3


12.class

定义的类只是语法糖,目的是让我们用更简洁明了的语法创建对象及处理相关的继承。

本质上,ES6 的类只是 ES5 的构造函数的一层包装,所以函数的许多特性都被Class继承

//定义类
class StdInfo {
    //...
}
console.log(typeof  StdInfo);  //function

console.log(StdInfo === StdInfo.prototype.constructor);  //true

从上面的测试中可以看出来,类的类型就是一个函数,指向的是构造函数。

函数的定义方式有函数声明和函数表达式两种,类的定义方式也有两种,分别是:类声明类表达式

constructor中的this指向新创建的实例对象,利用this往新创建的实例对象扩展属性,

//定义类,可以省略constructor
class StdInfo {
    getNames(){
        console.log("name:"+this.name);
    }
}
var st = new StdInfo();
st.name='小七';
st.getNames()//name:小七

类似函数表达式

 const People = class StdInfo {
    constructor(){
        console.log(StdInfo);  //可以打印出值,是一个函数
    }
}

new People();
new StdInfo();  //报错,StdInfo is not defined;

不存在变量提升

定义类不存在变量提升,只能先定义类后使用,跟函数声明有区别的。

//-----函数声明-------
//定义前可以先使用,因为函数声明提升的缘故,调用合法。
func();
function func(){}

//-----定义类---------------
new StdInfo();  //报错,StdInfo is not defined
class StdInfo{}

使用extends关键字实现类之间的继承

//定义类父类
class Parent {
    constructor(name,age){
        this.name = name;
        this.age = age;
    }

    speakSometing(){
        console.log("I can speek chinese");
    }
}
//定义子类,继承父类
class Child extends Parent {
    coding(){
        console.log("coding javascript");
    }
}

var c = new Child();

//可以调用父类的方法
c.speakSometing(); // I can speek chinese

Child.prototype
//Parent {constructor: ƒ, coding: ƒ}coding: ƒ coding()constructor: class Child__proto__: Object
Child.__proto__
//class Parent {
    constructor(name,age){
        this.name = name;
        this.age = age;
    }

    speakSometing(){
        console.log("I can speek chinese");
    }
}

13.Module-----import /export

ES6中使用exportimport关键词实现模块化。

module.js中使用export导出port变量和getAccounts函数:

export var port = 3000
export function getAccounts(url) {
  ...
}

main.js中使用import导入module.js,可以指定需要导入的变量:

import {port, getAccounts} from 'module'
console.log(port) // 输出3000

也可以将全部变量导入:

import * as service from 'module'
console.log(service.port) // 3000


1.当用export default people导出时,就用 import people 导入(不带大括号)

2.一个文件里,有且只能有一个export default。但可以有多个export。

3.当用export name 时,就用import { name }导入(记得带上大括号)

4.当一个文件里,既有一个export default people, 又有多个export name 或者 export age时,导入就用 import people, { name, age } 

5.当一个文件里出现n多个 export 导出很多模块,导入时除了一个一个导入,也可以用import * as example

14.Symbol

新的原始数据类型Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型.

凡是对象属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。

Symbol函数前不能使用new命令,因为他不是对象,他是一种数据类型。

最好给Symbol 函数加参数值,虽然加与不加都是独一无二的值,但是加了之后便于自己分辨啊。

不能与其他值计算,但是通过String(),!可以转化为字符串和布尔值。

Symbol 作为属性名,该属性不会出现在for...infor...of循环中,也不会被Object.keys()Object.getOwnPropertyNames()JSON.stringify()返回。

15.Set,Map

JavaScript原有的表示集合的数据结构,主要是数组(Array)和对象(Object),ES6 又添加了MapSet。这样就有了四种数据集合。

Set类似于数组,但是成员的值都是唯一的,没有重复的值。

操作方法:

·       add(value):添加某个值,返回 Set 结构本身。

·       delete(value):删除某个值,返回一个布尔值,表示删除是否成功。

·       has(value):返回一个布尔值,表示该值是否为Set的成员。

·       clear():清除所有成员,没有返回值

遍历:

·       keys():返回键名的遍历器。values():返回键值的遍历器。entries():返回键值对的遍历器

·       forEach():使用回调函数遍历每个成员

Map 类似于对象,也是键值对的集合,但是的范围不限于字符串,各种类型的值(包括对象)都可以当作键。

Object 结构提供了字符串的对应,Map 结构提供了的对应。

属性:size 属性

方法:set(key, value);get(key);has(key);delete(key);clear();keys():返回键名的遍历器。values():返回键值的遍历器。entries():返回所有成员的遍历器。forEach():遍历 Map 的所有成员。

与其他数据结构转换:

Map 转为数组:扩展运算符Map 转为数组[...map]。


16.Iterator,for...of

遍历器

我们已知,ES6有4种数据结构,可能数组中嵌套Map,Set中有对象,这就需要一种统一的接口机制,来处理所有不同的数据结构。

所以有 Iterator 接口,任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。

一种数据结构只要部署了 Iterator 接口,它就是“可遍历的”(iterable),即for...of循环。当使用for...of循环遍历某种数据结构时,该循环会自动去寻找 Iterator 接口。

ES6规定,一个数据结构只要具有Symbol.iterator属性,就认为是“可遍历的”,有些数据结构原生具备 Iterator 接口,即不用任何处理,就可以被for...of循环遍历。原因在于,这些数据结构原生部署了Symbol.iterator属性,这些本身就可遍历的有:

ArrayMapSetStringTypedArray;函数的 arguments 对象;NodeList 对象.

var obja={a:1,b:2,c:3}
for(var v of obja){
console.log(v)
}
// TypeError: obja is not iterable

对象想用for...of怎么办?

for (var key of Object.keys(obja)) {
  console.log(key + ': ' + obja[key]);
}

与其他遍历语法的比较:

·       数组的键名是数字,但是for...in循环是以字符串作为键名“0”“1”“2”等等。

·       for...in循环不仅遍历数字键名,还会遍历手动添加的其他键,甚至包括原型链上的键。

·       某些情况下,for...in循环会以任意顺序遍历键名。

总之,for...in循环主要是为遍历对象而设计的,不适用于遍历数组。

for...of循环相比上面几种做法,有一些显著的优点。

·       有着同for...in一样的简洁语法,但是没有for...in那些缺点。

·       不同于forEach方法,它可以与breakcontinuereturn配合使用。

·       提供了遍历所有数据结构的统一操作接口。

 

简单概括:https://www.jianshu.com/p/53fe8b56cfb0

https://www.jianshu.com/p/287e0bb867ae

此文根据阮大大ES6入门简化而来,大致的功能点,具体细节还需移步阮大:http://es6.ruanyifeng.com/#README



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值