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



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值