ES6中的常用知识点总结

1、let 和 const

             ES5 只有全局作用域和函数作用域,没有块级作用域,ES6中新增了let命令和const命令用于声明变量,且类似于C+、Java等语言是块级作用域。const声明一个只读的常量,必须在定义的时候初始化。let和const声明的变量不存在变量提升,但存在暂时性死区。而且相比于var 语法更加严格,不允许在相同作用域内重复声明同一个变量。let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性。也就是说,从 ES6 开始,全局变量将逐步与顶层对象的属性脱钩。

//不存在变量提升
console.log(m);   //错误:ReferenceError: m is not defined
let m=6;


//块级作用域
var a = [];
for (let i = 0; i < 10; i++) {
    a[i] = function () {
        console.log(i);
    };
}
a[6]();    // 6
//变量i是let声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以最后输出的是6


//暂时性死区
var tmp = 123;
if (true) {
    tmp = 'abc'; // 错误:ReferenceError: tmp is not defined
    //只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,凡是在声明之前就使用这些变量,就会报错
    let tmp;
}

//不允许在相同作用域内重复声明同一个变量
function func() {
    let a = 1;
    let a = 2;   //错误:SyntaxError: Identifier 'a' has already been declared
}

//const声明一个只读的常量
const mm=12;
mm=13;    //错误:TypeError: Assignment to constant variable.

const arr=[1,2,3];
arr.push(4);
//const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。
console.log(arr);   //[ 1, 2, 3, 4 ]

arr=[];          //错误:TypeError: Assignment to constant variable.
                 //变量指向的内存地址改变会报错

 

2、解构赋值

          ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构。只要等号两边的模式相同,左边的变量就会被赋予对应的值,可以避免对变量一条一条地赋值。

       常用的有数组形式的解构赋值和对象的解构赋值。解构赋值的时候也可以指定默认值,只有当一个成员严格等于undefined,默认值才会生效。

         注意null不严格等于undefined

// 以前我们给变量赋值,只能直接指定值
var a = 1;
var b = 2;
var c = 3;
console.log(a,b,c); // 1 2 3

// 现在用解构赋值的写法就变得简单了,只要模式匹配上了就行了,如下
// 注意数组是有顺序的
var [a,b,c] = [11,22,33];
console.log(a,b,c); // 11 22 33

var [b,a,c] = [11,22,33];
console.log(a,b,c); // 22 11 33

// 当然解构赋值还有嵌套比较复杂的写法,如下
let [foo,[[bar],[baz]]] = [111,[[222],[333]]];
console.log(foo,bar,baz); // 111 222 333

let [head,...foot] = [1,2,3,4];
console.log(head,foot); // 1 [2,3,4]

// 如果解构不成功,变量的值就等于undefined,如下
var [bar3,foo3] = [1000];
console.log(bar3,foo3); // 1000 undefined

// 另一种情况是不完全解构,即等号左边的模式,只匹配一部分的等号右边的数组。这种情况下,解构依然可以成功
let [x,y] = [10000,20000,30000];
console.log(x,y); // 10000 20000

// 默认值可以引用解构赋值的其他变量,但该变量必须已经声明
let [a=1,b=a] = [2,3];
console.log(a,b); // 2 3

// 对象的解构也可以指定默认值
var {x,y=5} = {x:1};
console.log(x,y); // 1 5

//对象的解构赋值解构不仅可以用于数组,还可以用于对象(json)
//对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;
//而对象的属性没有次序,变量必须与属性同名,才能取到正确的值
var {a,b} = {a:'apple',b:'banana'};
console.log(a,b); // apple banana
var {b,a} = {a:'apple',b:'banana'};
console.log(a,b); // apple banana

// 如果变量名与属性名不一致,必须写成下面这样
let obj = {first:'hello',last:'world'};
// first ---> f,那么此时f就是first,而不是undefined了,有点类似别名的概念
let {first:f,last} = obj;
console.log(f,last); // hello world

//1.也就是说,对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。 真正被赋值的是后者,而不是前者
//2.v是匹配的模式,n才是变量。真正被赋值的是变量n,而不是模式v。
//注意,采用这种写法时,变量的声明和赋值是一体的
// v ---> n,那么此时n就是vue,而不是undefined了
var {v:n} = {v:'vue',r:'react'};
console.log(n); // vue
console.log(v); // Uncaught ReferenceError: v is not defined
console.log(r); // Uncaught ReferenceError: r is not defined

 

3、模板字符串

      模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,将变量名写在${}中可以向字符串中嵌入变量。${}大括号内部可以放入任意的 JavaScript 表达式,可以进行运算,以及引用对象属性。

      注意:如果使用模板字符串表示多行字符串,所有的空格和缩进都会被保留在输出之中

//使用+号连接字符串
var a=12;
var b=36;
var c=a+b;
var str=a+'+'+b+"的结果是:"+c;
console.log(str);       //输出:12+12的结果是:24


//使用模板字符串
let x=12;
let y=16;
let resultStr=`${x}*${y}的结果是:${x*y}`;
console.log(resultStr);   //输出:12*16的结果是:192


//模板字符串的嵌套,
const str1=`1~9的平方数:
${[1,2,3,4,5,6,7,8,9].map((item)=>{return `${item}的平方:${item*item}`}).join(`
`)}
结束`;

console.log(str1);
//输出:
/*1~9的平方数:
1的平方:1
2的平方:4
3的平方:9
4的平方:16
5的平方:25
6的平方:36
7的平方:49
8的平方:64
9的平方:81
结束*/

4、箭头函数

ES6 允许使用“箭头”(=>)定义函数。
箭头函数有几个使用注意点。
(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
(4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。

注意:在使用箭头函数时,this的指向是一个重点。箭头函数中的this指向是定义时所在的作用域而不是运行时所在的作用域,也就是定义时代码外层块的this指向。这是因为箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。正是因为它没有this,所以也就不能用作构造函数。

//箭头函数的写法
var f = v => v;

// 等同于
var f = function (v) {
    return v;
};

//箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分
var sum = (num1, num2) => num1 + num2;
//等同于   箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回
var sum = (num1, num2) => {return num1 + num2};
// 等同于
var sum = function(num1, num2) {
    return num1 + num2;
};



//箭头函数中this的指向
var a=10;
var obj={
    a:1,
    fn1:function(){
        this.a++;
    },
    fn2:()=>{
        this.a++;
    }
};
obj.fn1();
console.log(a);     //10
console.log(obj.a); //2

obj.fn2();
console.log(a);     //11
console.log(obj.a); //2
//这里的this指向window

5、对象的扩展

属性的简洁表示法,ES6 允许直接写入变量和函数,作为对象的属性和方法。

Object.js用来判断两个值是否严格相等,ES5 比较两个值是否相等,只有两个运算符:相等运算符(==)和严格相等运算符(===)。它们都有缺点,前者会自动转换数据类型,后者的NaN不等于自身,以及+0等于-0。

Object.assign是进行值的浅拷贝而不是深拷贝,对象拷贝得到的是这个对象的引用。

//属性的简洁表示法
var name='cc';
var obj = {
    name,
    method() {
        return this.name;
    }
};
console.log(obj.method());  //cc
//等同于
var obj1 = {
    name:name,
    method:function() {
        return this.name;
    }
};
console.log(obj1.method());  //cc




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

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


//Object 用于对象的浅拷贝
var src={a:1};
var src1={a:2,b:{mm:'cc'}};
Object.assign(src, src1);
console.log(src);   //{ a: 2, b: { mm: 'cc' } }

src1.b.mm='another';
console.log(src);   //{ a: 2, b: { mm: 'another' } }

 

6、Set与Map

ES6 提供了新的数据结构 Set、WeakSet 、Map、WeakMap 。

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

WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,任何具有 Iterable 接口的对象,都可以作为 WeakSet 的参数。

Map 数据结构,它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。WeakMap的键名所指向的对象,不计入垃圾回收机制,只接受对象作为键名(null除外),不接受其他类型的值作为键名。

Set、Map 结构的实例有四个遍历方法,可以用于遍历成员。

  • keys():返回键名的遍历器
  • values():返回键值的遍历器
  • entries():返回键值对的遍历器
  • forEach():使用回调函数遍历每个成员
//Set基本用法
let set1=new Set();
set1.add(1);
set1.add(1);
set1.add(2);
set1.add(3);
for (let i of set1) {
    console.log(i);    //1,2,3    注意:成员的值唯一
}


//Set 内部判断两个值是否不同,使用的算法叫做“Same-value-zero equality”,它类似于精确相等运算符(===),主要的区别是NaN等于自身
let set2 = new Set([1, 2, 3, 4, 4,NaN,NaN]);
let arr=[...set2];
console.log(arr);           //[ 1, 2, 3, 4, NaN ]
console.log(set2.size);    //5
console.log(set2.has(2));   //true
set2.delete(2);
console.log(set2.has(2));   //false



//Map的基本用法
const m = new Map();
const o = {p: 'Hello World'};
m.set(o, 'content')
console.log(m.get(o)); // "content"
console.log(m.has(o)); // true
console.log(m.delete(o)); // true
console.log(m.has(o)) // false



//Map 也可以接受一个数组作为参数。该数组的成员是一个个表示键值对的数组
const map = new Map([
    ['name', '张三'],
    ['title', 'Author']
]);
for(let [key,value] of map)
{
    console.log(key, value);
}
//name 张三
//title Author

 

7、for-of循环

ES6引入了for-of循环,作为遍历所有数据结构的统一的方法。
for-in循环获取键名,for-of循环直接获取键值,且没有for-in循环的一些缺点。
for-of循环默认调用 Iterator 接口,原生具备 Iterator 接口的数据结构:Array、Map、Set、String、TypedArray、arguments 对象、NodeList 对象。

关于JavaScript中forEach、for-in、for-of循环的比较,可以看这篇博客:https://blog.csdn.net/cc_fys/article/details/78161573

//遍历Array
var arr=[2,56,78,34,7];
for(var i of arr)
{
    console.log(i);   //2 56 78 34 7
}
//遍历Map
var map = new Map().set('age', 10).set('name', 'cc');
for (var pair of map) {
    console.log(pair);         //[ 'age', 10 ] [ 'name', 'cc' ]
}
//遍历Set
var set=new Set();
set.add('name').add('age');
for (var j of set) {
    console.log(j);         //name age
}
//遍历String
for (var n of 'mystr') {
    console.log(n);         //m y s t r
}
//遍历TypedArray
var int16 = new Int16Array(2);
int16[0] = 42;
for (var t of int16) {
    console.log(t);         //42 0
}
//遍历arguments
function exa(){
    for (var ar of arguments) {
        console.log(ar);         //go 67 dog
    }
}
exa('go',67,'dog');
 
//遍历NodeList 对象
var paras = document.querySelectorAll("div");
for (let d of paras) {
    console.log(d);
}

8、promise对象

Promise被设计用于改善JS中的异步编程,与事件及回调函数相比,在异步操作方面提供了更多的控制权与组合性。可以避免层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。

Promise有三种状态:挂起、已完成、已拒绝。

常用的Promise 库有BluebirdQwhen。

Promise.race方法和Promise.all方法是将多个 Promise 实例,包装成一个新的 Promise 实例。Promise.all方法接受一个数组作为参数,只有传入的所有Promise都完成,返回的Promise才能完成。如果有任何Promise被拒绝,返回的主Promise就立即会被拒绝。Promise.race方法,只有第一个决议的promise取胜,并且其决议结果成为返回Promise的决议。

//promise基本用法
//Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject
//Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
//then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用
let promise = new Promise(function(resolve, reject) {
    console.log('Promise');
    resolve(54);
});
setTimeout(function(){
    console.log('timeout');
},0);
promise.then(function(value) {
    console.log(value);
});
console.log('Hi!');

//输出:
//Promise
//Hi
//value
//timeout
//说明:Promise 新建后立即执行,所以首先输出的是Promise。然后,then方法指定的回调函数,将在本轮事件循环的末尾执行,setTimeout(function(){},0)将在下一轮事件循环的开始执行


//Promise.all与Promise.race
let p1=Promise.resolve(42);
let p2=Promise.resolve('hello world');
let p3=Promise.reject('Oops');

Promise.race([p1,p2,p3]).then(function(msg){
    console.log(msg);     //42
});

Promise.all([p1,p2,p3]).catch(function(err){
    console.log(err);     //oops
});

Promise.all([p1,p2]).then(function(msgs){
    console.log(msgs);     //[ 42, 'hello world' ]
});

9、Generator生成器、Iterator迭代器

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

Iterator 的遍历过程是这样的。

(1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。

(2)第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。

(3)第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。

(4)不断调用指针对象的next方法,直到它指向数据结构的结束位置。

 

执行 Generator 函数会返回一个遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。

形式上,Generator 函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,定义不同的内部状态

//生成器
function* helloWorldGenerator() {
    yield 'hello';
    yield 'world';
    return 'ending';
}

var hw = helloWorldGenerator();
console.log(hw.next());   //{ value: 'hello', done: false }
console.log(hw.next());   //{ value: 'world', done: false }
console.log(hw.next());   //{ value: 'ending', done: true }
console.log(hw.next());   //{ value: undefined, done: true }

//helloWorldGenerator 函数会返回一个迭代器对象
//使用for...of循环遍历
for(let item of  helloWorldGenerator())
{
    console.log(item);
}
//输出:
//hello
//world


//迭代器
function idMaker() {
    var index = 0;
    return {
        [Symbol.iterator]:function(){
            return this;
        },
        next: function() {
            return {value: index++, done: false};
        }
    };
}

var it = idMaker();
console.log(it.next().value); // 0
console.log(it.next().value); // 1
console.log(it.next().value); // 2


//当使用for...of循环遍历某种数据结构时,该循环会自动去寻找 Iterator 接口。也就寻找idMaker()中的Symbol.iterator属性
for(let item of idMaker())
{
    if(item<5)
        console.log(item);   //0 1 2 3 4
    else
        break;
}


10、class

ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。

constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。

类和模块的内部,默认就是严格模式,所以不需要使用use strict指定运行模式。

Class定义的类不存在变量提升,需要在使用之前定义。

 

class Foo{
    constructor(a,b)
    {
        this.x=a;
        this.y=b;
    }
    gimmeXY(){
        return this.x*this.y;
    }
}

var f=new Foo(5,15);
console.log(f.x);   //5
console.log(f.y);   //15
console.log(f.gimmeXY());  //75


//class中的继承
class Bar extends Foo{
    constructor(a,b,c){
        super(a,b);
        this.z=c;
    }
    gimmeXYZ(){
        return super.gimmeXY()*this.z;
    }
}

var b=new Bar(5,15,25);
console.log(b.x);        //5
console.log(b.y);        //15
console.log(b.z);        //25
console.log(b.gimmeXYZ()); //1875

 

参考:http://es6.ruanyifeng.com/

          《你不知道的JavaScript》(中卷)

          

 

你可能感兴趣的:(ES6)