ES6 笔记

var、let与const

首先是 var 和 let 的区别。

let 声明的变量只在 let 命令所在的代码块内有效。

var 声明的变量式全局变量,在全局范围内有效。

const 声明一个只读的常量,一旦声明,常量的值就不能改变。

let 是在代码块内有效,var 是在全局范围内有效。

let 只能被声明一次,var 可以声明多次。

for 循环计数适合用 let。

let 不存在变量提升,var会变量提升。

let 和 const 会存在暂时性死区问题:也就是在声明之前赋值或者其他操作,会出现问题(TDZ)。

const 声明之后必须初始化,否则会报错。

字符串

字符的 Unicode 表示法:\u{20BB7}

字符串的遍历接口:for...of (优点是可以识别大于0xFFFF的码点)。

模板字符串:使用 ` (反引号)包裹,可以在字符串中嵌入变量。

一些方法:

  • String.fromCharCode()方法,用于从 Unicode 码点返回对应字符
  • String.raw()方法,ES6 还为原生的 String 对象,提供了一个raw()方法。该方法返回一个斜杠都被转义(即斜杠前面再加一个斜杠)的字符串,往往用于模板字符串的处理方法。

...

模板字符串

解决了传统的获取dom进行添加标签定义过于冗余。

我们可以用 TAB 上面的反引号定义字符串,对于变量可以用 ${} 的形式添加。

函数

函数默认值、剩余参数

ES5中:

function add(a, b) {
    a = a || 10;
    b = b || 20;
    return a + b;
}
console.log(add());

ES6中:

function add(a, b = 20) {
    return a + b;
}
console.log(add(30));

默认的表达式也可以是一个函数

function add(a, b = getVal(5)) {
	return a + b;
}

function getVal(val) {
    return val + 5;
}
console.log(add(10));

剩余参数

ES5 利用 arguments 获取Obj内部元素:

function pick(obj) {
    let res = Object.create(null);
    for(let i = 1; i < arguments.length; i ++) {
        res[arguments[i]] = obj[arguments[i]];
    }
    return res;
}
let book = {
    title: 'ES6',
    author: 'XiaoMo',
    year: 2019
}
let bookData = pick(book, 'title', 'year');
console.log(bookData);

ES6 剩余参数:由三个点...和紧跟着的具名参数指定 ...keys

(剩余参数的数组是真数组,而arguments是伪数组)

function pick(obj, ...keys) {
    let res = Object.create(null);
    for (let i = 0; i < keys.length; i++) {
        res[keys[i]] = obj[keys[i]];
    }
    return res;
}
let book = {
    title: 'ES6',
    author: 'XiaoMo',
    year: 2019
}
let bookData = pick(book, 'title', 'year');
console.log(bookData);

函数之扩展运算符、箭头函数

剩余运算符:把多个独立的合并到一个数组中。

扩展运算符:将一个数组分割,并将各个项作为分离的参数传给函数。

const arr = [10, 20, 50, 30, 90, 100, 40];
console.log(Math.max(...arr));

箭头函数

使用 => 来定义

function(){} 等价于 ()=>{}

let add = function(a, b) {
    return a + b;
}

let add = (a, b) => {
	return a + b;
}

console.log(add(10, 20));

闭包+箭头函数+立即调用

let fn = (() => {
    return () => {
        console.log("Hello World!")
    }
})();
fn();

箭头函数this指向和注意事项

ES6中使用箭头函数没有this绑定。

ES5中this指向,取决于该函数的上下文对象。

以下代码会出现问题:

let PageHandle = {
    id: 123,
    init: function() {
        document.addEventListener('click', function(event) {
            // this.doSomeThings is not a function
            this.doSomeThings(event.type);
        })
    },
    doSomeThings:function(type) {
        console.log(`事件类型:${type}, 当前ID:${this.id}`)
    }
}
console.log(PageHandle);
PageHandle.init();

ES5的解决办法:

let PageHandle = {
    id: 123,
    init: function() {
        document.addEventListener('click', function(event) {
            // this.doSomeThings is not a function
            this.doSomeThings(event.type);
        }.bind(this), false)
    },
    doSomeThings:function(type) {
        console.log(`事件类型:${type}, 当前ID:${this.id}`)
    }
}
PageHandle.init();

ES6的解决方法:

let PageHandle = {
    id: 123,
    init: function() {
        // 箭头函数没有 this 指向,箭头函数内部 this 值只能通过查找作用域链来获取 this。
        document.addEventListener('click', (event) => {
            this.doSomeThings(event.type);
        }, false)
    },
    doSomeThings:function(type) {
        console.log(`事件类型:${type}, 当前ID:${this.id}`)
    }
}
PageHandle.init();

一旦使用箭头函数,那么当前函数内部就没有作用域了。

  1. 箭头函数中不会存在 arguments。
  2. 箭头函数不能使用 new 来实例化对象(function函数是一个对象,但是箭头函数不是对象)。

ES6 解构赋值

解构赋值是对复制运算符的一种扩展。

它正对数组和对象来进行操作。

优点:代码书写上简洁易读。

let node = {
	type: 'iden',
    name: 'foo',
}

let {type, name} = node;
console.log(type, name);

let obj = {
	a: {
		name: 'zhangsan'
    },
    b:[],
    c:'hello, world'
}
// 不完全解构
let {a} = obj;
// 可以使用剩余运算符
let {a, ...res} = obj;
console.log(res);

// 默认值
let {a, b = 30} = {a: 20};

对象的扩展功能

ES6直接写入变量和函数作为对象的属性和方法。

对于对象的名与值相同的,可以省略掉。

const person = {
    name,
    age,
    sayName() {
        console.log(this.name);
    }
}
person.sayName();

对象的方法

1.is() 类似于 ==== 比较两个值是否严格相等。

console.log(NaN === NaN); 比较失效。

console.log(Object.is(NaN, NaN)); 比较成功。

2.assign()

对象的合并。

Object.assign(target, obj1, obj2);

将obj1, obj2 合并进 target。

ES6 Symbol

最大用处:用来定义对象的私有变量。

使用场景:

  • 作为属性名
let sy = Symbol("key1");
 
// 写法1
let syObject = {};
syObject[sy] = "kk";
console.log(syObject);    // {Symbol(key1): "kk"}
 
// 写法2
let syObject = {
  [sy]: "kk"
};
console.log(syObject);    // {Symbol(key1): "kk"}
 
// 写法3
let syObject = {};
Object.defineProperty(syObject, sy, {value: "kk"});
console.log(syObject);   // {Symbol(key1): "kk"}

Symbol 作为对象属性名时不能用.运算符,要用方括号。因为.运算符后面是字符串,所以取到的是字符串 sy 属性,而不是 Symbol 值 sy 属性。

Symbol 值作为属性名时,该属性是公有属性不是私有属性,可以在类的外部访问。但是不会出现在 for...in 、 for...of 的循环中,也不会被 Object.keys() 、 Object.getOwnPropertyNames() 返回。如果要读取到一个对象的 Symbol 属性,可以通过 Object.getOwnPropertySymbols() 和 Reflect.ownKeys() 取到。

let syObject = {};
syObject[sy] = "kk";
console.log(syObject);
 
for (let i in syObject) {
  console.log(i);
}    // 无输出
 
Object.keys(syObject);                     // []
Object.getOwnPropertySymbols(syObject);    // [Symbol(key1)]
Reflect.ownKeys(syObject);                 // [Symbol(key1)]

Map 与 Set

Map 与 Object 的区别

  • 一个 Object 的键只能是字符串或者 Symbols,但一个 Map 的键可以是任意值。
  • Map 中的键值是有序的(FIFO原则),而添加到 Object 的值不是。
  • Map 的键值对个数可以从 size 属性获取,但是 Object 只能手动计算。
  • Object 都有自己的原型,原型链上的键名有可能和你自己在对象上的设置的键名的冲突。

Map 对象

Map类型是键值对的有序列表、键和值是任意类型。

let map = new Map();
map.set('name', '张三');
map.set('age', 20);
map.has('name');
map.clear();

Map的迭代

for...of

forEach()

Set 对象

Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。

创建一个空的集合:

let set = new Set();

添加元素:

set.add(1);// 添加值可以是任意类型

删除元素:

set.delete(1);

校验某个值是否在 set 集合中:

set.has(1);

set中的forEach没有意义,因为key和value相同。

将set转化成数组:

let set2 = new Set([1, 2, 3, 3, 3, 4]);
let arr = [...set2];
console.log(arr);

set 中对象的应用无法被释放。

let set3 = new Set(), obj = {};

set3.add(obj);

WeakSet 解决的这一问题,但是丢失了很多Set的属性。

  1. 不能传入非对象类型的参数。
  2. 不可迭代。
  3. 不能forEach。
  4. 没有size属性。

数组的扩展方法

from() 将为数组转化为真正的数组。

function add() {
    let arr = Array.from(arguments);
    console.log(arr);
}
add(1, 2, 3);

也可以使用[...arguments],更加方便。

from() 还可以接受第二个参数,用来对每个元素进行处理(回调函数)。

let lisarr = Array.from(lis, item => item.textContent);
console.log(lisarr);

of() 将一组值,转化为数组。

console.log(Array.of(3, 11, 20, [1, 2, 3], {id: 1}));

copyWithin() 数组内部将指定位置的元素复制到其他位置。

let arr = [1, 2, 3, 4, 8, 9, 10].copyWithin(0, 3);
console.log(arr);

find()、findIndex()查找到第一个符合条件的值。

接收一个回调函数。

let num = [1, 2, -10, -20, 9, 2].find(n => n < 0);
console.log(num);

entries() keys() values() 返回一个遍历器。

for (let index of ['a', 'b'].keys()) {
	console.log(index);
}

entries() 包含了 keys()values()

for (let [index, ele] of ['a', 'b'].entries()) {
	console.log(inedx, ele);
}

includes() 返回一个布尔值,表示某个数组中是否包含给定的值。

console.log([1, 2, 3].includes(2));

之前使用的是 indexof()

迭代器

Iterator 是一种新的遍历机制。

有两个核心:

  1. 迭代器是一个接口,能快捷的访问数据。通过Symbol.iterator来创建迭代器,通过迭代器的next()方法,获取迭代后的结果。
  2. 迭代器适用于遍历数据结构的指针(数据库的游标)。
const items = ['one', 'two', 'three'];
const it = items[Symbol.iterator]();
console.log(it.next());//{value: "one", done: false} done 如果为 false则还有值,为true则表示遍历到最后了。
console.log(it.next());
console.log(it.next());
console.log(it.next());

生成器 Generator

与迭代器紧密相关。

generator 函数,可以通过yield关键字,将函数挂起,为了改变执行流程提供了可能性,同时为了做异步编程,也提供了方案。

他与普通函数的区别:

1.function后面,函数名之前有个 *

2.只能在函数内部使用 yield,让函数挂起。

function* func() {
    console.log('start');
    yield 2;
    console.log('second');
    yield 3;
    console.log('end');
}
let fn = func();
// console.log(fn);
console.log(fn.next());
console.log(fn.next());
console.log(fn.next());

总结:generator 函数是分段执行的,yield 语句是暂停执行,而 next() 恢复执行。

function* add() {
    console.log('start');
    let x = yield '2';
    console.log('one:' + x);
    let y = yield '3';
    console.log('two:' + y);
    return x + y;
}
const fn = add();
console.log(fn.next());  // {value: '2', done: false}
console.log(fn.next(20));// {value: '3', done: false}
console.log(fn.next(30));// {value: 50, done: true}

使用场景:为不具备 Interator 接口的对象提供了遍历操作

function* objectEntries(obj) {
    // 获取对象的所有 key 保存到数组 [name, age]
    const propKeys = Object.keys(obj);
    for (const propkey of propKeys) {
        yield [propkey, obj[propkey]]
    }
}
const obj = {
    name: 'XiaoMo',
    age: 18,
}
obj[Symbol.iterator] = objectEntries;
console.log(obj);

for(let [key, value] of objectEntries(obj)) {
    console.log(`${key}:${value}`);
}

Generator 在异步编程中的应用

发送 ajax 请求,可能会陷入到 ”回调地狱“中。

使用 Generator 同步化。

function* main() {
	let res = yield request('url')
    console.log(res);
    console.log('数据请求完成, 可以继续操作')
}

const it = main();
it.next();

function reauest(url) {
	$.ajax({
		url,
        method:'get',
        sucess(res) {
            it.next(res);
        }
    })
}

加载Loading...页面、数据加载完成...(异步操作)、Loading关闭掉。

function* load() {
    loadUI();
    yield showData();
    hideUI();
}

let itLoad = load();
itLoad.next();

function loadUI() {
    console.log('加载loading...页面');
}

function showData() {
    setTimeout(() => {
        console.log('数据加载完成');
        itLoad.next();
    }, 1000);
}

function hideUI() {
    console.log('隐藏loading...页面');
}

Proxy && Reflect

Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。

Reflect对象与Proxy对象一样,也是 ES6 为了操作对象而提供的新 API。Reflect对象的设计目的有这样几个。

(1) 将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。现阶段,某些方法同时在ObjectReflect对象上部署,未来的新方法将只部署在Reflect对象上。也就是说,从Reflect对象上可以拿到语言内部的方法。

(2) 修改某些Object方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false

Promise

异步操作

相当于一个容器,保存着未来才会结束的事件(异步操作)的一个结果。

各种异步操作都可以用同样的方法进行处理 axios。

特点:

  1. 对象的状态不受外界影响、处理异步操作,三个状态:Pading(进行)、Resolved(成功)、Rejected(失败)。
  2. 一旦状态改变,就不会再变,任何时候都可以得到这个结果。
let pro = new Promise(function(resolved, rejected) {
    // 执行异步操作
    let res = {
        code: 201,
        data: {
            name: 'XiaoMo'
        },
        error: 'GG'
    }
    setTimeout(() => {
        if(res.code === 200) {
            resolved(res.data);
        } else {
            rejected(res.error);
        }
    }, 1000);
})
console.log(pro);
pro.then((val) => {
    console.log(val);
}, (err) => {
    console.log(err);
})

.then => 捕获(padding => resolved)

.catch => 捕获(padding => rejected)

function timeOut(ms) {
    return new Promise ((resolved, rejected) => {
        setTimeout(() => {
            resolved('Hello promise success!!')
        }, ms);
    })
}
timeOut(2000).then((val) => {
    console.log(val);
})

Promise封装ajax

const getJOSN = function(url) {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open('GET', url);
        xhr.onreadystatechange = handler;
        xhr.responseType = 'json';
        xhr.setRequestHeader('Accept', 'application/json');
        xhr.send();

        function handler() {
            console.log(this);
        }

    })
}
getJOSN('https://free-api.heweather.net/s6/weather/now').then((data) => {
    console.log(data);
},(error) => {
    console.log(error);
})

then() 方法:
then() 第一个参数是reslove回调函数,第二个是可选的为 reject 状态的回调函数。

可以使用:

getJSON().then((data) => {
    console.log(data);
}).then((null, error) => {
    console.log(error);
})

resolve()

let p = Promise.resolve('foo');
p.then((data) => {
	console.log(data);
})

all()

let promise1 = new Promise((resolve, reject) => {});
let promise2 = new Promise((resolve, reject) => {});
let promise3 = new Promise((resolve, reject) => {});

let p4 = Promise.all([promise1, promise2, promise3]);

p4.then(() => {
    // 三个都成功才会执行。
    
}).catch(err => {
    // 如果有一个失败 则失败。
})

race() 某个异步请求设置超时事件,并且在超时后执行响应的操作。

function requestImg(imgSrc) {
    return new Promise((resolve, reject) => {
        const img = new Image();
        img.onload = function() {
            resolve(img);
        }
        img.src = imgSrc;
    });
}
function timeout() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            reject('图片请求超时');
        }, 3000);
    })
}
Promise.race([requestImg('https://pic.616pic.com/ys_img/00/25/66/ydJbBZaUsR.jpg'), timeout()]).then(data => {
    console.log(data);
    document.body.appendChild(data);
}).catch(err => {
    console.log(err);
})

done() finally() 不管是 resolve 还是 reject 都会执行。

可以用来关闭弹窗。

async 异步操作

作用:使得异步操作更加方便。

基本操作 async 它会返回一个 Promise 对象 then、catch。

async 是 Generator 的一个语法糖。

async function f() {
    let s = await 'hello world';
    let data = await s.split('');
    return data;
}
f().then(v => {
    console.log(v);
}).catch(e => {
    console.log(e);
})

await 命令一定在 async 函数里面使用。

如果 async 函数中有多个 await 那么 then 函数会等待所有的 await 指令运行完的结果才去执行。

async function f2() {
    try{
        await Promise.reject('出错了');
    } catch (error) {
        
    }
    return await Promise.resolve('hello');
}
f2().then(v => {
    console.log(v);
}).catch(e => {
    console.log(e);
})

Class 类的用法

class Person {
    // 实例化的时候会立即被调用的方法
	constructor(name, age) {
		this.name = name;
        this.age = age;
    }
    sayName() {
        return this.name;
    }
}
let p1 = new Person('XiaoMo', 18);
console.log(p1.sayName())
// 通过 Object.assign() 方法一次性向类中添加多种方法
Object.assign(Person.prototype, {
	sayName() {
        return this.name,
    }
})

类的继承

使用关键字 extends

class Animal {
	constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    sayName() {
        return this.name;
    }
    sayAge() {
        return this.age;
    }
}

class Dog extends Animal {
    constructor(name, age, color) {
        super(name, age);
        this.color = color;
    }
}

ES6 的模块化实现

ES6 模块功能主要有两个命令构成:export 和 import。

export 用于规定模块的对外接口 import 用于输入其他模块提供的接口。

index.js:

export const name = 'XiaoMo';
export const age = 18;
export function sayName() {
    return 'my Name is XiaoMo';
}

对于一次性抛出:

cosnt name = 'XiaoMo';
...
export {
    name, age, sayName
}

对于全部导入:

import * as f from './index.js'
posted @ 2025-05-10 22:46  XiaoMo247  阅读(33)  评论(0)    收藏  举报