ES6常用

let

  • ES6新增的用于变量声明的关键字
  • 通过let声明的变量,不允许重复声明
  • 不支持变量声明预解析,let变量,必须先声明后使用,我们把当前作用域最开始到let声明的变量之间的区域,称为 - 暂存死区
  • let支持块级作用域
    • 全局
    • 函数
    • 块 - 一对{}包含的部分,称为一个块,在这样的块中会有独立的作用域,var不支持块作用域

const

  • 常量声明,和var与let不一样,var、let声明的值是可以变化的,但是通过const声明的值是固定的,不能再后期去改变他的
  • 其他特性与let一致
  • 因为const定义的是常量值,所以通过const定义一个常量的时候一定要初始化

object.freeze(对象)

冻结对象,无法解冻

如果希望一个对象本身以及自数据都不能变化,那么通过const和freeze同时使用

解构赋值

允许按照一定模式,从数组和对象中提取值

  • 声明{}中的变量
  • 根据{}中的变量,去=后面的对象中提取对应的属性,并把该属性对应的值赋值给前面的变量,如果没有该属性,返回undefined
  • 如果解构出来的变量名不希望和解构对象的属性名一致,这个时候需要给需要使用的变量名设置一个别名
let {left:L,top:T} = getComputedStyle(sdad);
  • 数组的解构赋值和对象的解构赋值
    • 数组使用[],对象使用{}
    • 数组解构赋值是一一对应的,而对象解构赋值是按照名称来进行的

函数扩展

函数参数默认值

  • 在es6之前,如果一个函数的参数是可选,那么我们通常需要手动在该函数的内部处理该参数的默认值
  • 在es6之后,函数在定义的时候,可以直接在参数上处理默认值
  • 注意:必选参数一般写在前面,可选参数写在最后
//es5
function fn (a,b) {
    a = a||10;
    b = b||10;
}
//es6
function fn (a=10,b=10)

剩余参数

  • 我们通常会给一个函数定义一些参数,有些参数是确定的,而有些参数是不确定(个数)的
  • 如果一个函数的参数个数不确定,原来我们是通过arguments(不定参)来处理
  • 如果一个函数定义了剩余参数,那么在调用该函数的时候传入的实参会赋值给对应的形参,剩下的没有对应的实参,会全部赋值给剩余参数
  • 注意 :
    • 剩余参数必须是伪参数
function push (arr,...data){};
push(arr,'1','2','3');

扩展运算符

  • ...
  • 把数组转成参数序列
function fn (a,b){};
var arr = [10,10]
fn(...arr)

var arr1 = ['a','b','c'];
var arr2 = [1,2,3];
arr1.splice(1,1,...arr2);

var arr3 = [...arr1,...arr2]
//相当于
var arr3 = arr1.concat(arr2);

//找数组中最大值的方法
var arr4 = [4,2,6,1,6,7];
Math.max(...arr4);

箭头函数

  • 在es6中,提供了一种新的函数格式:箭头函数
  • 有且只有一个形参的时候,才可以省略括号
  • 有只有一条语句的时候,可以省略函数体的{},同时该条语句的结果将作为该函数的返回值
  • 但是如果有多条语句或者返回值是一个对象,则必须用{}
  • 注意:
    • 箭头函数不能作为构造函数,也就是箭头函数不能使用new运算符
    • 箭头函数的this永远指向当声明作用域对象
      • 普通函数this指向取决于调用
      • 箭头函数this指向取决于声明位置
  • 箭头函数没有arguments对象
//函数声明
function fn1 (a,b){};
//函数表达式
var fn2 function (a,b){};

//箭头函数

//几种有条件的简化写法

//当参数有多个的时候
var fn3 = (a,b) => {};
//当参数只有一个的时候
var fn4 = a => {};
//当没有参数的时候
var fn5 = () => {};
//有只有一条语句的时候,可以省略函数体的{},同时该条语句的结果将作为该函数的返回值
var fn6 = r => r*r;
//但是如果有多条语句或者返回值是一个对象,则必须用{}

对象扩展

对象属性简洁表示法

  • 当一个对象的key和对应的值(必须是一个变量名称)同名的话,那么可以简写成一个key
var a = 1;
var b = 1;
//es5
var obj{
    left:100,
    top:100,
    a:a,
    b:b
}
//es6
var obj{
    left:100,
    top:100,
    a,
    b
}

对象方法的简洁表示法

//es5
let obj2 = {
    a:a,
    fn:function(){};
}
//es6
let obj3 = {
    a,
    fn(){};
}

对象属性名表达式

//es5
var x = 'username'
let obj4 = {
    x: 'maotao' //这里的x不会作为表达式被解析
}
//es6
let obj4 = {
    [x]: 'maotao' //如果把key放在一个[]中,那么[]中的内容将被作为表达式进行解析
}

Iterator==迭代器==

  • 数组通过forof得到的是数组的值,但是对于forof得到不一定是指,具体要看呗迭代对象的迭代器如何实现
  • 如果我们希望一个对象能够被迭代,那么久需要去实现该对象的迭代协议和迭代器

迭代协议

  • 当我们通过forof去迭代obj的时候,js内部会去查找并调用obj的Symbol.Iterator方法
var obj = {x:10,y:20};
obj[Symbol.iterator] = function(){
    let keys = Object.keys(obj);
    let n = -1;
    return {
        next(){
            if (n < keys.length-1) {
                n++;
                return {done: false, value: {
                    k: keys[n],
                    v: obj[keys[n]]
                }};
            } else {
                return {done: true};
            }
        }
    }
}

迭代器

  • obj[Symbol.iterator]() => iterator.next() => 根据返回的对象中的done值,来决定是否已经完成或继续调用next,如果done为真,表示迭代结束通过忽略这次value值,如果为false,表示当前value有效,并继续下一次迭代(next()

字符串扩展

字符串的遍历器接口

  • ES6为字符串添加了遍历器接口(详见《Iterator》一章),使得字符串可以被for...of循环遍历。
for (let codePoint of 'foo') {
  console.log(codePoint)
}
// "f"
// "o"
// "o"
  • 除了遍历字符串,这个遍历器最大的优点是可以识别大于0xFFFF的码点,传统的for循环无法识别这样的码点。
var text = String.fromCodePoint(0x20BB7);

for (let i = 0; i < text.length; i++) {
  console.log(text[i]);
}
// " "
// " "

for (let i of text) {
  console.log(i);
}
// ""

includes(), startsWith(), endsWith()

  • includes():返回布尔值,表示是否找到了参数字符串。
  • startsWith():返回布尔值,表示参数字符串是否在源字符串的头部。
  • endsWith():返回布尔值,表示参数字符串是否在源字符串的尾部。
  • 这三个方法都支持第二个参数,表示开始搜索的位置。
var s = 'Hello world!';

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

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

str.repeat(num)

repeat方法返回一个新字符串,表示将原字符串重复n次。

'x'.repeat(3) // "xxx"
'hello'.repeat(2) // "hellohello"
'na'.repeat(0) // ""

参数如果是小数,会被取整。

'na'.repeat(2.9) // "nana"

如果repeat的参数是负数或者Infinity,会报错。

'na'.repeat(Infinity)
// RangeError
'na'.repeat(-1)
// RangeError

但是,如果参数是0到-1之间的小数,则等同于0,这是因为会先进行取整运算。0到-1之间的小数,取整以后等于-0,repeat视同为0,NAN等同于0

'na'.repeat(-0.9) // ""
'na'.repeat(NAN) // ""

如果repeat的参数是字符串,则会先转换成数字。

'na'.repeat('na') // ""
'na'.repeat('3') // "nanana"

模板字符串

(`)

  • 模板字符串是增强版的字符串,用反引号(`)标识
    • 保持编辑格式
    • 支持表达式
      • ${表达式}
      • 我们可以把一个js的表达式放置在一个${}中,这里的表达式会被js所解析,注意:不支持语句,比如if,for,while等
  • 他可以当做普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量
  • 如果需要引入变量,则使用${变量名},在{}中可以进行运算,也可以引用对象属性
  • 模板字符串之中还能调用函数。
var name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`

上面代码中的模板字符串,都是用反引号表示。如果在模板字符串中需要使用反引号,则前面要用反斜杠转义。

var greeting = `\`Yo\` World!`;

.trim()

可以消除模板字符串中的换行或空格

`
  • first
  • second
`.trim()

数组扩展

Array.from()

  • Array.from()用于将类数组对象转换成真数组
  • 只要是部署了Iterator接口的数据结构,Array.from都能将其转为数组。
  • 任何有length属性的对象,都可以通过Array.from方法转为数组,而扩展运算符(...)并不可以
  • 扩展运算符(...)也可以将某些数据结构转为数组。

Array.of()

  • Array.of()用于将一组参数转换成数组
  • 这个方法的主要目的,是弥补数组构造函数Array()的不足。因为参数个数的不同,会导致Array()的行为有差异。
//es5
Array() // []
Array(3) // [, , ,]
Array(3, 11, 8) // [3, 11, 8]
//es6
Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array.of(3).length // 1

arr.find()

  • 找出第一个符合条件的数组元素
  • 参数:
    • 回调函数
    • 回调函数内this的指向
  • 遍历整个数组,遍历过程中调用回调函数,如果回调函数的返回值为true,则返回当前遍历的元素,如果所有元素都不符合则返回undefined
  • find方法的回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组。
var arr = '43157'.split('');
console.log(arr.find( function(item) {
        return item > 5;
} , document));//打印出来7

arr.findIndex()

findIndex方法的用法与find方法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1。

console.log( arr.findIndex( function(item) {
        //返回true,就表示找到了满足条件的值,则不会继续查找了,那么当前的值的位置作为findIndex的返回值
        return item > 4;
} ) );//打印出来0

arr.fill()

  • 用来填充数组(会把原位置上的元素替换掉)
  • 第一个参数 :填充的内容
  • 第二个参数 :启始位置
  • 第三个参数 :结束位置

for of

作用:用来遍历拥有遍历接口的对象的属性的值

arr.keys()

遍历并获取数组的所有key,返回数组对象

arr.values()

遍历并获取数组的所有value,返回数组对象

arr.entries()

遍历并获取数组的所有键值对,返回数组对象

数组推导

通过现有数组生成新数组,称为数组推导

var arr = [1,2,3,4];
var arr2 = [for (i of arr) i*2];
var arr3 = [for (i of arr) if(i > 2) i];
var arr4 = [for (i of arr) if(i > 2) if(i < 3) i];

数据结构

Set

  • 在es6中新增了一个新的数据结构 - 集合
  • Set是一个构造函数,可以不传入任何参数创建一个空的集合,也可以传入一个数组对其进行初始化
  • Set和数组类似,但是Set的值是唯一的不重复的
    • 不能通过下标进行取值
    • 集合是一个可迭代的对象
    • 有一个属性,size,类似数组的length属性
  • 我们通过Set来保证集合中的值得唯一性,然后如果需要单个取值可以转换成数组来操作
  • 操作:
    • add(value) : 添加某个值,返回Set结构本身
    • delete(value) : 删除某个值,返回一个布尔值,表示删除成功
    • has(value) : 返回一个布尔值,表示参数是否为Set的成员
    • clear() : 清除所有成员,没有返回值
let s1 = new Set(['a','b','c']);

s1.add('d');
console.log(s1);//a,b,c,d//可以添加元素
s1.add('a');
console.log(s1);//a,b,c,d//可以添加元素,但是如果添加的元素已经存在,则不会添加进去
//s1.clear();//清空
s1.delete('a');
console.log(s1);//b,c,d//可以删除元素

for (let v of s1) {
    console.log(v);
}

var arr = [...s1];
console.log(arr);


var arr1 = 'shdfkjwehfhjsdf'.split('');
console.log( [...new Set(arr1)] );//这里把数组转化成set集合再转回数组,由于set集合中的值具有唯一性从而达到去重的目的

WeakSet

WeakSet类似于Set,也是不重复的值得集合,但是他只能用于存储对象,而不能是其他类型的值

  • 方法 :
    • WeakSet.protoptype.add(value) : 向WeakSet实例添加一个新成员
    • WeakSet.protoptype.delete(value) : 删除WeakSet实例指定成员
    • WeakSet.protoptype.has(value) : 返回一个布尔值,表示某个值是否在WeakSet实例中
let ws = new WeakSet();
var arr = [1,2];
ws.add(arr);
ws.add(window);
ws.has(window);//true
ws.delete(arr);
ws.has(arr);//false
  • WeakSet不能遍历,因为成员都是弱引用,随时可能消失,遍历不能保证成员的存在,可能刚刚遍历结束,成员就取不到了
  • WeakSet的一个用处是存储DOM节点,而不用担心这些节点从文档移除时,会引起内存泄漏
let ws = new WeakSet();
function fn () = {};
ws.add(fn());
console.log(ws);//返回一个WeakSet对象,包含fn函数
setTimeout(function () {
    console.log(ws);//返回一个WeakSet空对象
},1000)

Map

  • Map数据结构类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键
  • Object结构停工了“字符串-值”的对应,Map结构提供了“值-值”的对应,是一种更完善的Hash结构实现
  • 方法:
    • set(key,value) :给Map结构新添加一对键值
    • get(key) :返回key所对应的值
    • has(key) :返回一个布尔值,判断是否有这个key
    • delete(key) :删除key所对应的这一对键值
const m = new Map();
const o = {p : 'Hello World'};
m.set(o,'content');
m.get(o);//返回content
m.has(o);//true
m.delete(o);

Map也接受数组作为参数,该数组的成员是一个个表示键值对的数组

const map = new Map([['name','张三'],['title','标题']]);
map.size;//2
map.has('name');//true
map.get('name');//张三
map.has('title');//true
map.get('title');//标题

Generator 函数

  • 特征 :
    • function关键字与函数名之间有一个星号
    • 函数体内部使用yield表达式,定义不同的内部状态
    • 函数外部调用使用next方法,每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield表达式(或return语句)为止
  • Generator调用函数后,不会立即执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象
  • 换言之,Generator 函数是分段执行的,yield表达式是暂停执行的标记,而next方法可以恢复执行。
  • Generator 函数返回的遍历器对象,只有调用next方法才会遍历下一个内部状态,所以其实提供了一种可以暂停执行的函数。yield表达式就是暂停标志。
function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';
}

var hw = helloWorldGenerator();

hw.next()
// { value: 'hello', done: false }

hw.next()
// { value: 'world', done: false }

hw.next()
// { value: 'ending', done: true }

hw.next()
// { value: undefined, done: true }

yield 表达式

  • yield表达式就是暂停标志

  • 遍历器对象的next方法的运行逻辑如下。

  • 遇到yield表达式,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值。

    • 下一次调用next方法时,再继续往下执行,直到遇到下一个yield表达式。

    • 如果没有再遇到新的yield表达式,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值。

    • 如果该函数没有return语句,则返回的对象的value属性值为undefined

  • 需要注意的是,yield表达式后面的表达式,只有当调用next方法、内部指针指向该语句时才会执行,因此等于为 JavaScript 提供了手动的“惰性求值”(Lazy Evaluation)的语法功能。

  • yield表达式与return语句既有相似之处,也有区别。相似之处在于,都能返回紧跟在语句后面的那个表达式的值。区别在于每次遇到yield,函数暂停执行,下一次再从该位置继续向后执行,而return语句不具备位置记忆的功能。一个函数里面,只能执行一次(或者说一个)return语句,但是可以执行多次(或者说多个)yield表达式。正常函数只能返回一个值,因为只能执行一次return;Generator 函数可以返回一系列的值,因为可以有任意多个yield。从另一个角度看,也可以说 Generator 生成了一系列的值,这也就是它的名称的来历

next方法的参数

  • yield表达式本身没有返回值,或者说总是返回undefinednext方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值。
  • 这个功能有很重要的语法意义。Generator 函数从暂停状态到恢复运行,它的上下文状态(context)是不变的。通过next方法的参数,就有办法在 Generator 函数开始运行之后,继续向函数体内部注入值。也就是说,可以在 Generator 函数运行的不同阶段,从外部向内部注入不同的值,从而调整函数行为。
function* foo(x) {
  var y = 2 * (yield (x + 1));
  var z = yield (y / 3);
  return (x + y + z);
}

var a = foo(5);
a.next() // Object{value:6, done:false}
a.next() // Object{value:NaN, done:false}
a.next() // Object{value:NaN, done:true}

var b = foo(5);
b.next() // { value:6, done:false }
b.next(12) // { value:8, done:false }
b.next(13) // { value:42, done:true }

Promise

解决js中异步编程的问题
当我们new Promise会得到一个Promise对象,该对象下会有一个任务要完成,同时该对象会维护一个状态

Promise :

  • ES6新增的一个内置对象
  • 解决JS中异步编程问题

异步/同步

  • 被请求者(该事情的处理者)在处理完事情的时候的通知机制

阻塞/非阻塞

  • 针对请求者来说的,委托其他人处理一些事情的时候,请求者是等待请求处理完成还是继续向下执行其他的任务

Promise:

  • 构造函数
  • new Promise(callback)
  • callback: 要异步处理的任务

通过Promise构造函数得到一个Promise对象,我们传入的callback将会被Promise对象所执行
Promise会维护一个任务状态,这个状态是一个Promise内部的属性

[[PromiseStatus]] :

  • pending : 正在处理,Promise对象一旦被创建就会更改为该状态,他是默认值
  • resolved : 已完成
  • rejected : 失败
  • resolved,rejected这两个状态是需要靠我们手动去维护的。因为异步任务的结果是成功还是失败,是由我们的具体业务所决定的,Promise对象是没有办法确定的。我们需要在我们业务处理中,去根据实际情况改变Promise对象的状态

当我们把一个callback函数传递给Promise对象的时候,Promise对象会去执行该callback函数,同时,还会传递两个参数给这个callback,所以,我们可以在callback函数接收这两个参数

  • resolve :当我们调用该函数的时候,会把当前的Promise对象的状态改成resolved
  • reject :当我们调用该函数的时候,会把当前的Promise对象的状态改成rejected

这两个参数都是一个函数,当我们调用他们的时候,会改变当前任务的状态

  • resolve() => resolved
  • reject() => rejected
let p1 = new Promise(function(resolve, reject) {
    setTimeout(() => {
        var a = Math.random();
        if (a < 0.5) {
            reject(a);
        } else {
            resolve(a);
        }

    }, 1000)
});

then方法

Promise对象还有一个then方法,当前置任务完成的时候,会调用该方法,并执行该方法传入函数参数
then方法接收两个参数,他们都是函数

  • 该方法接收两个参数,这两个参数都是callback函数,这两个callback不会立即执行,当Promise的状态一旦发生改变就会执行then方法中传入的函数,有点类似事件绑定
  • 第一个参数是当状态为resolved时候执行
  • 第二个参数是当状态为rejected时候执行
p1.then(function(a) {
        console.log('成功', a);
    },function(a) {
        console.log('失败', a);
});

then方法传递数据

其中a,在promise内部为实参,then方法中函数里的a为形参,接收内部传过来的实参a

一旦一个Promise的状态改变了,是没有办法在重置该Promise对象的状态的,我们只能另外创建一个新的Promise对象
虽然then方法中的参数接收两个
一个是成功的callback
一个是失败的callback
但是,在书写的时候,如果每一个then都传这两个callback的话,编写过程太麻烦
为了能够统一的去处理失败,Promise对象又提供了一个新的方法:catch
catch方法也接收一个callback
只要有一个Promise的状态变成了rejected,那么就会被catch方法捕获,执行catch的callback

var b = 10;
new Promise((resolve, reject) => {
    setTimeout(() => {
        b += 10;
        resolve();
//        reject();
    }, 1000);
}).then(function() {
    console.log(b);
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            b *= 2;
            resolve();
        }, 1000);
    });
}).then(function() {
    console.log(b);
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            b *= b;
//          resolve();
            reject();
        }, 1000);
    });
}).then(function() {
    console.log(b);
}).catch(function() {
    console.log(1);
});

all方法

  • 有的时候在一个Promise任务中需要处理多个异步任务,那这多个异步的任务是同时执行的,但是执行时间有是不确定的。后续的任务需要这几个异步任务全部完成以后再执行,那么就需要用到Promise中提供的all方法来实现
  • 把两个不同的异步任务分别包装在一个Promise对象中,然后调用Promise对象静态方法all,把上面多个不同异步Promise作为数组传递给all方法的参数
  • 这个时候Promise.all方法中会维护一个状态,这个状态是根据传入的多个异步任务的状态共同决定的
  • 当多个异步任务的状态都变成了resolved,那么all的状态才是resolved,但是只要有一个异步任务的状态变成了rejected,那么all的状态就会变成rejected
var p1 = new Promise((resolve, reject) => {
    let a = 1;
    setTimeout(() => {
        a++;
//        reject('one');
        resolve(a);
    }, Math.random() * 1000);
});

var p2 = new Promise((resolve, reject) => {
    let b = 2;
    setTimeout(() => {
        b++;
        resolve(b);
    }, Math.random() * 1000);
});
Promise.all([p1, p2]).then(([a, b]) => {
    console.log(a, b);
}).catch((err) => {
    console.log(err);
})

async,await

  • 把异步的任务通过Promise进行包装
  • 在异步函数(这个函数必须返回Promise对象)前面加上一个关键字 await,后续的代码不需要在then中去执行,而是想普通的同步代码一样书写就行
  • 注意:await必须在函数中才能使用,并且该函数必须申明成异步函数 : 通过关键字 async
async function fn() {
    var a = 1;

    a = await todo(a);

    console.log(a);
}

function todo(a) {
    return new Promise((resolve) => {
        setTimeout(() => {
            a += 10;
            resolve(a);
        }, 1000);
    })
}

运动实例

button.onclick = async function() {

    await startMove(div, {
        width: 200
    });
    await startMove(div, {
        height: 200
    });
    await startMove(div, {
        left: 200
    });
    await startMove(div, {
        top: 200
    });

}

function startMove(ele, attrs, duration=1000, fx='linear') {
    return new Promise((resolve) => {
        animation(ele, attrs, duration, fx, () => {
            resolve();
        });
    })
}

Object.defineProperty()

对对象的属性进行 定义/修改
返回值 :被传递给函数的对象

var obj = {};
Object.defineProperty(obj,y,{
    configurable: false,
    enumerable:false,
    value: 100
})

参数 :

  • obj :要在其上定义属性的对象
  • y :要定义或修改的属性的名称
  • 参数对象 :
    • configurable :设置是否能删除,默认为false
    • value :设置属性值
    • enumerable :改属性是否能够出现在对象的美剧属性中,默认为false
    • writable :该属性值能否被赋值运算符改变,默认为false
    • get() : 当obj的y属性被调用的时候触发,该方法的返回值将作为获取的结果
    • set(value) :当obj的y属性被设置的时候触发,该方法拥有唯一参数,并将该参数的新值分配给该属性
      • get和set不能与configurable,enumerable,value,writable同时存在
let obj = {x:10}

let y = 100;
Object.defineProperty(obj, 'y', {
    get() {
        //当obj的y属性被调用的时候触发,该方法的返回值将作为获取的结果
        console.log('get');
        return y;
    },
    set(value) {
        //当obj的y属性被设置的时候触发
        console.log('set', value);
        y = value;
    }
})
console.log(obj.y);
obj.y = 1;
console.log(obj.y);

数据双向绑定实例 :


你可能感兴趣的:(ES6常用)