ES6知识点

ES6

Babel 转码器:可以将 ES6 代码转为 ES5 代码,从而在老版本的浏览器执行。这意味着,可以用 ES6 的方式编写程序,又不用担心现有环境是否支持。

一、let 与 const

  • var定义的变量,没有块的概念,可以跨块访问, 不能跨函数访问。
  • let定义的变量,只在let命令所在的代码块内有效即只能在块作用域里访问,不能跨块访问,也不能跨函数访问。
  • const用来定义常量,使用时必须初始化(即必须赋值),只能在块作用域里访问,而且不能修改。(存放的是地址,可以当作指针使用),但允许在不重新赋值的情况下修改它的值
    • 重复声明:已经存在的变量或常量,又声明了一遍,var 允许重复声明,let、const 不允许
    • 变量提升:var 会提升变量的声明到当前作用域的顶部,let、const 不存在变量提升
    • 只要作用域内存在 let、const,它们所声明的变量或常量就自动“绑定”这个区域,不再受到外部作用域的影响,let、const 存在暂时性死区,var不存在
    • window 对象的属性和方法:全局作用域中,var 声明的变量,通过 function 声明的函数,会自动变成 window 对象的属性或方法,let、const 不会
    • 块级作用域:var没有,let和const有,作用域链:内层作用域->外层作用域->…->全局作用域
<script type="text/javascript">
    // 块作用域
    {
        var a = 1;
        let b = 2;
        const c = 3;
        // c = 4; // 报错
        var aa;
        let bb;
        // const cc; // 报错
        console.log(a); // 1
        console.log(b); // 2
        console.log(c); // 3
        console.log(aa); // undefined
        console.log(bb); // undefined
    }
    console.log(a); // 1
    // console.log(b); // 报错
    // console.log(c); // 报错
 
    // 函数作用域
    (function A() {
        var d = 5;
        let e = 6;
        const f = 7;
        console.log(d); // 5
        console.log(e); // 6  
        console.log(f); // 7 
 
    })();
    // console.log(d); // 报错
    // console.log(e); // 报错
    // console.log(f); // 报错
</script>

for循环的计数器,就很合适使用let命令。

var a = [];
for (var i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 10
变量i是var命令声明的,在全局范围内都有效,所以全局只有一个变量i。每一次循环,变量i的值都会发生改变,而循环内被赋给数组a的函数内部的console.log(i),里面的i指向的就是全局的i。也就是说,所有数组a的成员里面的i,指向的都是同一个i,导致运行时输出的是最后一轮的i的值,也就是 10

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

const实际上保证的并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。

const foo = {};
// 为 foo 添加一个属性,可以成功
foo.prop = 123;
foo.prop // 123
// 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError: "foo" is read-only

ES6 声明变量的六种方法:var命令和function命令、let命令和const命令、import命令和class命令

顶层对象,在浏览器环境指的是window对象,在 Node 指的是`global对象

总结:

以后声明变量使用 let 就对了

声明对象类型使用 const,非对象类型声明选择 let

当遇到字符串与变量拼接的情况使用模板字符串(``)

二、⭐解构赋值

从数组和对象中提取值,对变量进行赋值,这被称为解构

1、嵌套数组进行解构

属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值

let [foo, [[bar], baz]] = [1, [[2], 3]];
foo // 1
bar // 2
baz // 3

let [ , , third] = ["foo", "bar", "baz"];
third // "baz"

let [x, , y] = [1, 2, 3];
x // 1
y // 3

let [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4]

let [x, y, ...z] = ['a'];
x // "a"
y // undefined
z // []
如果解构不成功,变量的值就等于undefined

2、不完全解构

即等号左边的模式,只匹配一部分的等号右边的数组。这种情况下,解构依然可以成功。

(1)数组

对于Set 结构,也可以使用数组的解构赋值。

let [x, y, z] = new Set(['a', 'b', 'c']);

x // "a"

事实上,只要某种数据结构具有Iterator接口,都可以采用数组形式的解构赋值。

let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'

let [x = 1] = [undefined]; x // 1

let [x = 1] = [null]; x // null

如果默认值是一个表达式,那么这个表达式是惰性求值的,即只有在用到的时候,才会求值。

function f() {
  console.log('aaa');
}
let [x = f()] = [1];
因为x能取到值,所以函数f根本不会执行

默认值可以引用解构赋值的其他变量,但该变量必须已经声明。

let [x = 1, y = x] = [];     // x=1; y=1
let [x = 1, y = x] = [2];    // x=2; y=2
let [x = 1, y = x] = [1, 2]; // x=1; y=2
let [x = y, y = 1] = [];     // ReferenceError: y is not defined
报错原因:x用y做默认值时,y还没有声明

(2)对象:对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量,没有次序

// 例一
let { log, sin, cos } = Math;
将Math对象的对数、正弦、余弦三个方法,赋值到对应的变量上

// 例二
const { log } = console;
log('hello') // hello

变量名与属性名不一致
let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa"

// 错误的写法

let x;

{x} = {x: 1}; // SyntaxError: syntax error

// 正确的写法

let x;

({x} = {x: 1});

3、字符串

字符串被转换成了一个类似数组的对象

const [a, b, c, d, e] = 'hello';

["h","e","l","l","o"]

4、数值和布尔值

let {toString: s} = 123;

s === Number.prototype.toString // true

let {toString: s} = true;

s === Boolean.prototype.toString // true

5、函数参数

[[1, 2], [3, 4]].map(([a, b]) => a + b); // [ 3, 7 ]

(1)函数move的参数是一个对象,通过对这个对象进行解构,得到变量x和y的值
function move({x = 0, y = 0} = {}) {
    return [x, y];
}
move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, 0]
move({}); // [0, 0]
move(); // [0, 0]

(2)为函数move的参数指定默认值,而不是为变量x和y指定默认值
function move({x, y} = { x: 0, y: 0 }) {
    return [x, y];
}
move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, undefined]
move({}); // [undefined, undefined]
move(); // [0, 0]

三、箭头函数

let fn = (arg1, arg2, arg3) => {
 return arg1 + arg2 + arg3;
}

箭头函数的注意点:

  • 如果形参只有一个,则小括号可以省略
  • 函数体如果只有一条语句,则花括号可以省略,函数的返回值为该条语句的 执行结果
  • 箭头函数 this 指向声明时所在作用域下 this 的值(箭头函数不会更改 this 指向,用来指定回调函数会非常合适)
  • 箭头函数不能作为构造函数实例化
  • 不能使用 arguments

四、rest 参数

ES6 引入 rest 参数,用于获取函数的实参,用来代替 argument

function add(a,b,…args){ console.log(args); } add(1,2,3,4,5);

rest 参数必须是最后一个形参

rest 参数非常适合不定个数参数函数的场景

五、spread 扩展运算符

好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列,对数组进行解包。

let tfboys = ['德玛西亚之力','德玛西亚之翼','德玛西亚皇子'];
function fn(){
    console.log(arguments);
}
fn(...tfboys)

//展开对象
let skillOne = {
    q: '致命打击',
};
let skillTwo = {
    w: '勇气'
};
let skillThree = {
    e: '审判'
};
let skillFour = {
    r: '德玛西亚正义'
};
let gailun = {...skillOne, ...skillTwo,...skillThree,...skillFour};

六、Symbol

Symbol 特点 :

  1. Symbol 的值是唯一的,用来解决命名冲突的问题
  2. Symbol 值不能与其他数据进行运算
  3. Symbol 定义的对象属性不能 使 用 for…in 循 环遍 历 ,但 是可 以 使 用 Reflect.ownKeys 来获取对象的所有键名
//创建 Symbol
let s1 = Symbol();
console.log(s1, typeof s1);  // Symbol() ,'symbol'

//添加标识的 Symbol
let s2 = Symbol('me');
let s2_2 = Symbol('me');
console.log(s2 === s2_2);   // false

//使用 Symbol for 定义
let s3 = Symbol.for('me');
let s3_2 = Symbol.for('me');
console.log(s3 === s3_2);   // true

注: 遇到唯一性的场景时要想到 Symbol

Symbol 内置值

  • Symbol.hasInstance 当其他对象使用
  • instanceof 运算符,判断是否为该对 象的实例时,会调用这个方法
  • Symbol.isConcatSpreadable 对象的 Symbol.isConcatSpreadable 属性等于的是一个 布尔值,表示该对象用于 Array.prototype.concat()时, 是否可以展开。
  • Symbol.species 创建衍生对象时,会使用该属性 Symbol.match 当执行 str.match(myObject) 时,如果该属性存在,会 调用它,返回该方法的返回值。
  • Symbol.replace 当该对象被 str.replace(myObject)方法调用时,会返回 该方法的返回值。
  • Symbol.search 当该对象被 str.search (myObject)方法调用时,会返回 该方法的返回值。
  • Symbol.split 当该对象被 str.split(myObject)方法调用时,会返回该 方法的返回值。
  • Symbol.iterator 对象进行 for…of 循环时,会调用 Symbol.iterator 方法, 返回该对象的默认遍历器 Symbol.toPrimitive 该对象被转为原始类型的值时,会调用这个方法,返 回该对象对应的原始类型值。
  • Symbol. toStringTag 在该对象上面调用 toString 方法时,返回该方法的返回值
  • Symbol. unscopables 该对象指定了使用 with 关键字时,哪些属性会被 with 环境排除。

七、迭代器

遍历器(Iterator)就是一种机制。它是一种接口,为各种不同的数据结构提 供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作.

原生具备 iterator 接口的数据(可用 for of 遍历) :Array 、Arguments 、 Set 、 Map 、 String 、 TypedArray 、NodeList

工作原理:

  • 创建一个指针对象,指向当前数据结构的起始位置
  • 第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员
  • 接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员
  • 每调用 next 方法返回一个包含 value 和 done 属性的对象

注: 需要自定义遍历数据的时候,要想到迭代器。

八、生成器

生成器函数是一种异步编程解决方案

function * gen(){
     yield '一只没有耳朵';
     yield '一只没有尾巴';
     return '真奇怪';
}
let iterator = gen();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next())

代码说明:

  • *的位置没有限制
  • 生成器函数返回的结果是迭代器对象,调用迭代器对象的 next 方法可以得到 yield 语句后的值
  • yield 相当于函数的暂停标记,也可以认为是函数的分隔符,每调用一次 next 方法,执行一段代码
  • next 方法可以传递实参,作为 yield 语句的返回值

九、Promise

Promise 是 ES6 引入的异步编程的新解决方案。

  • 1.js的执行顺序,先同步后异步
  • 2.异步中任务队列的执行顺序: 先微任务microtask队列,再宏任务macrotask队列
  • 3.调用Promise 中的resolve,reject属于微任务队列,setTimeout属于宏任务队列

注意以上都是 队列,先进先出。

微任务包括 process.nextTickpromiseMutationObserver

宏任务包括 scriptsetTimeoutsetIntervalsetImmediateI/OUI rendering

语法上 Promise 是一个构造函数, 用来封装异步操作并可以获取其成功或失败的结果。

  1. Promise 构造函数: Promise (excutor) {}
  2. Promise.prototype.then 方法
  3. Promise.prototype.catch 方法
  4. Promise.prototype.finally方法

从语法上说,Promise是一个对象,从它可以获取异步操作的消息,可以解决回调地狱(回调地狱嵌套回调函数)
Promise的含义:本身不是异步,是封装异步操作容器,统一异步的标准
Promise对象的
特点
:对象的状态不受外界影响,一旦状态改变,就不会再变,任何时候都可以得到这个结果

js中的异步操作

ajax请求  
var xhr = new XMLHttpRequest();
xhr.onreadystatechange=function(){}
xhr.addEventListener('readystatechange',function(){})
浏览器事件
ele.onclick=function(){}
定时
setTime(function(){
},1000);

对原生ajax封装

function aa(method='get',path='1.php'){
   return new Promise((f1,f2)=>{
       let request = new XMLHttpRequest();
       request.open(method,path);
       request.send();
       request.onreadystatechange=()=>{
           if(request.readyState ==4){
               if(request.status ==200){
                   f1.call(null,request.responseText)
               }else{
                   f2.call(undefined,request.status)
               }
           }
       }
   })    
}
aa('get','1.php').then(function(da){
     console.log(da)
}).then(function(da){
     aa('get','2.php').then(function(da){
         console.log(da)
     })
})
fetch("https://jsonplaceholder.typicode.com/post/1")
    .then((res) => res.json())
    .then((json) => {
        console.log(json);
    })
    ...
    .catch((err) => {
        // 之前任意一个阶段发生错误都会触发catch,之后的then()将不会执行
        console.log(err);
    })
    .finally(() => {
        // 执行清理操作
        stopLoadingAnimation();
    })

使用async和await

async function f(){
    const response = await fetch("http://...")
    const json = await response.json();
    console.log(json)
}
f();


async function f(){
    // const a = await fetch("http://...")
    // const b = await fetch("http://...")
    // 打破fetch()操作的并行
    
    // 修改后
    const promiseA = fetch("http://.../post/1");
    const promiseB = fetch("http://.../post/2");
    
    const[a,b] = await Promise.all([promiseA,promiseB])
}


//使用循环时要用for循环,不采用forEach
async function f(){
    const promises = [
        someAsyncOperation(),
        someAsyncOperation(),
        someAsyncOperation(),
    ];
    for await (let result of promise){
        // ...
    }
    console.log("done");
}
f();

十、Set和Map

1、Set

数据结构 Set(集合)类似于数组,但成员的值都是唯 一的,集合实现了 iterator 接口,所以可以使用『扩展运算符』和『for…of…』进 行遍历。

集合的属性和方法:

  • size 返回集合的元素个数
  • add 增加一个新元素,返回当前集合
  • delete 删除元素,返回 boolean 值
  • has 检测集合中是否包含某个元素,返回 boolean 值
  • clear 清空集合,返回 undefined

2、Map

Map 数据结构类似于对象,也是键值对的集合。但是“键” 的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Map 也实现了 iterator 接口,所以可以使用『扩展运算符』和『for…of…』进行遍历。

Map 的属 性和方法:

  • size 返回 Map 的元素个数
  • set 增加一个新元素,返回当前 Map
  • get 返回键名对象的键值
  • has 检测 Map 中是否包含某个元素,返回 boolean 值
  • clear 清空集合,返回 undefined

十一、class 类

通过 class 关键字,可以定义类。

//父类
class Phone {
    //构造方法
    constructor(brand, color, price) {
        this.brand = brand;
        this.color = color;
        this.price = price;
    }
 //对象方法
    call() {
        console.log('我可以打电话!!!')
    }
}

//子类
class SmartPhone extends Phone {
     constructor(brand, color, price, screen, pixel) {
         super(brand, color, price);
         this.screen = screen;
         this.pixel = pixel;
     }
    //子类方法
    photo(){
        console.log('我可以拍照!!');
    }
    playGame(){
        console.log('我可以玩游戏!!');
    }
    //方法重写
    call(){
        console.log('我可以进行视频通话!!');
    }
    //静态方法
    static run(){
        console.log('我可以运行程序')
    }
    static connect(){
        console.log('我可以建立连接')
    }
}

//实例化对象
const Nokia = new Phone('诺基亚', '灰色', 230);
const iPhone6s = new SmartPhone('苹果', '白色', 6088, '4.7inch','500w');
//调用子类方法
iPhone6s.playGame();
//调用重写方法
iPhone6s.call();
//调用静态方法
SmartPhone.run();

十二、模块化

ES6 模块化语法 模块功能主要由两个命令构成:export 和 import。

  • export 命令用于规定模块的对外接口
  • import 命令用于输入其他模块提供的功能

优势:1) 防止命名冲突 2) 代码复用 3) 高维护性

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值