ES6新特性

一、let&&const  块级作用域    const也是块级作用域

let、const不存在变量提升

// var 的情况
console.log(foo); // 输出undefined
var foo = 2;

// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;

 var与let的区别:

(1)var定义的变量,作用域是整个封闭函数,是全域的。通过let定义的变量,作用域是在块内的;

(2)关于变量的提升。 var可以变量提升,但是let不存在变量提升。

(3)let不允许在相同的作用域内,重复声明同一个变量。

// 报错
function func() {
    var a = 1;
    let a = 10;
} 
//如果在当前作用域中嵌套另一个作用域,便可在嵌套作用域中用let声明同名变量
function func() {
    var a = 1;
    if(condition){
        let a = 10;
    }
}     
var a=10;
function foo(){
      console.log(a);
      let a=20;
}
foo()//ReferenceError
//let和const声明可以让变量在其作用域上受限于它所使用的块、语句或表达式,与var不同的是,这些变量没有被提升,并且有一个所谓的暂时死区(TDZ)试图访问TDZ中
的这些变量将引发ReferenceError错误,因为只有执行到达声明时才能访问它们。

为什么需要块级作用域:

ES5只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景

  • 第一种场景,内层变量可能会覆盖外层变量。
var tmp = new Date();

function f() {
  console.log(tmp);
  if (false) {
    var tmp = 'hello world';
  }
}
f(); // undefined

上面代码的原意是,if代码块的外部使用外层的tmp变量,内部使用内层的tmp变量。但是,函数f执行后,输出结果为undefined,原因在于变量提升,导致内层的tmp变量覆盖了外层的tmp变量。

  • 第二种场景,用来计数的循环变量泄露为全局变量。

var s = 'hello';
for (var i = 0; i < s.length; i++) {
  console.log(s[i]);
}

console.log(i); // 5

  上面代码中,变量i只用来控制循环,但是循环结束后,它并没有消失,泄露成了全局变量。

  • 第三种场景,循环中的函数
//(1)在循环中使用var声明
//循环中每次迭代同时共享着变量i,循环内部创建的函数全都保留了对相同变量的引用,循环结束时变量i的值为10
var a = [];
for (var i = 0; i < 10; i++) {
  a.push(function () {
    console.log(i);
  });
}
a.forEach(function(item){
  item();//输出10次数字10
})

//(2)为解决以上问题,一般使用立即执行函数,以强制生成变量的副本,如
for (let i = 0; i < 10; i++) {
a.push((function (num) {
    return function(){
        console.log(num);
    }
  })(i));
}


//(3)在循环中使用let声明
//每次循环时let声明都会创建一个新变量i,并将其初始化为当前值,所以内部函数每次都能得到属于他们自己的i的副本
for (let i = 0; i < 10; i++) {
a.push(function () {
    console.log(i);
  });
 }
a.forEach(function(item){
  item();//输出0-9
})  

const用于声明一个常量,设定后值不会发生改变,强行对其进行重新赋值会报错。

这个不可变对于基础类型(按值访问)而言是值不可变;而对于引用类型(按引用访问)是引用地址不变,比如用const声明对象后,可以修改该对象的属性值。

const num=123;
num=456;
console.log(num);//Uncaught SyntaxError: Identifier 'num' has already been declared

const obj={
    a:2
}
//可以修改对象属性的值
obj.a=4;
console.log(obj.a);//4

//抛出语法错误
obj={
  a:4      
}

 1.1  新增数据类型:Symbol

Symbol功能类似于一种标识唯一性的ID,通常情况下,我们可以通过调用Symbol()函数来创建一个Symbol实例。

let s1=Symbol();

或者可以传入一个可选的字符串参数

let s2=Symbol('another symbol');

由于Symbol是一种基础数据类型,所以当我们使用typeof去检查它的类型的时候,会返回一个属于自己的类型symbol。

另外,每个Symbol实例都是唯一的,因此,当你比较两个Symbol实例的时候,将总会返回false。

let s2=Symbol('another symbol');
let s3=Symbol('another symbol');
s2===s3  //false

二、变量的解构赋值

  ES6允许按照一定的模式,从数组和对象中提取值,对变量进行赋值,称为解构。本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。

let[a,b,c]=[1,2,3];//等同于let a = 1;let b = 2;let c = 3;

如果解构不成功,变量的值就等于undefined

let [bar, foo] = [1];
console.log(bar);//1
console.log(foo);//undefined

解构不仅可以用于数组,还可以用于对象。

let { foo, bar } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"

三、箭头函数

ES6 允许使用“箭头”(=>)定义函数。

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

如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。


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

var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
  return num1 + num2;
};

箭头函数与传统的js函数的区别:
(1)箭头函数体内的this对象,就是函数定义时所在的对象,而普通函数的this是函数运行时所在的对象。且箭头函数内部的this值不可被改变
(2)不可以当作构造函数,也就是说不可以使用new命令,否则会抛出一个错误。

    因为箭头函数在创建时this对象就绑定了,更不会指向对象实例。

//普通函数
function fun(){
    console.log('普通函数');
}
var p=new fun();//普通函数

//箭头函数
var fun=()=>{
    console.log('箭头函数');
}
var p=new fun();//Uncaught TypeError: fun is not a constructor

(3)不可以使用arguments对象,该对象在函数体内不存在。

//普通函数
function fun(x,y){
    console.log(arguments[0]);
}
fun('a','b');//a

//箭头函数
var fun=(x,y)=>{
    console.log(arguments[0]);
}
fun('a','b');//ƒ (e,t){return new v.fn.init(e,t,n)}

(4)不可以使用yield命令,因此箭头函数不能用作Generator函数。

(5)没有原型,因为不可以通过new关键字调用箭头函数,因而没有构建原型的需求,所以箭头函数不存在prototype这个属性。

 
ES5与ES6中函数this的指向不同

箭头函数可以让setTimeout里面的this,绑定定义时所在的作用域,而不是指向运行时所在的作用域。下面是另一个例子。

function Timer() {
  this.s1 = 0;
  this.s2 = 0;
  // 箭头函数
  setInterval(() => this.s1++, 1000);
  // 普通函数
  setInterval(function () {
    this.s2++;
  }, 1000);
}

var timer = new Timer();

setTimeout(() => console.log('s1: ', timer.s1), 3100);
setTimeout(() => console.log('s2: ', timer.s2), 3100);
// s1: 3
// s2: 0

上面代码中,Timer函数内部设置了两个定时器,分别使用了箭头函数和普通函数。前者的this绑定定义时所在的作用域(即Timer函数),
后者的this指向运行时所在的作用域(即全局对象)。所以,3100毫秒之后,timer.s1被更新了3次,而timer.s2一次都没更新。

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

四、类
ES6提供了更接近传统语言的写法,引入了Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类,与多数传统语言类似。
为了简化原型链继承,es6的class大大减少了相关代码,不用再去构建有序的原型链,直接用extend就能实现继承。
//原型
function Student(name) {
    this.name = name;
}

Student.prototype.hello = function () {
    alert('Hello, ' + this.name + '!');
}
//
class Student {
    constructor(name) {
        this.name = name;
    }

    hello() {
        alert('Hello, ' + this.name + '!');
    }
}
var xiaoming = new Student('小明');
xiaoming.hello();

class继承

用class定义对象的另一个好处就是继承更方便了,直接用extends来实现

class PrimaryStudent extends Student{
    constructor(name,grade){
        super(name);
        this.grade=grade;
    }
    myGrade(){
        console.log('my grade is'+this.grade);
    }
}
var stu=new PrimaryStudent('an','32');
stu.hello();//hello,an
stu.myGrade();//my grade is32

五、字符串模板
模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。

六、默认参数值

ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。

 

七、for of/in遍历

区别

(1)for in遍历的是数组的索引,而for of遍历的是数组的元素值;

(2)for in更适合遍历对象,不要使用for in遍历数组;

for...in

Array.prototype.method=function(){
    console.log(this.length);
}
var myArr=[1,2,3,4,5];
console.log('for...in遍历');
for(var i in myArr){
    console.log(myArr[i])
}

 遍历结果:

                          

当你需要向数组中添加额外的方法(或另一个对象)时,for...in 会遍历数组所有可枚举的属性,意味着如果向数组的原型中添加任何其他属性,这些属性也会出现在循环中。

for/in与Object.keys(obj)的区别:

最主要的区别是for/in会走原型链,而Object.keys()不会走原型链

for...of

for...of遍历的只是数组内的元素,不包含数组原型上的属性和方法。
         

(3)for of只能遍历拥有迭代器对象的对象

var obj={
    a:1,
    b:2,
    c:3
}
for(let i in obj){
    console.log(i)//a,b,c
}
for(let item of obj){
    console.log(item)//Uncaught TypeError: obj is not iterable
}

一个数据结构只要部署了Symbol.iterator属性,就被视为具有iterator接口,就可以使用for of循环。

而对象是没有Symbol.iterator这个属性,所以使用for of会报obj is not iterable。

for of不同于forEach,他可以与break、continue和return配合使用,也就是说for of循环可以随时退出循环,而forach()不能跳出循环,break报错

哪些数据结构部署了Symbol.iterator属性了呢??

  • 数组Array
  • Map
  • Set
  • String
  • arguments对象

以上这些都可以直接使用for of循环,凡是部署了iterator接口的数据结构也都可以使用数组的扩展运算符和解构赋值操作。

假如想要对象也可以使用for of循环怎么办?使用Objct.keys()获取对象的key值集合后,再使用for of。

var obj={
    a:1,
    b:2,
    c:3
}
for(let item of Object.keys(obj)){
    console.log(item)//a,b,c
}

判断一个数据结构是否具有可迭代能力,只有当数据具有Symbol.iterator属性的时候才可以使用for...of进行迭代。

console.log(Array.prototype.hasOwnProperty(Symbol.iterator));//true

console.log(Object.prototype.hasOwnProperty(Symbol.iterator));//false

八、扩展运算符

扩展运算符(用三个连续的点 (...) 表示)是 ES6 中的新概念,使你能够将字面量对象展开为多个元素

通过减少赋值语句的使用,或者减少通过下标访问数组或对象的方式,使代码更加简洁优雅,可读性更佳。

const books = ["Don Quixote", "The Hobbit", "Alice in Wonderland", "Tale of Two Cities"];
console.log(...books);

Prints: Don Quixote The Hobbit Alice in Wonderland Tale of Two Cities

应用:

(1)复制数组, 使用扩展运算符拷贝对象或数组时,只对第一层深拷贝,二层及以后为浅拷贝

var a1=[1,3,2,5,3];
//ES5
var a2=a1.concat();
//ES6
var a3=[...a1];

(2)合并多个数组

//ES5
var a2=arr1.concat(arr2,arr3);
//ES6
var a3=[...arr1,...arr2,...arr3];

(3)使用扩展运算符展开数组代替apply方法,将数组转为函数的参数。

//ES5
var a2=Math.max.apply(this,[1,5,3,8,6]);
//ES6
var a3=Math.max(...[1,5,3,8,6]);
//相当于Math.max(1,5,3,8,6);

(4)将字符串转为数组

var str='abcde';
//ES5
var a2=str.split('');
//ES6
var a3=[...str];
//["a", "b", "c", "d", "e"]

(5)Map结构

let map=new Map([
    [1,'one1'],
    [2,'one2'],
    [3,'one3']
]);
let arr=[...map.keys()];
console.log(arr);//[1,2,3]
九、Set数据结构

ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。   可用于数组去重


Set本身是一个构造函数,用来生成 Set 数据结构。 Set函数接收一个数组作为参数用来初始化。


// 例一
const set = new Set([1, 2, 3, 4, 4]);
[...set] 获Array.from(set);
// [1, 2, 3, 4]

// 例二
const items = new Set([1, 2, 3, 4, 5, 5, 5, 5]);
items.size // 5

(1)se结构与数组的区别

  • 添加元素   add()        如:const set = new Set([1, 2, 3, 4, 4]);  set.add('1');
  • 删除元素   set.delete(value);
  • 判断某值是否存在  set.has(value)
  • 清除所有值   set.clear();
  • 获取长度   set.size

(2)将set结构转换为数组:

  • [...set]
  • Array.from(set)

 (3)Set和数组的相同点:

  • 数组的map()和filter()方法用于set数据结构

   注意写法:(不是在set实例后面写方法,是在参数数组后面)

           

  • Set支持for...of和forEach循环

  注意: 在forEach((item,index){})循环中发现:Set的value值与index值相同

  Set结构:

  

  数组:

  

10、Map数据结构
Map结构出现的目的:
由于JS的对象(Object)本质上是键值对的集合(Hash结构),但是传统意义上只能用字符串当作键,这就给它的使用带来了很大的限制。
  ES6提供的Map数据结构,类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object结构提供了“字符串——值”的对应,Map结构提供了“值——值”的对应,是一种更加完善的Hash结构,如果你需要“键值对”的数据结构
,Map比Object更合适。

传统对象键名必须为字符串
let obj={
    a:1,
    666:2
}
console.log(obj.666);//报错
map结构是一种“值对值”的数据结构
let map=new Map();
map.set(666,2);
console.log(map.get(666));//2

Map结构的属性和方法

(1)属性:size
(2)set(key,val):向字典中添加新元素
(3)get(key):通过键查找特定的数值并返回
(4)has(key):如果键存在字典中返回true
(5)delete(key)
(6)clear():将字典中的所有元素都删除

遍历方法
(1)keys():将字典中包含的所有键名以数组形式返回
(2)values():将字典中包含的所有数值以数组的形式返回
(3)forEach()遍历字典中的成员

Map结构初始化的参数,可以接受一个二维数组作为参数。
let map=new Map([
    ['name','张三'],
    ['title','Author']
])
console.log(map.size)//2
console.log(map.has('name'))//true
console.log(map.get('name'))//"张三"

for(let key of map.keys()){ console.log(key);//name title } for(let val of map.values()){ console.log(val);//张三 Author }

11、ES6新增的数组方法?
(1)map()
map():对数组的每个元素进行一定的操作(映射)后,会返回一个新的数组。

   map()传参与forEach()相同

(2)forEach()
  forEach():该方法对数组中的每一项运行给定函数,该方法没有返回值。其实就是遍历循环
  forEach()包含两个参数,第一个参数是匿名函数,第二个参数是this。匿名函数中包含三个参数:item(每一项的值),index(每一项的索引),self(代表所遍历函数自己)

  map()和forEach()的区别:
    map()会返回一个新的数组,forEach()不会返回数据;
    forach()允许改变原数组的元素,map()返回的是新数组,不会修改原数组。
    
(3)filter()
  filter():该方法对数组中的每一项运行给定函数,返回该函数会返回每一相对应的true或false,最后返回包含留下为true对应的元素的数组。

  filter()传参与forEach()相同。

  filter()用法:
  • 创建一个数组,判断数组中是否存在某个值或过滤某些值
var arr=[2,4,3,2,5];
console.log(arr.filter(item=>item==3));//[3]
  • 去掉数组中的空数组、空字符串、undefined、null
var arr = ['1','2',undefined,'','3.jpg',undefined,null];
var newArr = arr.filter(function(item){
    return item;
})
console.log(newArr);//["1", "2", "3.jpg"]
  • 数组去重
var arr = [1,2,4,5,3,2,4,3,6,8,6,8,8,8];
var newArr=arr.filter(function(item,index,self){
    return self.indexOf(item)==index;
})
console.log(newArr);//[1, 2, 4, 5, 3, 6, 8]

(4)reduce()
  归并方法reduce():该方法会迭代数组中的每一项,然后生成一个最终返回值。接收两个参数(回调函数,初始值),将第二个参数作为初始值
  数组求和来启动累积。
  reduce()接收一个函数作为累加器,回调函数接收最多四个参数:初始值(或者上一次回调函数的返回值),当前元素值,当前索引,调用 reduce 的数组。

//数组求和
var arr = [1,2,4,5,3];
var sum=arr.reduce(function(pre,item){
        return pre+item;
},0)
console.log(sum);//15

(5)every()
  对数组的每一项都运行给定的函数,如果每一项都返回true,则返回true,否则返回false。

(6)some()
  对数组的每一项都运行给定的函数,如果任一项返回true,则返回true。
  
function add(){
    var arr=[1,2,3,4,5];
    return arr.every(function(item){
        return item>3;
    })
}
console.log(add());//false

function add(){
    var arr=[1,2,3,4,5];
    return arr.some(function(item){
        return item>3;
    })
}
console.log(add());//true

11、模块

导入、导出

12、Promise

 13、Object.assign()、Object.create()

原文地址:https://www.cnblogs.com/xiaoan0705/p/8671495.html