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
- 箭头函数没有原型
- 箭头函数不能作用构造函数使用
应用场景
- 临时性使用的函数,并不会可以调用它,比如:
- 事件处理函数
- 异步处理函数
- 其他临时性的函数
- 为了绑定外层this的函数
- 在不影响其他代码的情况下,保持代码的简洁,最常见的,数组方法中的回调函数
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出现之前,我们使用的是对象的方式来存储键值对,键是属性名,值是属性值。
使用对象存储有以下问题:
- 键名只能是字符串
- 获取数据的数量不方便
- 键名容易跟原型上的名称冲突
- 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的问题
- 所有的功能全部集中在同一个对象上,容易书写出混乱不易维护的代码
- 采用传统的事件驱动模式,无法适配新的 Promise Api
Fetch Api 的特点
- 并非取代 AJAX,而是对 AJAX 传统 API 的改进
- 精细的功能分割:头部信息、请求信息、响应信息等均分布到不同的对象,更利于处理各种复杂的 AJAX 场景
- 使用 Promise Api,更利于异步代码的书写
- Fetch Api 并非 ES6 的内容,属于 HTML5 新增的 Web Api
- 需要掌握网络通信的知识
请求配置对象
- 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 的响应。
迭代器
背景知识
- 什么是迭代?
从一个数据集合中按照一定的顺序,不断取出数据的过程
- 迭代和遍历的区别?
迭代强调的是依次取数据,并不保证取多少,也不保证把所有的数据取完
遍历强调的是要把整个数据依次全部取出
- 迭代器
对迭代过程的封装,在不同的语言中有不同的表现形式,通常为对象
- 迭代模式
一种设计模式,用于统一迭代过程,并规范了迭代器规格:
- 迭代器应该具有得到下一个数据的能力
- 迭代器应该具有判断是否还有后续数据的能力
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
*/
反射与代理
反射
- Reflect是什么?
Reflect是一个内置的JS对象,它提供了一系列方法,可以让开发者通过调用这些方法,访问一些JS底层功能
由于它类似于其他语言的反射,因此取名为Reflect
- 它可以做什么?
使用Reflect可以实现诸如 属性的赋值与取值、调用普通函数、调用构造函数、判断属性是否存在与对象中等功能
- 它里面到底提供了哪些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