ES6 & ESNext 规范及编译工具简介

1、历史简介

ECMAScript:一种标准(ECMA-262)
JavaScript:一种语言
ES5:2011发布,第五个版本
ES6:2015发布的,第六个版本,大型变动 break change
ESNext:符合现在主流ES的预案,有几个阶段(大于1的就是属于ESNext)
阶段:
Stage 0:开工
Stage 1:收录意见
Stage 2:草案
3:候选者(基本要准备发布)
4:定案

2、变量定义新形式(let、const)

Q: var 定义变量有什么问题?

  1. 变量提升
  2. 无法形成词法作用域
  3. 可以随意篡改变量值,重复声明

2.1 let

let 关键字用于声明一个块级作用域的局部变量,可以将 let 声明的变量重新赋值。let 声明的变量只在代码块内部有效。

if (true) {
    let i = 1;
    console.log(i); // 输出 1
}
console.log(i); // 报错,i 未定义

console.log(a); // undefind(变量提升)
var a=2;

console.log(b); //报错,暂时性死区,let在下面定义了
let b=2

死区

function bar(x=y,y=2){
    return [x,y]
}
bar()
//报错,y死区(运行时报错)
function bar(x=2,y=x){
    return [x,y]
}
bar()
//正常执行

不允许重复声明

var a=2;
let a=1;
//报错

2.2 const

const 关键字用于声明一个块级作用域只读常量,一旦 const 声明了某个变量,就不能使用赋值语句改变它的值。常量必须在声明时进行初始化

const PI = 3.1415926535;
PI = 3; // 报错,无法修改常量

const obj={}//具体引用的地址不能变
obj.a=1//不报错
obj=1//报错

ES5里没有块级作用域

//ES6写法
if(true){
   function fn(){
   }
}
//ES5写法
if(true){
 var fn = function(){
   }
}

2.3 实际开发中的使用

  1. 示例一:循环中使用 let 声明变量避免问题
for (let i = 0; i < 5; i++) {
    setTimeout(function() {
        console.log(i); // 0, 1, 2, 3, 4
    }, 1000);
}

使用 let 声明的变量 i 有块级作用域,在每一次循环中都会重新定义并赋值,避免了使用 var 声明变量可能导致的变量共享问题。

  1. 示例二:使用 const 声明常量
const PI = 3.1415926;
const URL = "https://www.example.com";

使用 const 声明常量可以防止被修改,保证代码的可靠性和稳定性。

  1. 示例三:使用 const 声明对象属性避免误修改
const user = {
    name: "张三",
    age: 18,
    gender: "男"
};

user.name = "李四";
console.log(user); // { name: "李四", age: 18, gender: "男" }

Object.freeze(user);

user.age = 20;
console.log(user); // { name: "李四", age: 18, gender: "男" }

使用 const 声明对象属性可以避免误修改,同时使用 Object.freeze() 方法可以将对象冻结,防止意外修改对象属性。

总的来说,使用 let 或者 const 声明变量可以解决一些以往使用 var
变量定义存在的问题,例如变量作用域混乱,变量共享、变量易被修改等情况。使用 let 和 const
可以使代码更加健壮、可维护,提升开发效率和代码质量。

3、解构语法

定义:将数组或对象中的元素提取出来并赋值给变量

3.1 数组解构

const [a, b, c] = [1, 2, 3];
console.log(a, b, c); // 1 2 3

在这个例子中,JavaScript 引擎背后发生的事情如下:

const tempArray = [1, 2, 3];
const a = tempArray[0];
const b = tempArray[1];
const c = tempArray[2];
console.log(a, b, c); // 1 2 3
let [a, ...rest] = [1, 2, 3];
console.log(a, rest); // 1 [2,3]

[x,y,,z]=[1,2,3,4]
console.log(x,y,z); // 1,2,4

3.2 对象解构


const {firstname: first, lastname: last, ...rest} = { firstname: 'John', lastname: 'Doe' };
console.log(first, last); // John Doe

let obj={
  p:['Hello',
  {y:'World'}
  ]
}
let {
 p:[x,{y}]
}=obj
//x,y   Hello,World

3.3 嵌套解构

const [a, [b, [c]]] = [1, [2, [3]]];
console.log(a, b, c); // 1 2 3

3.4 默认值

const [a = 1, b = 2] = [];
console.log(a, b); // 1 2

let obj={a:1,b:2,c:3}
const {a=2}=obj
//a=obj.a?obj.a:2,obj的a存在,取obj.a,不存在,取默认值2
const {a:d=2}=obj
//另外取名字  d=obj.a?obj.a:2

4、模板字符串

const name = "Tom";
const age = 20;
const str = `My name is ${name}, I'm ${age} years old.
I'm from China.`;
console.log(str);

5、 箭头函数

箭头函数与普通函数有哪些不同?

  1. this 指向,箭头函数不能定义构造器
  2. 不能 new
  3. 内部无 arguments 对象
  4. this 绑定方法失效,比如:call apply bind

// 传统函数定义
function multiply(x, y) {
  return x * y;
}
// 箭头函数
const multiply = (x, y) => x * y;

function foo(){
   setTimeout(() => {
      console.log(`num`,this.num);//没有自己的this,这里的this是foo的this
    }, 1000);
}
var num =123
foo()//输出 num 123
//改变函数指向
foo.call({
num:456
})
foo()//输出 num 456



//看一个例子
function Timer(){
  this.s1=0;
  this.s2=0;
  //箭头函数
  setInterval(()=>this.s1++,1000);//外层函数的this,指向Timer,改变了Timer实例的s1
  //普通函数
  setInterval(function(){
    this.s2++;//function的this,没有改变Timer实例的s2
  },1000);
}
var timer =new Timer()
    setTimeout(() => {
      console.log('s1:',timer.s1)
    }, 3100);
    setTimeout(() => {
      console.log('s2:',timer.s2)
    }, 3100);
    //输出:
    //s1:3
    //s2:0

6、ES6新增的数组方法

ES6新增了一些数组方法,具体可以看 ES6 入门教程
Js的全部数组方法,可以看 数组方法总结+详解
如:

  • Array.flat()
    Array.flat() 方法创建一个新数组,其中所有子数组元素递归地连接到指定的深度。(打平数组深度)
 const arr = [1, [2, [3, [4]]]];
console.log(arr.flat(1)); // [1, 2, [3, [4]]]
console.log(arr.flat(2)); // [1, 2, 3, [4]]
console.log(arr.flat(3)); // [1, 2, 3, 4]
console.log(arr.flat(4)); // [1, 2, 3, 4]
  • Array.includes()
    Array.includes() 方法判断一个数组是否包含某个指定的元素,根据情况,如果包含则返回 true ,否则返回 false
const arr = [1, 2, 3, 4, 5];
console.log(arr.includes(3)); // true
console.log(arr.includes(6)); // false

Array.from()
Array.of()
Array.find()
Array.findIndex()
Array.includes()
Array.flat()
Array.flatMap()
Array.every()
Array.some()
Array.reduce()
Array.reduceRight()
Array.sort()
Array.reverse()
Array.fill()
Array.slice()
Array.splice()
Array.copyWithin()
Array.forEach()
Array.map()
Array.filter()

7、Set和Map

Set 和 Map 主要的应用场景在于 数据重组数据储存
Set 是一种叫做 集合 的数据结构,Map 是一种叫做 字典 的数据结构。

7.1 Set

  • ES6 新增的一种新的数据结构,类似于数组,成员唯一(内部元素没有重复的值)。且使用键对数据排序即顺序存储。
  • Set 本身是一种构造函数,用来生成 Set 数据结构。
  • Set 对象允许你储存任何类型的唯一值,无论是原始值或者是对象引用
var s1=new Set();
var s2=new Set([1,2,3]);
//  重复元素在Set中会自动过滤(即重复元素不会被保留)
var s=new Set([1,2,3,3]);
s.add(4);  // set{1,2,3,4}
s.add(3); //set{1,2,3,4}
s.size();   //4
s.has(3);  //true

weakSet 和Set一样的用法,不过里面的值只能是Symbol或对象,垃圾回收不会考虑weakSet 和weakMap里的值,内部变量没有可达性

7.2 Map

Map是一组键值对的结构,用于解决以往不能用对象做为键的问题,具有极快的查找速度。(注:函数、对象、基本类型都可以作为键或值。)

const m=new map(['Kris',21],['Bob',19],['Lily',25],['Jack',27]);
m.get('Kris');   //  21
m.get('Lily');   //  25

var mm=new Map( );	//初始化一个空的 map
mm.set('Pluto',23);	//添加新的key-value 值
mm.has('Pluto');   //true	是否存在key ‘Pluto’
mm.get('Pluto');   	//23
mm.delete('Pluto');	//删除key   ‘Pluto ’

一个key只能对应一个value,所以多次对一个key放入value,后面的值会把前面的值冲掉
const m=new Map([['Lily',100],['Bob',97]]);
m.get('Bob');  //97
m.set('Bob',88);  //对key放入新的value
m.get('Bob');  //88

8、面向对象编程——class 语法

JavaScript 的类最终也是一种函数,JavaScript 的 class 语法只是语法糖,使用 class 关键字创建的类会被编译成一个函数,因此其底层原理与函数有一些相似之处。

JavaScript原型继承实现继承的有哪些方式(详情可以看这篇博客

  • 原型链继承
  • 借用构造函数
  • 组合继承
  • 原型式继承
  • 寄生式继承
  • 寄生组合继承

Class 语法继承,最接近我们自己实现的那种继承方式?

  • 寄生组合继承

8.1 类、构造函数

ES6里的类

class Point{
  constructor(x,y){
     this.x=x;
     this.y=y;
  }
  toString(){
     return '('+this.x+','+this.y+')'
  }
}
new Point()

等价于ES5


var Point = /*#__PURE__*/ (function () {
  function Point(x, y) {
    this.x = x;
    this.y = y;
  }
  var _proto=Point.prototype;
  _proto.toString=function toString(){
     return '('+this.x+','+this.y+')'
  }
  return Point;
})();
new Point();

8.2 继承

在 JavaScript 中,继承是通过类的 prototype 属性实现的。在定义类时,可以使用 extends 关键字来继承其他的类:

class Student extends Person {
    constructor(name, age, grade) {
        super(name, age);
        this.grade = grade;
    }
}

8.3 静态方法和属性

类中的静态方法和属性可以使用 static 关键字来定义,它们不是类实例的属性,而是类本身的属性和方法。

class Person {
    static species = "human";

    static saySpecies() {
        console.log(`We are ${this.species}.`);
    }
}

8.4 getter 和 setter

在类中定义 getter 和 setter 方法可以让我们封装实例的内部数据属性,使得这些属性的读写行为更加的安全和合理。

class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }

    get name() {
        return this.name.toUpperCase();
    }

    set age(age) {
        if (age > 0 && age < 120) {
            this.age = age;
        } else {
            console.log("Invalid age value.");
        }
    }

    get age() {
        return this.age;
    }
}

在类的实现中,getter 和 setter 其实是被定义在构造函数的 prototype 属性上,从而可以被该类的所有实例对象访问。

9、生成器 generator

生成器为了解决什么问题?

解决了函数的不可中断性

生成器是可中断函数,yield 关键字暂时中断函数执行,在合适的实际从中断位置继续执行。

function* generateSequence() {
  yield 1;
  yield 2;
  yield 3;
}

const sequence = generateSequence(); // 获取生成器实例
console.log(sequence.next().value); // 输出 1
console.log(sequence.next().value); // 输出 2
console.log(sequence.next().value); // 输出 3

10、异步处理——callback、Promise、async & await

这个部分可以看博客:3.1、前端异步编程(超详细手写实现Promise;实现all、race、allSettled、any;async/await的使用)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值