当前位置: 首页 > news >正文

es6特性-第二部分

Promise

介绍和基本使用

Promise是ES6引入的异步编程的新解决方案,主要用来解决回调地狱问题。语法上 Promise是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果。

  1. Promise构造函数:new Promise()

  2. Promise.prototype.then方法

  3. Promise.prototype.catch方法

//创建实例
const p = new Promise(function (resolve, reject) {//模拟主体业务代码setTimeout(() => {//let data = "成功获取数据库中的数据";//如果获取数据成功,执行resolve()函数,并将返回数据作为参数传入//resolve(data);let data = "获取失败";//如果获取数据失败,执行reject()函数,并将返回数据作为参数传入reject(data);}, 1000);
});
//调用promise实例
p.then(function (value) {//执行resolve函数执行的方法//...
}, function (reason) {//执行reject()函数执行的方法//...
});

这种写法好处在于代码简洁,避免了回调地狱问题。

Promise.prototype.then

调用then方法,then方法的逐回结果是 Promise对象,对象状态由回调函数的执行结果决定。

  1. 如果回调函数中返回的结果是非promise 类型的属性,状态为成功,返回值为对象的成功的值.
  2. 如果回调函数中返回的结果是promise 类型的属性,状态根据then方法内部Promise返回的状态决定。
  3. 如果直接抛出错误,返回值也是promise类型的,值为抛出错误的值。
//创建实例
const p = new Promise(function (resolve, reject) {//模拟主体业务代码setTimeout(() => {//let data = "成功获取数据库中的数据";//resolve(data);let data = "获取失败";reject(data);}, 1000);
});
//调用promise实例
const result = p.then(function (value) {//1.非promise类型的属性return 'iloveyou';//2.是 promise对象return new Promise((resolve, reject) => {// resolve('ok');reject('error ');});//3.抛出错误throw new Error("出错啦!");
}, function (reason) {console.warn(reason);
});
console.log(result);

由于promise返回的是promise类型,所以可以进行链式调用

const fs = require('fs');const p = new Promise(function (resolve, reject) {fs.readFile("./source/为学.md", function (err, data) {if (err) reject(err);resolve(data);});
});p.then(value => {return new Promise((resolve, reject) => {fs.readFile("./source/为学1.md", function (err, data) {resolve([value, data]);});});
}).then(value => {return new Promise((resolve, reject) => {fs.readFile("./source/为学2.md", function (err, data) {value.push(data);return resolve(value);});});
}).then(value => {console.log(value.join('\r\n'));
});
Promise.prototype.catch

通过cache方法可以指定Promise发生错误时的回调。

promise-ajax
const p = new Promise(function (resolve, reject) {const xhr = new XMLHttpRequest();xhr.open('GET', 'https://layuion.com/static/json/table/user.json?page=1&limit=10');xhr.send();xhr.onreadystatechange = function () {if (xhr.readyState == 4) {if (xhr.status >= 200 && xhr <= 400) {resolve(xhr.response);} else {reject('获取失败');}}};
});
p.then(function (value) {console.log(value);
}, function (reason) {console.log(reason);
})

set

ES6提供了新的数据结构 set(集合)。它类似于数组,但成员的值都是唯一的(声明时即使有重复,也会去重)。集合实现了Iterator接口,所以可以使用扩展运算符和for ...of...进行遍历。

集合的属性和方法

size 返回集合的元素个数

add 增加一个新元素,返回当前集合

delete 删除元素,返回boolean值

has 检测集合中是否包含某个元素,返回boolean值

//声明集合并赋值
let s1 = new Set(['猪', '狗', '牛', '羊', '狗']);
console.log(s1); //Set(4) {'猪', '狗', '牛', '羊'}
//长度
console.log(s1.size); //4
//添加
s1.add('猫');
console.log(s1); //Set(5) {'猪', '狗', '牛', '羊', '猫'}
//删除
s1.delete('牛');
console.log(s1); //Set(4) {'猪', '狗', '羊', '猫'}
//has
console.log(s1.has('猫')) //true
console.log(s1.has('鸡')) //false
//清空
s1.clear();
console.log(s1);Set(0) {size: 0}
//遍历
for (const v of s1) {console.log(v);
}
集合实践
//集合实践
let a1 = [1, 2, 3, 5, 5, 6, 7, 8, 6, 5];
console.log(a1); //[1, 2, 3, 5, 5, 6, 7, 8, 6, 5]
//去重
let unq = [...new Set(a1)];
console.log(unq);//[1, 2, 3, 5, 6, 7, 8]
//交集
let a2 = [4, 5, 6, 9, 6, 4, 3];
const result = [...new Set(a1)].filter(item => new Set(a2).has(item));
console.log(result); //[3, 5, 6]
//并集
console.log([...new Set([...a1, ...a2])]);
//差集-> a1 和 a2取差集意思是a1里面有,a2没有的数据; 也就是交集的取反
console.log([...new Set(a1)].filter(item => !new Set(a2).has(item))); //[1, 2, 7, 8]

map

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

Map的属性和方法:

size 返回Map的元素个数,

set 增加一个新元素,返回当前Map

get 返回键名对象的键值

has 检测Map 中是否包含某个元素,返回boolean值

clear清空集合,返回undefined

//声明map
let m1 = new Map();
//添加
m1.set('team', 'IG');
m1.set('jiaolian', 'ssss');
m1.set('lpl', function () {console.log('IG电子竞技!');
})
let boos = {'boos': '王思聪'
}
m1.set(boos, {'上单': 'the shy','打野': 'ning','中单': 'rookie','adc': 'jacklove','辅助': 'baolan'
});
//size
console.log(m1.size);
//删除
m1.delete('jiaolian');
//获取
console.log(m1.get('team'));
console.log(m1.get('lpl'));
console.log(m1.get(boos));
//清空
m1.clear();

WeakMap

WeakMap 对象是一组键/值对的集合,其中的键是弱引用的。其键必须是对象,而值可以是任意的。它的键被弱保持,也就是说,当其键所指对象没有其他地方引用的时候,它会被 GC 回收掉。

为什么使用weakmap?

在 JavaScript 里,map API 可以 通过使其四个 API 方法共用两个数组(一个存放键,一个存放值)来实现。给这种 map 设置值时会同时将键和值添加到这两个数组的末尾。从而使得键和值的索引在两个数组中相对应。当从该 map 取值的时候,需要遍历所有的键,然后使用索引从存储值的数组中检索出相应的值。

但这样的实现会有两个很大的缺点:

  1. 首先赋值和搜索操作都是 O(n) 的时间复杂度(n 是键值对的个数),因为这两个操作都需要遍历全部整个数组来进行匹配。

    简单来说就是赋值和搜索都会遍历整个数组,时间复杂度都是O(n)

  2. 另外一个缺点是可能会导致内存泄漏,因为数组会一直引用着每个键和值。这种引用使得垃圾回收算法不能回收处理他们,即使没有其他任何引用存在了。

相比之下,WeakMap它的键被弱保持,也就是说,当其键所指对象没有其他地方引用的时候,它会被 GC 回收掉。WeakMap提供的接口与Map相同。

Map对象不同的是,WeakMap的键是不可枚举的。不提供列出其键的方法。列表是否存在取决于垃圾回收器的状态,是不可预知的。

class类

ES6提供了更接近传统语言的写法,引入了Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。基本上,ES6的class可以看作只是一个语法糖,它的绝大部分功能,ES5都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。

/*--------------------之前es5实现类的方法举例------------------------*/
//手机
function Phone(brand, price) {this.brand = brand;this.price = price;
}
//添加方法
Phone.prototype.call = function () {console.log("我可以打电话!!");
}
//实例化对象
let Huawei = new Phone('华为', 5999);
Huawei.call();
console.log(Huawei);
/*--------------------es6实现类------------------------*/
class shouji {//构造方法名字不能修改constructor(brand, price) {this.brand = brand;this.price = price;}call() {console.log("我可以打电话!!");}
}
let onePlus = new shouji('1+', 1999);
console.log(onePlus);
onePlus.call();

同一作用域的类名和函数名不能相同,例如上述代码的类名就不能命名为Phone。

类中构造方法不是必须要有。

静态成员
function Phone() { } //表示函数对象phone,相当于面向对象里面的类。
//给函数对象添加属性,它的name属性只属于Phone对象。相当于面向对象里面的静态属性
Phone.name = '手机';
//给函数对象添加方法。它的change方法只属于Phone对象相当于面向对象里面的静态方法
Phone.change = function () {console.log("我可以改变世界");
}
//访问函数对象属性和方法
console.log(Phone.name);
console.log(Phone.change);//可以通过函数对象的prototype属性给实例对象添加方法和属性
Phone.prototype.size = '5.5inch';
let nokia = new Phone(); //实例化一个对象
console.log(nokia.size); //5.5inch
//undefined 实例化对象和函数对象不相同。所以不能访问。
console.log(nokia.name);
console.log(nokia.change());//caught TypeError: nokia.change is not a function

es6中写法

class Phone{//静态属性static name ='手机';//只能通过类名访问static change(){ //只能通过类名访问console.log("我可以改变世界");}
}
let nokia = new Phone();
console.1og(nokia.name);
console.log(Phone.name);
继承

es5里实现继承

function Phone(brand, price) {this.brand = brand;this.price = price;
}
Phone.prototype.call = function () {console.log("我可以打电话");
}
//智能手机
function SmartPhone(brand, price, color, size) {Phone.call(this, brand, price);this.color = color;this.size = size;
}
//设置子级构造函数的原型
SmartPhone.prototype = new Phone;
SmartPhone.prototype.constructor = SmartPhone;//矫正,不加也可以
//声明子类的方法
SmartPhone.prototype.photo = function () {console.log("我可以拍照")
}
SmartPhone.prototype.playGame = function () {console.log("我可以玩游戏");
}
const chuizi = new SmartPhone('锤子', 2499, '黑色', '5.5inch ');
console.log(chuizi);

es6里面类的继承

class Phone {constructor(brand, price) {this.brand = brand;this.price = price;}call() {console.log('打电话')}
}class SmartPhone extends Phone {constructor(brand, price, color, size) {super(brand, price);this.color = color;this.brand = brand;}photo() {console.log("我可以拍照");}playGame() {console.log('我可以玩游戏');}
}
let xiaomi = new SmartPhone('小米', '799', '粉色', '70inch');
console.log(xiaomi);

es6中只允许继承一个类。

es6类中普通的方法中不允许调用super方法

重写

子类中可以声明同父类名称相同的方法。

getter和setter

getter:可以对类属性绑定一个函数,当调用这个属性的时候,这个函数被执行,这个函数的返回值就是这个属性的值。通常对对象的动态属性进行封装。

setter:可以对类属性绑定一个函数,当设置这个属性的时候,这个函数被执行。可以对属性的合法性进行判断

class Phone {//这里的price为类Phone的属性,后边的分别是getter和setter绑定的方法get price() {console.log('get price')return 90;}set price(price) {console.log("set price");}
}
let p = new Phone();
console.log(p.price);//get price 90
p.price = 100;//set price

数值扩展

Number.EPSILON

Number.EPSILON是JavaScript表示的最小精度。

EPSILON属性的值接近于2.2204460492503130808472633361816E-16

通常用于两个数字进行比较,如果结果比Number.EPSILON小或等于这个数,就认为这两个数字相等。

function equal(a, b) {if (Math.abs(a - b) < Number.EPSILON) {return true;} else {return false;}
}
console.log(0.1 + 0.2 === 0.3); //false
console.log(equal(0.1 + 0.2, 0.3)) //true

二进制和八进制

let b = 0b1010; //二进制
let o = 0o777; //八进制
let d = 100; //十进制
let x= 0xff; //16进制

Number.isFinite()

检测一个数值是否为有限数

console.log(Number.isFinite(100)); //true
console.log(Number.isFinite(100/0)); //false
console.log(Number.isFinite(Infinity));//false

Number.isNaN()

Number.isNaN()检测一个数值是否为NaN

Number.parseInt() Number.parseFloat()

字符串转整数

Number.isInteger()

判断一个数是否为整数

Math.trunc()

将数字的小数部分抹掉

Math.sign()

判断一个数到底为正数、负数还是零

对象的方法扩展

object.is()

判断两个值是否完全相等。和===略有不同,判断NaN的时候不一样。

console.log(Object.is(120,120));//true
console.log(Object.is(NaN,NaN));//true
console.log(NaN === NaN);//false

Object.assign()

对象的合并。如果合并的对象中有相同的属性或方法,后边的会覆盖前边的属性或方法。

get或set原型对象

Object.setPrototype0f 设置原型对象

Object.getPrototypeof 获取原型对象

模块化

模块化是指将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来。

模块化的好处

防止命名冲突

代码复用

高维护性

模块化规范产品

ES6 之前的模块化规范有:

CommonJs规范 => NodeJS、Browserify产品

AMD规范 => requireJs产品

CMD规范 => seaJs产品

ES6模块化语法

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

export命令用于规定模块的对外暴露接口。

/*---./js/m1.js---*/
//分别暴露
export let s = "测试模块";
export function change() {console.log('用心改变世界');
}

上边的使用export分别将schange暴露。还有以下两种暴露方式:

  • 统一暴露:在最底部写入一下代码export {s, change};

  • 默认暴露:default里面可以跟任意数据,对象居多。这种方式使用的时候就要多加个default,例如:m1.default.school

    //默认暴露
    export default {s: "测试模块',change: function(){console.log("我们可以改变你!!");}
    }
    

import命令用于输入其他模块提供的功能

import * as m1 from "./js/m1.js";
console.log(m1);

上边的代码使用的是通用导入方式,还有以下两种导入方式:

  • 解构赋值形式

    import {s, change} from "./js/m1.js";
    import {s as guigu, change} from "./js/m2.js"; //与第一个s重名,可以使用as命名别名
    import {default as m3} from " ./js/m3.js";
    
  • 简便形式,只能针对默认暴路

    import m3 from "./js/m3.js";
    console.log(m3);
    

一般引入模块都是放在一个入口文件里面如:app.js, 在html中使用时候引入这个文件就可以了

<script src="./src/js/app.js" type="module"></script> 注意:type="module".

在js文件中只要使用了import语句,不管import之间是否有javascript语句,都会按import顺序先执行import的语句

es6模块化代码转换

由于es6语法对所有的浏览器都都不一定支持,所以考虑到兼容性,需要将es6模块代码转换成es5代码。

  1. 安装工具 babel-cli(babel客户端命令行) babel-preset-env(babel环境) browserify(项目中一般使用webpack打包)

  2. 使用命令npx babel src/js -d dist/js --presets=babel-preset-env,如果全局安装babel,可以直接使用babel命令:babel src/js -d dist/js --presets=babel-preset-env. 命令的格式:

    [npx] babel 要转换文件路径 -d 转换完成后要存储的文件路径 --presets=babel-preset-env

  3. 打包:npx browserify dist/js/app.js -o dist/bundle.js

打包完成后就可以在html引入了。每次更改都需要进行上述2、3步操作。

proxy

Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。proxy 是javascript的内置对象,在es6中被引入。

语法
const p = new Proxy(target, handler)
参数

target : 要使用 Proxy 包装的目标对象(引用类型:对象,数组,函数,map, set, 甚至另一个代理)。

handler : 一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。

let person = {name: "John",age: 21,
};const p = new Proxy(person, {/*** 拦截读取属性* @param target 目标对象* @param property 被获取的属性名。* @param receiver Proxy 或者继承 Proxy 的对象*/get(target, property, receiver) {},/*** 拦截赋值属性* @param target 目标对象* @param property 被设置的属性名。* @param value 属性值* @param receiver Proxy 或者继承 Proxy 的对象* @returns boolean*/set(target, property, value, receiver) {return true;},/*** 拦截方法调用* @param target 目标对象(函数)* @param thisArg 被调用时的上下文对象* @param argumentsList 被调用时的参数数组。* apply方法可以返回任何值。*/apply(target, thisArg, argumentsList) {},/*** 用于拦截 new 操作符* @param target 目标对象* @param argumentsList constructor 的参数列表* @param newTarget 最初被调用的构造函数* construct 方法必须返回一个对象。*/construct(target, argumentsList, newTarget) {return {};},/*** 用于拦截 in 操作符* @param target 目标对象。* @param prop 需要检查是否存在的属性。* has 方法返回一个 boolean 属性的值。*/has(target, prop) {return true;},/*** 法用于拦截 Reflect.ownKeys()* @param target 目标对象*/ownKeys(target) {return Reflect.ownKeys(target);},/*** 用于拦截对对象属性的 delete 操作。* @param target 目标对象。* @param property 待删除的属性名。* @returns boolean*/deleteProperty(target, property) {return true;},
});

Reflect

Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与 proxy handler (en-US) 的方法相同。Reflect 不是一个函数对象,因此它是不可构造的。反射,就是将代理的内容反射出去。

与大多数全局对象不同 Reflect 并非一个构造函数,所以不能通过 new 运算符对其进行调用,或者将 Reflect 对象作为一个函数来调用。Reflect 的所有属性和方法都是静态的

注意:Reflect的静态方法是直接使用的

const p = new Proxy(person, {/*** 拦截读取属性* @param target 目标对象* @param property 被获取的属性名。* @param receiver Proxy 或者继承 Proxy 的对象*/get(target, property, receiver) {if (target.age <= 15) {return Reflect.get(target, property, receiver); //直接使用和对象操作一样} else {return "John成年了!!!";}},
静态方法
Reflect.apply(target, thisArgument, argumentsList)
对一个函数进行调用操作,同时可以传入一个数组作为调用参数。和 Function.prototype.apply() 功能类似。Reflect.construct(target, argumentsList[, newTarget])
对构造函数进行 new 操作,相当于执行 new target(...args)。Reflect.deleteProperty(target, propertyKey)
作为函数的delete操作符,相当于执行 delete target[name]。Reflect.get(target, propertyKey[, receiver])
receiver可以理解为上下文this对象
获取对象身上某个属性的值,类似于 target[name]。Reflect.getPrototypeOf(target)
类似于 Object.getPrototypeOf()。Reflect.has(target, propertyKey)
判断一个对象是否存在某个属性,和 in 运算符 的功能完全相同。Reflect.isExtensible(target)
类似于 Object.isExtensible().Reflect.ownKeys(target)
返回一个包含所有自身属性(不包含继承属性)的数组。(类似于 Object.keys(), 但不会受enumerable 影响).Reflect.preventExtensions(target)
类似于 Object.preventExtensions()。返回一个Boolean。Reflect.set(target, propertyKey, value[, receiver])
将值分配给属性的函数。返回一个Boolean,如果更新成功,则返回true。

在面向对象中,反射的三种用法:1、查看元数据。2、动态创建对象。3、动态调用方法。

示例

模拟mobx

const list: Set<Function> = new Set();
const autorun = (cb: Function) => {list.add(cb);
};
const observable = <T extends object>(target) => {return new Proxy(target, {set(target, p, newValue, receiver) {const result = Reflect.set(target, p, newValue, receiver);list.forEach((fn) => fn());return result;},});
};
const dog = observable({ name: "阿黄", age: 1 });
autorun(() => {console.log("change");
});dog.name = "阿绿"; //change
dog.age = 2; //change
http://www.lqws.cn/news/568567.html

相关文章:

  • 【JavaScript】setTimeout和setInterval中的陷阱
  • 数据挖掘、机器学习与人工智能:概念辨析与应用边界
  • Linux基本命令篇 —— cal命令
  • 模型预测控制专题:基于增量模型的无差拍预测电流控制
  • Rust 和C++工业机器人实践
  • React与Vue的主要区别
  • 数据分析标普500
  • 打造地基: App拉起基础小程序容器
  • 【AOSP专题】07. FART脱壳-02
  • Python训练营-Day45-tensorboard
  • 设计模式精讲 Day 18:备忘录模式(Memento Pattern)
  • 如何搭建基于RK3588的边缘服务器集群?支持12个RK3588云手机
  • FAST-LIO2源码分析-状态预测
  • 二叉树进阶刷题02(非递归的前中后序遍历)
  • libevent(2)之使用教程(1)介绍
  • 【分析学】 从闭区间套定理出发(二) -- 闭区间连续函数性质
  • 【WRF-Urban数据集】 WRF 模型城市冠层参数 GloUCP 的使用
  • 1 Studying《Computer Vision: Algorithms and Applications 2nd Edition》11-15
  • 【修电脑的小记录】连不上网
  • 强制IDEA始终使用Java 8
  • (24)如何在 Qt 里创建 c++ 类,以前已经学习过如何在 Qt 里引入资源图片文件。以及如何为继承于 Qt已有类的自定义类重新实现虚函数
  • Java面试宝典:基础二
  • 进阶向:Django入门,从零开始构建一个Web应用
  • 面试准备first
  • 【C++11】异常
  • [Linux入门] Linux LVM与磁盘配额入门指南
  • Tomcat 安装使用教程
  • 大数据Hadoop之——安装部署hadoop
  • 深入剖析Nacos服务发现与注册,及如何基于LoadBalancer实现负载均衡
  • 大事件项目记录13-接口开发-补充