ECMA Script 6学习手记

ES6 基础用例

变量声明

let命令
{
    let a = 10; //let命令所在的代码块内有效
    var b = 1;
}
    console.log(b); // 1
    console.log(a); // 报错a没有定义
const命令
//const声明一个只读的常量。一旦声明,常量的值就不能改变,且声明时必须立即初始化,不能留到以后赋值。
 const a = 10;
 a = 9;
 console.log(a); //报错

解构

数组的解构赋值
let [a,b,c] = [0,1,2]; //按照位置解构
//等价于
let a = 0;
let b = 1;
let c = 2;

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

let [foo=true] = []; //给予默认值
console.log(foo) //true
let [foot='string'] = [];
console.log(foot) //string
let [a,b='jsfan'] = ['blog'];
console.log(a+b) //blogjsfan

function sum(a, b = 1, c = 2) {
    return a + b + c; }
console.log(sum(10, undefined, undefined))//13
console.log(sum(11))//14
console.log(sum(1, undefined, 5))//7
对象的解构赋值
let {foo,bar} = {foo:'JSfan',bar:'博客'} //按照对象名解构
console.log(foo+bar)//JSfan博客

const user = {
    name: "kevin",
    age: 11,
    sex: "男",
    address: {
        province: "四川",
        city: "成都"
    }
}
const { name, ...obj } = user;
console.log(name, obj)//kevin { age: 11, sex: '男', address: { province: '四川', city: '成都' } }

//圆括号的使用:
//如果在解构之前就定义了变量,这时候你再解构会出现问题。
let foo;
{foo} ={foo:'JSfan'};
console.log(foo); //报错
//解决方法
let foo;
({foo} ={foo:'JSfan'});
console.log(foo); //控制台输出jsfan
字符串解构
const [a,b,c,d,e]="JSfan";
console.log(a); //J
console.log(b); //S
console.log(c); //f
console.log(d); //a
console.log(e); //n
对象的函数解构
let a = {
    a:'jsfan',
    b:'blog'
}
function fun({a,b='nice'}){
    console.log(a,b);//jsfan blog
}
fun(a);
数组的函数解构
let arr = ['jsfan','blog','music'];
function fun(a,b,c){
    console.log(a,b,c);//jsfan blog music
}
fun(...arr);

function sum(...args) {
let sum = 0;
for (let i = 0; i < args.length; i++) {//累加
    sum += args[i];
}
return sum;
}
numbers=[1,2,3]//将数组的每一项展开,依次作为参数传递,而不是把整个数组作为一个参数传递
console.log(sum(...numbers))//相当于传递了3个参数 6



运算符

扩展运算符
function jsfan(...arg){
    console.log(arg[0]);//1
    console.log(arg[1]);//2
    console.log(arg[2]);//3
}
jsfan(1,2,3)

//此函数问题(更改arr2,却导致arr1也发生变化)
let arr1=['www','jsfan','net'];
let arr2=arr1;
console.log(arr2); //["www", "jsfan", "net"]
arr2.push('bug');
console.log(arr1); //["www", "jsfan", "net", "bug"]
//用扩展运算符解决
let arr1=['www','jsfan','net'];
let arr2=[...arr1];
console.log(arr2);// ["www", "jsfan", "net"]
arr2.push('bug');
console.log(arr2);//["www", "jsfan", "net", "bug"]
console.log(arr1);//["www", "jsfan", "net"]
rest运算符
  • rest参数和arguments对象的区别

    • rest参数只包括没有给出名称的参数,arguments包含所有参数

    • arguments对象不是真正的数组,而rest参数是数组实例,可直接使用sort、map、forEach、pop等方法

    • arguments对象拥有自己额外的功能

function jsfan(first,...arg){
    console.log(arg.length);//7(此时first为0,arg为1-7的数组)
}
jsfan(0,1,2,3,4,5,6,7);

字符串

字符串模版
let jsfan='Youngster_yj';
let blog = `欢迎来到${jspfan}的博客。这是字符串模版。`    
document.write(blog);
includes\startsWith\endsWith 字符串查找
let jsfan='jsfan';
let blog = '欢迎来到jsfan的博客。';
console.log(blog.includes(jsfan));//true 存在
console.log(blog.startsWith(jsfan))//false 开头是否存在
console.log(blog.endsWith(jsfan))//false 结尾是否存在
repeat复制字符串
console.log('jsfan'.repeat(3))//jsfanjsfanjsfan
[扩展]模板字符串标记
//在模板字符串书写之前,可以加上标记:
//标记名`模板字符串`
//标记是一个函数,函数参数如下:
//1. 参数1:被插值分割的字符串数组
//2. 后续参数:所有的插值

var love1 = "秋葵";
var love2 = "香菜";
var text = myTag`YJ喜欢${love1},YJ也喜欢${love2}。`;
//相当于: 
//text = myTag(["YJ喜欢", ",YJ也喜欢", "。"], "秋葵", "香菜")
function myTag(parts) {
    const values = Array.prototype.slice.apply(arguments).slice(1);
    let str = "";
    for (let i = 0; i < values.length; i++) {
        str += `${parts[i]}${values[i]}`;
        if (i === values.length - 1) {
            str += parts[i + 1];
        }
    }
    return str;
}
console.log(text);//YJ喜欢:秋葵,YJ也喜欢:香菜。

Number中的方法

数字验证Number.isFinite( )
//只要是数字,不论是浮点型还是整形都会返回true,其他时候会返回false
let a= 13/4;
console.log(Number.isFinite(a));//true
console.log(Number.isFinite('jsfan'));//false
console.log(Number.isFinite(NaN));//false
console.log(Number.isFinite(undefined));//false
NaN验证(NaN是特殊的非数字)
console.log(Number.isNaN(NaN))//true
Number.isInteger( )判断是否为整数
let a=123.123;
console.log(Number.isInteger(a)); //false
Number.parseInt( )整数转换与Number.parseFloat( )浮点型转换
let a='9.18';
console.log(Number.parseInt(a)); //9
console.log(Number.parseFloat(a));//9.18
整数取值范围操作(整数的操作的取值范围是2的53次方)
let a = Math.pow(2,53)-1;
console.log(a); //9007199254740991
//es6提供了一个常数,叫做最大安全整数
console.log(Number.MAX_SAFE_INTEGER);//9007199254740991
//最小安全整数则为负
console.log(Number.MIN_SAFE_INTEGER);//-9007199254740991
//安全整数判断isSafeInteger( )
let a= Math.pow(2,53)-1;
console.log(Number.isSafeInteger(a));//false

Array中的方法

Array.from( )将JSON代码转换成数组
//构造一个JSON
let  json = { 
    '0': 'jsfan',
    '1': 'blog',
    '2': 'music',
    length:3
}
let arr=Array.from(json);
console.log(arr)//["jsfan", "blog", "music"]
Array.of( )将一堆文本或者变量转换成数组
let arr =Array.of(3,4,5,6);
console.log(arr);//[3, 4, 5, 6]

函数中的严谨模式

不能使用未声明的变量等
相对以前严谨模式必须写在代码最上边,类似于全局使用,ES6则可以写在函数体中,针对函数来使用
function add(a,b=1){//此处报错(严谨模式与默认赋值冲突)
    'use strict'
    throw new Error('This is error');
    return a+b;
}
console.log(add(1));
获得函数需要传递的参数个数
function add(a,b){//如给其中一个参数默认赋值则输出1
    'use strict'
    return a+b;
}
console.log(add.length);//2

Other

find( )实例方法
//实例方法就是并不是以Array对象开始的,而是必须有一个已经存在的数组,然后使用的方法
let arr=[1,2,3,4,5,6,7,8,9];//find( )可查找数字、字符串等
console.log(arr.find(function(value,index,arr){//未找到则返回undefine
//value:表示当前查找的值
//index:表示当前查找的数组索引。
//arr:表示当前数组。
return value > 5;//找到符合条件的数组元素就return,停止查找
}))//6
fill( )实例方法 数组替换
let arr=[0,1,2,3,4,5,6,7];
arr.fill('jsfan',2,4);
console.log(arr);//[0, 1, "jsfan", "jsfan", 4, 5, 6, 7]
for…of数组的遍历
let arr=['jsfan','blog','music']
for (let item of arr){
    console.log(item);//jsfan blog music
}
for (let index of arr.keys()){
    console.log(index);//0 1 2
}
for (let [index,val] of arr.entries()){
    console.log(index+'与'+val);//同时输出item与index
}
entries( )实例方法 手动获取并跳转到下一个值
let arr=['jsfan','blog','music']
let list=arr.entries();
console.log(list.next().value);//[0, "jsfan"]
console.log(list.next().value);//[1, "blog"]
console.log(list.next().value);//[2, "music"]
ES6错误抛出
throw new Error('This is error')
箭头函数简单使用(箭头函数中不可加new)

注意细节

  • 箭头函数中,不存在this、arguments、new.target,如果使用了,则使用的是函数外层的对应的this、arguments、new.target
  • 箭头函数没有原型
  • 箭头函数不能作用构造函数使用

应用场景

  1. 临时性使用的函数,并不会可以调用它,比如:
    1. 事件处理函数
    2. 异步处理函数
    3. 其他临时性的函数
  2. 为了绑定外层this的函数
  3. 在不影响其他代码的情况下,保持代码的简洁,最常见的,数组方法中的回调函数
var add =(a,b=1) => a+b;
console.log(add(1));//2
//等价于
var add =(a,b=1) => {return a+b;};
console.log(add(1));//2
//其还有修订this指向的功能

数组

// 创建了一个长度为100的数组,数组的每一项是"abc"
const arr = new Array(100);
arr.fill("abc"); 
in 判断对象或者数组中是否存在某个值

对象判断

let obj={
    a:'jsfan',
    b:'blog'
}
console.log('a' in obj);  //true
console.log('c' in obj);  //false

数组判断(判断数组中是否存在空值)

let arr=['jsfan',];
console.log(0 in arr); //false
console.log(1 in arr); //true

数组判断(判断数组中是否满足条件)

const arr = [45, 21, 356, 66 , 6, NaN, 723, 54];
console.log(arr.indexOf(66) >= 0)//true
console.log(arr.includes(NaN));//true
数组的遍历方法

1.forEach(特点是会自动省略为空的数组元素)

2.filter(此方法曾在Vue播放器中使用,拥有过滤效果)

let arr=['jsfan','blog','music'];
arr.filter(x=>console.log(x));

3.some

let arr=['jsfan','blog','music'];
arr.some(x=>console.log(x));

4.map(含有替换作用)

let arr=['jsfan','blog','music'];
console.log(arr.map(x=>'web'));//["web", "web", "web"]
数组转换为字符串

1.join(在数组元素中间加了间隔)

let arr=['jsfan','blog','music'];
console.log(arr.join('|'));//jsfan|blog|music

2.toString()

let arr=['jsfan','blog','music'];
console.log(arr.toString());//jsfan,blog,music
find与findIndex
const arr = [{ name: "a",id: 1},
            {name: "b",id: 2},
            {name: "c",id: 3},    
            {name: "d",id: 4},    
            {name: "e",id: 5},
            {name: "f",id: 6},
            {name: "g",id: 7}]   
//找到id为5的对象
const result = arr.find(item => item.id === 5)//id: 5  name: "e"
const resultIndex = arr.findIndex(item => item.id === 5)//4
console.log(result, resultIndex);
copyWithin
const arr = [1, 2, 3, 4, 5, 6];
//从下标2开始,改变数组的数据,数据来自于下标0位置开始
arr.copyWithin(2); // [1, 2, 1, 2, 3, 4]

arr.copyWithin(2, 1); // [1, 2, 2, 3, 4, 5]

arr.copyWithin(2, 1, 3); // [1, 2, 2, 3, 5, 6]

对象

ES6对象赋值
let name="jsfan";
let skill= 'web';
var obj= {name,skill};
console.log(obj); //{name: "jsfan", skill: "web"}
ES6对象key值构建
let key='skill';
var obj={
    [key]:'web'
}
console.log(obj);//{skill: "web"}
对象方法
var obj={
    add:function(a,b){
        return a+b;
    }
}
console.log(obj.add(1,2));  //3

Object中的方法

Object.is( )比较
var obj1 = {name:'jsfan'};
var obj2 = {name:'jsfan'};
console.log(obj1.name === obj2.name);//true

对比===与Object.is( )

console.log(+0 === -0);  //true
console.log(NaN === NaN ); //false
console.log(Object.is(+0,-0)); //false
console.log(Object.is(NaN,NaN)); //true
//===为同值相等,is()为严格相等
Object.assign( )合并对象
var a={a:'jsfan'};
var b={b:'blog'};
let d=Object.assign(a,b)
//将b的数据,覆盖到a,并且会对a产生改动,然后返回a
console.log(a);//{a: "jsfan", b: "blog"}
console.log(d);//{a: "jsfan", b: "blog"}
//处理方法
let e=Object.assign({},a,b)
Object.getOwnPropertyNames 枚举顺序
  • Object.getOwnPropertyNames方法之前就存在,只不过,官方没有明确要求,对属性的顺序如何排序,如何排序,完全由浏览器厂商决定。

  • ES6规定了该方法返回的数组的排序方式如下:

    • 先排数字,并按照升序排序

    • 再排其他,按照书写顺序排序

const obj = {
    d: 1,
    b: 2,
    a: 3,
    0: 6,
    5: 2,
    4: 1
}
const props = Object.getOwnPropertyNames(obj)
console.log(props)//[ '0', '4', '5', 'd', 'b', 'a' ]

Symbol类型(普遍用于nodejs)

  • JavaScript 中,共有6种基本类型:string,number,boolean,null,undefined,symbol (ECMAScript 2015新增)
  • 没有字面量
  • 使用 typeof 得到的类型是 symbol
  • 每次调用 Symbol 函数得到的符号永远不相等,无论符号名是否相同
  • 符号可以作为对象的属性名存在,这种属性称之为符号属性
    • 开发者可以通过精心的设计,让这些属性无法通过常规方式被外界访问
    • 符号属性是不能枚举的,因此在 for-in 循环中无法读取到符号属性,Object.keys 方法也无法读取到符号属性
    • Object.getOwnPropertyNames 尽管可以得到所有无法枚举的属性,但是仍然无法读取到符号属性
    • ES6 新增 Object.getOwnPropertySymbols 方法,可以读取符号
  • 符号无法被隐式转换,因此不能被用于数学运算、字符串拼接或其他隐式转换的场景,但符号可以显式的转换为字符串,通过 String 构造函数进行转换即可,console.log 之所以可以输出符号,是它在内部进行了显式转换
var f= Symbol();
console.log(typeof(f))//symbol
var d= Symbol('jsfan');
console.log(d)//Symbol(jsfan)
Symbol对象元素的保护作用
let obj={name:'jsfan',skill:'web'};
let age=Symbol();
obj[age]=18;//不可用.语法调用
for (let item in obj){
    console.log(obj[item]);//jsfan  //web
} 
console.log(obj);//{name: "jsfan", skill: "web", Symbol(): 18}
console.log(obj[age])//18
const syb = Symbol();
const obj = {
    [syb]: 1,
    a: 2,
    b: 3
}
for (const prop in obj) {
    console.log(prop)//a  b
}
console.log(Object.keys(obj))//[ 'a', 'b' ]
console.log(Object.getOwnPropertyNames(obj))//[ 'a', 'b' ]
//得到的是一个符号属性的数组
const sybs = Object.getOwnPropertySymbols(obj);
console.log(sybs, sybs[0] === syb)//[ Symbol() ] true

Set

一直以来,JS只能使用数组和对象来保存多个数据,缺乏像其他语言那样拥有丰富的集合类型。因此,ES6新增了两种集合类型(set 和 map),用于在不同的场景中发挥作用。

Set不允许内部有重复的值,如果有只显示一个,相当于去重

let setArr = new Set(['jsfan','blog','music','jsfan','music']);
console.log(setArr);//{"jsfan", "blog", "music"}
add追加

数组使用时,可以用push进行追加值,那Set稍有不同,它用更语义化的add进行追加

let setArr = new Set(['jsfan','blog','music','jsfan','music']);
setArr.add('我是追加');
console.log(setArr);//{"jsfan", "blog", "music", "我是追加"}
delete删除
let setArr = new Set(['jsfan','blog','music','jsfan','music']);
setArr.delete('jsfan');
console.log(setArr); //{"blog", "music"}
has查找
let setArr = new Set(['jsfan','blog','music','jsfan','music']);
console.log(setArr.has('jsfan'));//true
clear清除
let setArr = new Set(['jsfan','blog','music','jsfan','music']);
setArr.clear();
console.log(setArr);//{}
for…of…遍历
let setArr = new Set(['jsfan','blog','music','jsfan','music']);
for (let item of setArr){
    console.log(item);//jsfan与blog与music
}
size获取Set值数量
let setArr = new Set(['jsfan','blog','music','jsfan','music']);
console.log(setArr.size);//3
forEach遍历
    let setArr = new Set(['jsfan','blog','music','jsfan','music']);
    setArr.forEach((value)=>console.log(value))//jsfan与blog与music
迭代的应用
// 两个数组的并集、交集、差集 (不能出现重复项),得到的结果是一个新数组
const arr1 = [33, 22, 55, 33, 11, 33, 5];
const arr2 = [22, 55, 77, 88, 88, 99, 99];

//并集
// const result = [...new Set(arr1.concat(arr2))];
console.log("并集", [...new Set([...arr1, ...arr2])]);

const cross = [...new Set(arr1)].filter(item => arr2.indexOf(item) >= 0);
//交集
console.log("交集", cross)

//差集
// console.log("差集", [...new Set([...arr1, ...arr2])].filter(item => arr1.indexOf(item) >= 0 && arr2.indexOf(item) < 0 || arr2.indexOf(item) >= 0 && arr1.indexOf(item) < 0))
console.log("差集", [...new Set([...arr1, ...arr2])].filter(item => cross.indexOf(item) < 0))
手写Set
class MySet {
    constructor(iterator = []) {
        //验证是否是可迭代的对象
        if (typeof iterator[Symbol.iterator] !== "function") {
            throw new TypeError(`你提供的${iterator}不是一个可迭代的对象`)
        }
        this._datas = [];
        for (const item of iterator) {
            this.add(item);
        }
    }

    get size() {
        return this._datas.length;
    }

    add(data) {
        if (!this.has(data)) {
            this._datas.push(data);
        }
    }

    has(data) {
        for (const item of this._datas) {
            if (this.isEqual(data, item)) {
                return true;
            }
        }
        return false;
    }

    delete(data) {
        for (let i = 0; i < this._datas.length; i++) {
            const element = this._datas[i];
            if (this.isEqual(element, data)) {
                //删除
                this._datas.splice(i, 1);
                return true;
            }
        }
        return false;
    }

    clear() {
        this._datas.length = 0;
    }

    *[Symbol.iterator]() {
        for (const item of this._datas) {
            yield item;
        }
    }

    forEach(callback) {
        for (const item of this._datas) {
            callback(item, item, this);
        }
    }

    /**
     * 判断两个数据是否相等
     * @param {*} data1 
     * @param {*} data2 
     */
    isEqual(data1, data2) {
        if (data1 === 0 && data2 === 0) {
            return true;
        }
        return Object.is(data1, data2);
    }
}

Map

键值对(key value pair)数据集合的特点:键不可重复

map集合专门用于存储多个键值对数据。

在map出现之前,我们使用的是对象的方式来存储键值对,键是属性名,值是属性值。

使用对象存储有以下问题:

  1. 键名只能是字符串
  2. 获取数据的数量不方便
  3. 键名容易跟原型上的名称冲突
  • size:只读属性,获取当前map中键的数量
  • set(键, 值):设置一个键值对,键和值可以是任何类型
    • 如果键不存在,则添加一项
    • 如果键已存在,则修改它的值
    • 比较键的方式和set相同
  • get(键): 根据一个键得到对应的值
  • has(键):判断某个键是否存在
  • delete(键):删除指定的键
  • clear(): 清空map
const mp1 = new Map([["a", 3], ["b", 4], ["c", 5]]);
const obj = {};
mp1.set(obj, 6456);
mp1.set("a", "abc");
mp1.set(obj, 111);
        
console.log(mp1)
console.log("总数:", mp1.size);//总数: 4
console.log("get('a')", mp1.get("a"));//get('a') abc
console.log("has('a')", mp1.has("a"));//has('a') true
手写Map
class MyMap {
    constructor(iterable = []) {
        //验证是否是可迭代的对象
        if (typeof iterable[Symbol.iterator] !== "function") {
            throw new TypeError(`你提供的${iterable}不是一个可迭代的对象`)
        }
        this._datas = [];
        for (const item of iterable) {
            // item 也得是一个可迭代对象
            if (typeof item[Symbol.iterator] !== "function") {
                throw new TypeError(`你提供的${item}不是一个可迭代的对象`);
            }
            const iterator = item[Symbol.iterator]();
            const key = iterator.next().value;
            const value = iterator.next().value;
            this.set(key, value);
        }

    }

    set(key, value) {
        const obj = this._getObj(key);
        if (obj) {
            //修改
            obj.value = value;
        }
        else {
            this._datas.push({
                key,
                value
            })
        }
    }

    get(key) {
        const item = this._getObj(key);
        if (item) {
            return item.value;
        }
        return undefined;
    }

    get size() {
        return this._datas.length;
    }

    delete(key) {
        for (let i = 0; i < this._datas.length; i++) {
            const element = this._datas[i];
            if (this.isEqual(element.key, key)) {
                this._datas.splice(i, 1);
                return true;
            }
        }
        return false;
    }

    clear() {
        this._datas.length = 0;
    }

    /**
     * 根据key值从内部数组中,找到对应的数组项
     * @param {*} key 
     */
    _getObj(key) {
        for (const item of this._datas) {
            if (this.isEqual(item.key, key)) {
                return item;
            }
        }
    }

    has(key) {
        return this._getObj(key) !== undefined;
    }

    /**
     * 判断两个数据是否相等
     * @param {*} data1 
     * @param {*} data2 
     */
    isEqual(data1, data2) {
        if (data1 === 0 && data2 === 0) {
            return true;
        }
        return Object.is(data1, data2);
    }

    *[Symbol.iterator]() {
        for (const item of this._datas) {
            yield [item.key, item.value];
        }
    }

    forEach(callback) {
        for (const item of this._datas) {
            callback(item.value, item.key, this);
        }
    }
}

Proxy

new Proxy{},{};
//第一个花括号就相当于我们方法的主体,后边的花括号就是Proxy代理处理区域,等价于写钩子函数的地方。
get类似在方法前调用的钩子函数
var pro = new Proxy({
    add: function (val) {
        return val + 10;
    },
    name: 'I am Jsfan'
}, {
        get:function(target,key){//get属性是在你得到某对象属性值时预处理的方法
            console.log('come in Get');
            return target[key];//target得到的目标值 key目标的key值,相当于对象的属性
        }
    });
console.log(pro.name);
//控制台先输出了come in Get 然后输出I am Jsfan
set属性是值你要改变Proxy属性值时,进行的预先处理。
var pro = new Proxy({
add: function (val) {
    return val + 10;
},
name: 'I am Jsfan'
}, {
    set:function(target,key,value,receiver){
    //target:目标值 key:目标的Key值 value:要改变的值 receiver:改变前的原始值
        console.log(`setting ${key} = ${value}`);
        return target[key] = value;
    }
});
console.log(pro.name);
pro.name='大爱胖哥';
console.log(pro.name);
//I am Jsfan、setting name = 大爱胖哥、大爱胖哥
proxy的知识是非常多,请转至阮一峰大神es6

promise模仿多步操作的过程

1.洗菜做饭。

2.坐下来吃饭。

3.收拾桌子洗碗。

let state=1;

function step1(resolve,reject){
console.log('1.开始-洗菜做饭');
if(state==1){
    resolve('洗菜做饭--完成');
}else{
    reject('洗菜做饭--出错');
}
}
function step2(resolve,reject){
console.log('2.开始-坐下来吃饭');
if(state==1){
    resolve('坐下来吃饭--完成');
}else{
    reject('坐下来吃饭--出错');
}
}
function step3(resolve,reject){
console.log('3.开始-收拾桌子洗完');
if(state==1){
    resolve('收拾桌子洗完--完成');
}else{
    reject('收拾桌子洗完--出错');
}
}
new Promise(step1).then(function(val){
    console.log(val);
    return new Promise(step2);
}).then(function(val){
    console.log(val);
    return new Promise(step3);
}).then(function(val){
    console.log(val);
    return val;
});
    //1.开始-洗菜做饭
    //洗菜做饭--完成
    //2.开始-坐下来吃饭
    //坐下来吃饭--完成
    //3.开始-收拾桌子洗完
    //收拾桌子洗完--完成
手写Promise
const MyPromise = (() => {
    const PENDING = "pending",
        RESOLVED = "resolved",
        REJECTED = "rejected",
        PromiveValue = Symbol("PromiseValue"), //状态数据
        PromiseStatus = Symbol("PromiseStatus"),
        thenables = Symbol("thenables"), //thenable
        catchables = Symbol("catchbles"), //catchables
        changeStatus = Symbol("changeStatus"),//当前状态
        settleHandle = Symbol("settleHandle"), //后续处理的通用函数
        linkPromise = Symbol("linkPromise");  //创建串联的Promise

    return class MyPromise {

        /**
         * 改变当前Promise的状态
         * @param {*} newStatus 
         * @param {*} newValue 
         * @param {*} queue 执行的作业队列
         */
        [changeStatus](newStatus, newValue, queue) {
            if (this[PromiseStatus] !== PENDING) {
                //状态无法变更
                return;
            }
            this[PromiseStatus] = newStatus;
            this[PromiveValue] = newValue;
            //执行相应队列中的函数
            queue.forEach(handler => handler(newValue));
        }

        /**
         * 
         * @param {*} executor 未决阶段(pending状态)下的处理函数
         */
        constructor(executor) {
            this[PromiseStatus] = PENDING;
            this[PromiveValue] = undefined;
            this[thenables] = []; //后续处理函数的数组 -> resolved
            this[catchables] = []; //后续处理函数的数组 -> rejected

            const resolve = data => {
                this[changeStatus](RESOLVED, data, this[thenables]);
            }

            const reject = reason => {
                this[changeStatus](REJECTED, reason, this[catchables]);
            }
            try {
                executor(resolve, reject)
            }
            catch (err) {
                reject(err);
            }
        }

        /**
         * 处理 后续处理函数
         * @param {*} handler 后续处理函数
         * @param {*} immediatelyStatus 需要立即执行的状态
         * @param {*} queue 作业队列
         */
        [settleHandle](handler, immediatelyStatus, queue) {
            if (typeof handler !== "function") {
                return;
            }
            if (this[PromiseStatus] === immediatelyStatus) {
                //直接运行
                setTimeout(() => {
                    handler(this[PromiveValue]);
                }, 0);
            }
            else {
                queue.push(handler);
            }
        }

        [linkPromise](thenalbe, catchable) {
            function exec(data, handler, resolve, reject) {
                try {
                    const result = handler(data); //得到当前Promise的处理结果
                    if (result instanceof MyPromise) {
                        result.then(d => {
                            resolve(d)
                        }, err => {
                            reject(err);
                        })
                    }
                    else {
                        resolve(result);
                    }
                }
                catch (err) {
                    reject(err);
                }
            }

            return new MyPromise((resolve, reject) => {
                this[settleHandle](data => {
                    exec(data, thenalbe, resolve, reject);
                }, RESOLVED, this[thenables])

                this[settleHandle](reason => {
                    exec(reason, catchable, resolve, reject);
                }, REJECTED, this[catchables])
            })
        }

        then(thenable, catchable) {
            return this[linkPromise](thenable, catchable);
        }

        catch(catchable) {

            return this[linkPromise](undefined, catchable);
        }


        static all(proms) {
            return new Promise((resolve, reject) => {
                const results = proms.map(p => {
                    const obj = {
                        result: undefined,
                        isResolved: false
                    }
                    p.then(data => {
                        obj.result = data;
                        obj.isResolved = true;
                        //判断是否所有的全部完成
                        const unResolved = results.filter(r => !r.isResolved)
                        if (unResolved.length === 0) {
                            //全部完成
                            resolve(results.map(r => r.result));
                        }
                    }, reason => {
                        reject(reason);
                    })
                    return obj;
                })
            })
        }

        static race(proms) {
            return new Promise((resolve, reject) => {
                proms.forEach(p => {
                    p.then(data => {
                        resolve(data);
                    }, err => {
                        reject(err);
                    })
                })
            })
        }

        static resolve(data) {
            if (data instanceof MyPromise) {
                return data;
            }
            else {
                return new MyPromise(resolve => {
                    resolve(data);
                })
            }
        }

        static reject(reason) {
            return new MyPromise((resolve, reject) => {
                reject(reason);
            })
        }
    }
})();

使用

        const pro = new MyPromise((resolve, reject) => {
            setTimeout(() => {
                resolve(1)
            }, 3000);
        })

        pro.then(data => {
            console.log(1, data)
            return 123;
        }).then(data => {
            console.log(2, data)
            return new MyPromise(resolve => {
                resolve(456);
            });
        }).then(data => {
            console.log(3, data)
        })

        console.log(pro)
ES7基于Promise的async/await使用方法

使用1:

//var a=1
async function testSync() {
    const response = await new Promise((resolve,reject) => {
    //函数执行将会在await new promise 这一行暂停,直到Promise返回结果
        setTimeout(() => {
            resolve("我还没上车,等等我...");
        }, 1000);
        if(a==1){
            reject('错误')
        }
    });
    console.log(response);
}
testSync();

使用2:

function fn(num) {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    if(num==10){
                        resolve('等等')
                    }
                    if(num==20){
                        resolve('我们')
                    }
                    else{
                        resolve('上车')
                    }
                }, 2000);
            })
}
async function testResult() {
    let first = await fn(10);
    let second = await fn(20);
    let third = await fn(30);
    console.log(first + second + third);//6s后打印(依次等待)
    //等等我们上车
}
testResult()

class类

类的声明与使用
const A = class {} //匿名类
const a = new A();

class Coder{
    name(val){
        console.log(val);
        return val;
    }
    skill(val){
        console.log(this.name('jsfan')+':'+'Skill:'+val);
    }
}
let jsfan= new Coder;
    jsfan.name('jsfan');
    jsfan.skill('web');
类的传参
class Coder{
    constructor(a,b){
        this.a=a;
        this.b=b;
    }
    add(){
        return this.a+this.b;
    }
}
let jsfan=new Coder(1,2);
console.log(jsfan.add());
类的继承
class Coder{
    name(val){
        console.log(val);
        return val;
    }
}
class htmler extends Coder{}
let blog = new htmler;
blog.name('jsfan');
class Animal {
    constructor(type, name, age, sex) {
        if (new.target === Animal) {
            throw new TypeError("你不能直接创建Animal的对象,应该通过子类创建")
        }
        this.type = type;
        this.name = name;
        this.age = age;
        this.sex = sex;
    }

    print() {
        console.log(`【种类】:${this.type}`);
        console.log(`【名字】:${this.name}`);
        console.log(`【年龄】:${this.age}`);
        console.log(`【性别】:${this.sex}`);
    }

    jiao() {
        throw new Error("动物怎么叫的?");
    }
}

class Dog extends Animal {
    constructor(name, age, sex) {
        super("犬类", name, age, sex);
        // 子类特有的属性
        this.loves = "吃骨头";
    }

    print() {
        //调用父类的print
        super.print();
        //自己特有的代码
        console.log(`【爱好】:${this.loves}`);
    }

    //同名方法,会覆盖父类
    jiao() {
        console.log("旺旺!");
    }
}

const a = new Dog("旺财", 3, "公")
a.print();
// 【种类】:犬类
// 【名字】:旺财
// 【年龄】:3
// 【性别】:公
// 【爱好】:吃骨头
a.jiao()
//旺旺!

js中的call( )用法

  • A.call(B,x,y)

    1.改变函数A的this指向,使之指向B

    2.把A函数放到B中运行,x和y是A函数的参数

function Animal(type, name, age, sex) {
    this.type = type;
    this.name = name;
    this.age = age;
    this.sex = sex;
}
Animal.prototype.print = function () {
    console.log(`【种类】:${this.type}`);
    console.log(`【名字】:${this.name}`);
    console.log(`【年龄】:${this.age}`);
    console.log(`【性别】:${this.sex}`);
}

function Dog(name, age, sex) {
    //借用父类的构造函数
    Animal.call(this, "犬类", name, age, sex);
}

Object.setPrototypeOf(Dog.prototype, Animal.prototype);

const d = new Dog("旺财", 3, "公");
d.print();
// 【种类】:犬类
// 【名字】:旺财
// 【年龄】:3
// 【性别】:公
console.log(d);
//Dog { type: '犬类', name: '旺财', age: 3, sex: '公' }
getter和setter

Object.defineProperty 可定义某个对象成员属性的读取和设置

使用getter和setter控制的属性,不在原型上

const printName = "print";

class Animal {
    constructor(type, name, age, sex) {
        this.type = type;
        this.name = name;
        this.age = age;
        this.sex = sex;
    }

    //创建一个age属性,并给它加上getter,读取该属性时,会运行该函数
    get age() {
        return this._age + "岁";
    }

    //创建一个age属性,并给它加上setter,给该属性赋值时,会运行该函数
    set age(age) {
        if (typeof age !== "number") {
            throw new TypeError("age property must be a number");
        }
        if (age < 0) {
            age = 0;
        }
        else if (age > 1000) {
            age = 1000;
        }
        this._age = age;
    }

    [printName]() {
        console.log(`【种类】:${this.type}`);
        console.log(`【名字】:${this.name}`);
        console.log(`【年龄】:${this.age}`);
        console.log(`【性别】:${this.sex}`);
    }
}

var a = new Animal("狗", "旺财", 3, "男");
a[printName]();
//【种类】:狗
//【名字】:旺财
//【年龄】:3岁
//【性别】:男
static

使用static的字段初始化器,添加的是静态成员

没有使用static的字段初始化器,添加的成员位于对象上

箭头函数在字段初始化器位置上,指向当前对象

class Chess {
    name = 'jsfan'

    static width = 50;

    static height = 50;    

    constructor() {
        this.name 
    }

    static method() {
        console.log('static香不香?')
    }
}

console.log(new Chess())//Chess { name: 'jsfan' }
console.log(Chess.width)//50
console.log(Chess.height)//50
Chess.method();//static香不香?

模块化操作

export : 负责进行模块化,也是模块的输出

import : 负责把模块引,也是模块的引入操作

export var a = 'jsfan';//模块输出

import {a} from '路径';//模块引入
console.log(a);
//多变量的输出
var a ='jsfan';
var b ='blog';
var c = 'music';
export {a,b,c}
//或者
export {
    x as a,
    y as b,
    z as c
}
//多输出与多引入
export var a ='jspang';
export function add(a,b){
    return a+b;
}

import {a,add} form '路径';//可分开写

Fetch

XMLHttpRequest的问题

  1. 所有的功能全部集中在同一个对象上,容易书写出混乱不易维护的代码
  2. 采用传统的事件驱动模式,无法适配新的 Promise Api

Fetch Api 的特点

  1. 并非取代 AJAX,而是对 AJAX 传统 API 的改进
  2. 精细的功能分割:头部信息、请求信息、响应信息等均分布到不同的对象,更利于处理各种复杂的 AJAX 场景
  3. 使用 Promise Api,更利于异步代码的书写
  4. Fetch Api 并非 ES6 的内容,属于 HTML5 新增的 Web Api
  5. 需要掌握网络通信的知识

请求配置对象

  • method:字符串,请求方法,默认值GET
  • headers:对象,请求头信息
  • body: 请求体的内容,必须匹配请求头中的 Content-Type
  • mode:字符串,请求模式
    • cors:默认值,配置为该值,会在请求头中加入 origin 和 referer
    • no-cors:配置为该值,不会在请求头中加入 origin 和 referer,跨域的时候可能会出现问题
    • same-origin:指示请求必须在同一个域中发生,如果请求其他域,则会报错
  • credentials: 如何携带凭据(cookie)
    • omit:默认值,不携带cookie
    • same-origin:请求同源地址时携带cookie
    • include:请求任何地址都携带cookie
  • cache:配置缓存模式
    • default: 表示fetch请求之前将检查下http的缓存.
    • no-store: 表示fetch请求将完全忽略http缓存的存在. 这意味着请求之前将不再检查下http的缓存, 拿到响应后, 它也不会更新http缓存.
    • no-cache: 如果存在缓存, 那么fetch将发送一个条件查询request和一个正常的request, 拿到响应后, 它会更新http缓存.
    • reload: 表示fetch请求之前将忽略http缓存的存在, 但是请求拿到响应后, 它将主动更新http缓存.
    • force-cache: 表示fetch请求不顾一切的依赖缓存, 即使缓存过期了, 它依然从缓存中读取. 除非没有任何缓存, 那么它将发送一个正常的request.
    • only-if-cached: 表示fetch请求不顾一切的依赖缓存, 即使缓存过期了, 它依然从缓存中读取. 如果没有缓存, 它将抛出网络错误(该设置只在mode为”same-origin”时有效).

返回值

fetch 函数返回一个 Promise 对象

  • 当收到服务器的返回结果后,Promise 进入resolved状态,状态数据为 Response 对象
  • 当网络发生错误(或其他导致无法完成交互的错误)时,Promise 进入 rejected 状态,状态数据为错误信息

Response对象

  • ok:boolean,当响应消息码在200~299之间时为true,其他为false
  • status:number,响应的状态码
  • text():用于处理文本格式的 Ajax 响应。它从响应中获取文本流,将其读完,然后返回一个被解决为 string 对象的 Promise。
  • blob():用于处理二进制文件格式(比如图片或者电子表格)的 Ajax 响应。它读取文件的原始数据,一旦读取完整个文件,就返回一个被解决为 blob 对象的 Promise。
  • json():用于处理 JSON 格式的 Ajax 的响应。它将 JSON 数据流转换为一个被解决为 JavaScript 对象的promise。
  • redirect():可以用于重定向到另一个 URL。它会创建一个新的 Promise,以解决来自重定向的 URL 的响应。

迭代器

背景知识

  1. 什么是迭代?

从一个数据集合中按照一定的顺序,不断取出数据的过程

  1. 迭代和遍历的区别?

迭代强调的是依次取数据,并不保证取多少,也不保证把所有的数据取完

遍历强调的是要把整个数据依次全部取出

  1. 迭代器

对迭代过程的封装,在不同的语言中有不同的表现形式,通常为对象

  1. 迭代模式

一种设计模式,用于统一迭代过程,并规范了迭代器规格:

  • 迭代器应该具有得到下一个数据的能力
  • 迭代器应该具有判断是否还有后续数据的能力

JS中的迭代器

JS规定,如果一个对象具有next方法,并且该方法返回一个对象,该对象的格式如下:

{value:, done: 是否迭代完成}

则认为该对象是一个迭代器

含义:

  • next方法:用于得到下一个数据
  • 返回的对象
    • value:下一个数据的值
    • done:boolean,是否迭代完成
const arr = [1, 2, 3, 4, 5];
//迭代数组arr
const iterator = {
    i: 0, //当前的数组下标
    next() {
        var result = {
            value: arr[this.i],
            done: this.i >= arr.length
        }
        this.i++;
        return result;
    }
}

//让迭代器不断的取出下一个数据,直到没有数据为止
let data = iterator.next();
while (!data.done) { //只要没有迭代完成,则取出数据
    console.log(data.value)
    //进行下一次迭代
    data = iterator.next();
}

console.log("迭代完成")

迭代博主处于懵逼状态,后续没看懂,以此为记


属性描述符

Property Descriptor 属性描述符 是一个普通对象,用于描述一个属性的相关信息

通过Object.getOwnPropertyDescriptor(对象, 属性名)可以得到一个对象的某个属性的属性描述符

  • value:属性值
  • configurable:该属性的描述符是否可以修改
  • enumerable:该属性是否可以被枚举
  • writable:该属性是否可以被重新赋值

Object.getOwnPropertyDescriptors(对象)可以得到某个对象的所有属性描述符

如果需要为某个对象添加属性时 或 修改属性时, 配置其属性描述符,可以使用下面的代码:

Object.defineProperty(对象, 属性名, 描述符);
Object.defineProperties(对象, 多个属性的描述符)

存取器属性

属性描述符中,如果配置了 get 和 set 中的任何一个,则该属性,不再是一个普通属性,而变成了存取器属性。

get 和 set配置均为函数,如果一个属性是存取器属性,则读取该属性时,会运行get方法,将get方法得到的返回值作为属性值;如果给该属性赋值,则会运行set方法。

存取器属性最大的意义,在于可以控制属性的读取和赋值。

const obj = {
    a: 1,
    b: 2
}
Object.defineProperty(obj, "a", {
    value: 3,
    configurable: false,
    enumerable: false,
    writable: false
})
obj.a = 10;
console.log(obj);//{b: 2, a: 3}

const props = Object.keys(obj)
console.log(props)//["b"]

const values = Object.values(obj);
console.log(values);//[2]

const desc = Object.getOwnPropertyDescriptor(obj, "a")
console.log(desc);//{value: 3, writable: false, enumerable: false, configurable: false}
const obj = {
    b: 2
}
Object.defineProperty(obj, "a", {
    get() {
        console.log("运行了属性a的get函数")
        return obj._a;
    },
    set(val){
        console.log("运行了属性a的set函数", val)
        obj._a = val;
    }
})
obj.a = 10;
console.log(obj.a);
/*
运行了属性a的set函数 10
运行了属性a的get函数
10
*/

反射与代理

反射
  1. Reflect是什么?

Reflect是一个内置的JS对象,它提供了一系列方法,可以让开发者通过调用这些方法,访问一些JS底层功能

由于它类似于其他语言的反射,因此取名为Reflect

  1. 它可以做什么?

使用Reflect可以实现诸如 属性的赋值与取值、调用普通函数、调用构造函数、判断属性是否存在与对象中等功能

  1. 它里面到底提供了哪些API呢?
  • Reflect.set(target, propertyKey, value): 设置对象target的属性propertyKey的值为value,等同于给对象的属性赋值
  • Reflect.get(target, propertyKey): 读取对象target的属性propertyKey,等同于读取对象的属性值
  • Reflect.apply(target, thisArgument, argumentsList):调用一个指定的函数,并绑定this和参数列表。等同于函数调用
  • Reflect.deleteProperty(target, propertyKey):删除一个对象的属性
  • Reflect.defineProperty(target, propertyKey, attributes):类似于Object.defineProperty,不同的是如果配置出现问题,返回false而不是报错
  • Reflect.construct(target, argumentsList):用构造函数的方式创建一个对象
  • Reflect.has(target, propertyKey): 判断一个对象是否拥有一个属性
  • 其他API:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflec
//get
const obj = {
    a: 1,
    b: 2
}
Reflect.set(obj, "a", 10);
console.log(Reflect.get(obj, "a"))//10

//apply
function method(a, b){
    console.log("method", a, b);//method 3 4
}
Reflect.apply(method, null, [3, 4])

//deleteProperty
const obj = {
    a: 1,
    b: 2
}
Reflect.deleteProperty(obj, "a");
console.log(obj);//{b: 2}

//construct
function Test(a, b) {
    this.a = a;
    this.b = b;
}
const t = Reflect.construct(Test, [1, 3]);
console.log(t)//Test {a: 1, b: 3}

//has
const obj = {
    a: 1,
    b: 2
}
console.log(Reflect.has(obj, "a"));//true
代理

代理:提供了修改底层实现的方式

//代理一个目标对象
//target:目标对象
//handler:是一个普通对象,其中可以重写底层实现
//返回一个代理对象
new Proxy(target, handler)
const obj = {
    a: 1,
    b: 2
}

const proxy = new Proxy(obj, {
    set(target, propertyKey, value) {
        Reflect.set(target, propertyKey, value);
    },
    get(target, propertyKey) {
        if (Reflect.has(target, propertyKey)) {
            return Reflect.get(target, propertyKey);
        } else {
            return -1;
        }
    },
    has(target, propertyKey) {
                return false;
    }
});
console.log(proxy);//Proxy {a: 1, b: 2}
proxy.a = 10;
console.log(proxy.a);//10
console.log(proxy.d);//-1
console.log("a" in proxy);//false
[应用]观察者模式

有一个对象,是观察者,它用于观察另外一个对象的属性值变化,当属性值变化后会收到一个通知,可能会做一些事。

<div id="container">
</div>

<script>
    //创建一个观察者
    function observer(target) {
        const div = document.getElementById("container");
        const proxy = new Proxy(target, {
            set(target, prop, value) {
                Reflect.set(target, prop, value);
                render();
            },
            get(target, prop){
                return Reflect.get(target, prop);
            }
        })
        render();

        function render() {
            let html = "";
            for (const prop of Object.keys(target)) {
                html += `
                    <p><span>${prop}:</span><span>${target[prop]}</span></p>`;
            }
            div.innerHTML = html;
        }

        return proxy;
    }
    const target = {
        a: 1,
        b: 2
    }
    const obj = observer(target)
</script>
[应用]偷懒的构造函数
class User {}

function ConstructorProxy(Class, ...propNames) {
    return new Proxy(Class, {
        construct(target, argumentsList) {
            const obj = Reflect.construct(target, argumentsList)
            propNames.forEach((name, i) => {
                obj[name] = argumentsList[i];
            })
            return obj;
        }
    })
}

const UserProxy = ConstructorProxy(User, "firstName", "lastName", "age")

const obj = new UserProxy("js", "fan", 18);
console.log(obj)
/*
User
age: 18
firstName: "js"
lastName: "fan"
*/

class Monster {}

const MonsterProxy = ConstructorProxy(Monster, "attack", "defence", "hp", "rate", "name")

const m = new MonsterProxy(10, 20, 100, 30, "怪物")
console.log(m);
/*
Monster
attack: 10
defence: 20
hp: 100
name: "怪物"
rate: 30
*/
可验证函数参数
function sum(a, b) {
    return a + b;
}

function validatorFunction(func, ...types) {
    const proxy = new Proxy(func, {
        apply(target, thisArgument, argumentsList) {
            types.forEach((t, i) => {
                const arg = argumentsList[i]
                if (typeof arg !== t) {
                    throw new TypeError(`第${i+1}个参数${argumentsList[i]}不满足类型${t}`);
                }
            })
            return Reflect.apply(target, thisArgument, argumentsList);
        }
    })
    return proxy;
}
const sumProxy = validatorFunction(sum, "number", "number")
console.log(sumProxy(1, 2))//3
console.log(sumProxy('ceshi', 2))//Uncaught TypeError: 第1个参数ceshi不满足类型number
--End--
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值