ES6新特性

本文详细介绍了ES6中新增的重要特性,包括let和const声明、块级作用域、箭头函数、类、模板字符串、解构赋值、默认参数、for...of循环、展开运算符、Set数据结构、Map、Promise及模块的导入导出等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、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;
    }
}     

 

为什么需要块级作用域:

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      
}

 

2、变量的解构赋值

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"

 

3、箭头函数

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命令,否则会抛出一个错误。
//普通函数
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,所以也就不能用作构造函数。

所以,箭头函数转成ES5的代码如下。

// ES6
function foo() {
  setTimeout(() => {
    console.log('id:', this.id);
  }, 100);
}

// ES5
function foo() {
  var _this = this;

  setTimeout(function () {
    console.log('id:', _this.id);
  }, 100);
}

上面代码中,转换后的ES5版本清楚地说明了,箭头函数里面根本没有自己的this,而是引用外层的this


4、类
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

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

6、默认参数值

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

 

7、for of值遍历
我们都知道for in循环用于遍历数组,类数组或对象,ES6中新引入的for of循环功能相似,不同的是每次循环它提供的不是序号而是值

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

for...in

const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for (const index in digits) {
  console.log(digits[index]);
}

依然需要使用 index 来访问数组的值

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

所以for in更适合遍历对象,不要使用for in遍历数组。

 for...of
for...of 循环用于循环访问任何可迭代的数据类型。
for of遍历的只是数组内的元素,不包括数组原型上的属性

 8、展开运算符

展开运算符(用三个连续的点 (...) 表示)是 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
9、set数据结构  Map

ES6 提供了新的数据结构 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

map()方法将调用的数组的每个元素传递给指定的函数,并返回一个数组,它包含该函数的返回值。

 传递给map()的函数的调用方式和传递给forEach()的函数的调用方式一样。但传递给map()的函数应该有返回值。注意:map()返回的是新数组:它不修改调用的数组。

举个例子:

      要求:为数组 arr 中的每个元素求二次方。不要直接修改数组 arr,结果返回新的数组 

      实现:

 function square(arr){  
       return arr.map(function(item)
      {  
        return item*item;}
        );     }   
var arr=[1, 2, 3, 4];   console.log(square(arr)); 结果:[1,4,9,16]
10、promise

ES5新增对数组的操作map,filter和reduce分别是什么?
map():对数组的每个元素进行一定的操作(映射)后,会返回一个新的数组。
map()传参与forEach()相同

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

filter():该方法对数组中的每一项运行给定函数,返回该函数会返回包含true和false的项组成的数组。
filter()传参与forEach()相同。

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

如数组扁平化处理可以用reduce
例:实现一个flatten方法,使得输入一个数组,该数组里面的元素也可以是数组,该方法会输出一个扁平化的数组。
arr=[[1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14]]]], 10];
console.log([1,2,2,3,4,5,5,6,7,8,9,11,12,12,13,14,10]);
法一:递归
function flatten(arr){
      var res=[];
     for(var i=0;i<arr.length;i++){
          if(Array.isArray(arr[i])){
                 res=res.concat(flatten(arr[i]));
            }
         else{
                 res.push(arr[i]);
            }
        }
       return res;
}       

法二:reduce

function flatten(arr){
      return arr.reduce(function(pre,item){
             return pre.concat(Array.isArray(item)?flatten(item):item);
      },[]);
}

 法三:map

function flatter2(arr){
    var res=[];
    arr.map(function(item){
        if(Array.isArray(item)){
            res=res.concat(flatter2(item));
        }
        else{
            res.push(item);
        }
    })
    return res;
}

 

11、模块

导入、导出

 

转载于:https://www.cnblogs.com/xiaoan0705/p/8671495.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值