文章目录
ES6是一个泛指,含义是5.1版本之后的js版本,涵盖了ES2015,ES2016,ES2017等。
0、es7 新增的两个特性
//** 幂指数
console.log(3**4,2**6,Math.pow(3,4)); //81 64 81
var arr = [1,2,3,4,5];
console.log(arr.includes(6)); //false
//.includes() 判断一个数组是否包含一个指定的值,另外弥补indexOf找不到NaN的bug
var arr = ['hello'/2,NaN];
console.log(arr.indexOf('hello'/2),arr.indexOf(NaN)); //-1 -1
console.log(arr.includes('hello'/2),arr.includes(NaN)); //true true
1、定义变量: var、let、 const
变量声明方式 | 作用域 | 重复声明 | 声明变量时必须初始化(赋值) | 变量提升 |
---|---|---|---|---|
var(es5) | 函数级作用域 | √ | × | √ |
let (es6) | 块级作用域 | × | × | × |
const (es6) | 块级作用域 | × | √ | × |
var特点:
1.会变量提升,不存在暂时性死区
2.可以重复声明变量
3.不存在块级作用域 if() for()
let特点:
1.不可以变量提升存在暂时性死区(在声明之前无法访问)
2.可以在声明变量同时不进行赋值
3.使用let不可以重复声明变量,但是可以重新赋值
4.使用let声明变量存在块级作用域
//1.
// console.log(a); //报错
// let a = 1;
// 2.
// let a;//var a;
// console.log(a); //undefined
// 3.
// let a = 'hello';
// let a = 10;
// console.log(a) //报错,重复声明
//
// let b = 'hello';
// b = 10;
// console.log(b) //10
// 4.
// if (true) {
// let a = 1;
// }
// console.log(a); //报错
// for (let i = 0; i < 5; i++) {
// console.log(i) //0 1 2 3 4
// }
// console.log(i, '外部打印') //报错
const特点:
1.使用const不可以重复声明变量
2.不会变量提升存在暂时性死区
3.必须在声明得时候进行初始化
4.使用const声明 基本数据类型值 、 引用数据类型值 一旦声明不可以修改, 但声明 引用数据类型属性 是可以修改删除
5.存在块级作用域
6.一般用于声明常量
// 1.
// const a = 'hello';
// const a = 'world'; //报错,因为 1
// 2.
// console.log(a);
// const a = 10; //报错,2
// 3.
// const a; //报错 ,3
// 4.
// const a = BigInt(2n);
// a ='world';
// console.log(a); //报错, 4的基本数据类型
//
// const obj = {
// name: 'zhangsan',
// age: 12
// }
// obj.name = 'lisi';
// delete obj.age;
// // obj = 'hello'; //加上它就报错,因为不能改值,只能改属性
// console.log(obj) //{ name: 'lisi' } ,4的引用数据类型
//5.
// if(true){
// const a = '10'
// }
// console.log(a) //报错
2、包管理机制(npm ,cnpm ,yarn)
- npm :node软件自带npm包管理工具 ,安装依赖速度慢(服务器部署在国外,受网络波动较大)
- cnpm:中国版的npm,淘宝团队优化npm速度 旧的仓库源:taobao.org 新的仓库源npmmirror.com 16以上
- yarn:下载依赖会先从离线缓存中寻找 ,找不到则下载
3、降版本代码 babel********
babel:将高版本代码(es6及以上)转为低版本代码(es5)
- 将文件夹初始化为项目:
- npm init
- npm init -y (快速初始化(自动一路yes))
- 安装转码工具
- cnpm install -g babel-cli (全局安装转码工具)
- babel --version (检测Babel版本,及是否安装)
- cnpm install --save -dev babel-cli babel-preset-latest (安装预设局部安装)
- 安装转换规则
- cnpm install -g babel-preset-latest
- 指定转换规则
- 新建.babelrc文件,输入 { “presets”:[“latest”] }
- cnpm install --save-dev babel-cli babel-preset-latest
- babel xxx.js(对文件进行转码)
- 文件高转低后输入到另一个文件(文件会自动新建)
- babel xxx.js --out-file xxx.js
- 整个目录高转低后的输入到另一个目录(目录会自动新建)
- babel xxx --out-dir xxx
node xxx (指运行该文件)
es6:
let a =1;
let foo=()=>{
console.log('我是箭头函数');
}
转为ES5后:
'use strict';//严格模式 语法进行限制(转es5后自带)
var a = 1;
var foo = function foo() {
console.log('我是箭头函数');
};
// b=1;//严格模式下,必须var声明变量,否则不符合严格模式,报错
// console.log(b);
4、模块化机制********
package.json 文件中 默认nodejs采用模块化规范为commonjs
可以设置模块化规范为es6模块化规范 “type”:“module/commonjs(默认)” 导入变量的文件后缀名要加上
4.1 ES6模块化
-
导出
1.列表导出:export {first,last}
2.重命名导出:export {first as fi,last as la};
3.导出单属性:export var a=3; / export function get(){}
4.默认导出: export default { first, function get() {} } -
导入
1.列表导入 import {first,last} from ‘xx.js’
2.重命名导入 import {first as f,last as l};
3.单个属性导入 import {a,get} from ‘xxx.js’
4.默认导入需要重命名
import person from ‘xxx.js’ (person ---->默认导出内容)
5.引入模块中所有内容 import * as all from ‘xxx.js’ (all为随便名字)
导出
let firstName = 'zhao';
let lastName='larry';
//1.列表导出'
// export{firstName,lastName};
//2.重命名导出
// export{firstName as first,lastName as last};
//3.导出单个属性
// export let a=1;
// export function foo(){
// console.log('我是foo函数');
// }
//4.默认导出 一个模块只能有一个默认导出,不能使用 var、let 或 const
// export default{
// obj:{
// name:"zhangsan",
// age:12
// },
// b:'我是字符串b'
// }
导入
//1'列表导入 es6编译时加载
// import {firstName,lastName } from "./1-ES6模块导出";
// console.log(firstName,lastName);
// 2. 列表导入的时候导入新的变量名
// import {first,last} from"./1-ES6模块化导出";
// console.log(first,last);
//2.重命名导入 first last
// import {first as f,last as l}from "./1-ES6模块导出.js";
// console.log(f,l,'重命名导入');
//3.导入单个属性
// import {a,foo} from './1-ES6模块导出.js'
// console.log(a,foo()); //我是foo函数 1 undefined 函数会提升,所以先输入函数
//4.默认导入
// import person from './1-ES6模块导出.js'
// console.log(person); //{ obj: { name: 'zhangsan', age: 12 }, b: '我是字符串b' }
//5.引入文件中所有导出的内容
import * as obj from './1-ES6模块导出.js'
console.log(obj);
4.2 commonjs模块化
即es6之前的模块化
-
导出
module.exports.first = ‘zhao’;
module.exports = { first,last,…} -
导入
let xxx = require( ‘xx.js’ );
导出
// console.log(module,'当前模块','node内部提供了Moudle构造函数');
// var module=new Module();//module代表当前模块
let first='ren';
let last='terry';
//向外输出对象
// module.exports.first=first;
// module.exports.last=last;
module.exports={
first,
last
}
导入
//commonjs模块化导入
// let{first,last}=require('./3-commonjs模块化导出');
// console.log(first,last);
let _ =require('./3-commonjs模块化导出');
console.log(_);
4.3 ES6与CommonJS模块的差异
- ES6在编译时加载模块,Commonjs在运行时加载模块
- ES6导出使用export ;导入使用import导入
Commonjs导出使用当前模块 module.exports ;导入使用require()- ES6模块化输出的是值得引用,Commonjs输出的是值的复制
ES6模块化
//导出
let first='zhao';
let last='larry';
export{first,last};
setTimeout(function(){
first='ren';
},2000);
//导入
import {first,last} from "./5-ES6模块化导出差异";
console.log(first,last,'导入变量'); //zhao larry 导入变量
setTimeout(function(){
console.log(first,last);
},4000); //ren larry
CommonJS模块化
//导出
let first='ren';
let last='terry';
module.exports={
first,last
}
setTimeout(function(){
first='zhao';
},2000)
//导入
let _=require('./7-commonjs模块化导出差异');
console.log(_.first,_.last); //ren terry
setTimeout(function(){
console.log(_.first,_.last);
},4000) //ren terry
5、解构********
- 概念:从数组或者对象中提取值,给变量进行赋值操作就是解构 。
- 本质属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。如果解构不成功,变量的值就等于undefined。
5.1 数组解构
1.完全解构
// let [a,b,c,d,e] = [1,2,3,4,5];
// console.log(a,b,c,d,e); //1 2 3 4 5
//let [a,b,c,d,e] = [1,2,3,[4,5,6],7];
// console.log(a,b,c,d,e); //1 2 3 [4,5,6] 7
2.不完全解构
// let [a,b,c,[d],e] = [1,2,3,[4,5,6],7];
// console.log(a,b,c,d,e); //1 2 3 4 7
3.集合解构 拓展运算符 (将剩余未匹配的集合到一起)
// let [a,...b] = [1,2,3,4,5];
// console.log(a,b); //1 [2,3,4,5]
4.默认值解构 当没有与变量匹配的值默认值就生效
// let [a=4,b=5,c=6] = [1,2,3];
// console.log(a,b,c); //1 2 3
// let [a=4,b=5,c=6] = [];
// console.log(a,b,c); //4 5 6
5.默认值也可以是函数
// function foo(){ console.log('我是foo函数'); }
// let [a=foo()] = [1,2,3];
// console.log(a); //1
// let [a=foo()] = [];
// console.log(a); //我是foo函数
6.虽然两者内容相同,但引用地址不同
// let arr = [1,2,3,4,5];
// let [...a] = arr;
// console.log(a===arr); //false
5.1.1 拓展运算符*********
用于解构数组和对象,并返回解构后的新数组或者新对象
1.用到左侧是聚合
// var arr = [1,2,3,4,5];
// let [...res] = arr;
// console.log(res); //[1,2,3,4,5]
// console.log(res===arr); //false(引用地址不同,(深拷贝))
2.用到右侧是展开
// let obj={name:'zhangsan',age:12};
// let obj1={...obj};
// console.log(obj1); //{name:'zhangsan',age:12}
// console.log(obj1===obj); //false
var params = {page:1 , pageSize:10}
var form = {title:"" , type:"" , status:""}
let temp = {...params , ...form}
for(let key in temp){
if(!temp[key]){
delete temp[key]
}
}
console.log(temp); //{ page: 1, pageSize: 10 }
5.2 对象解构
左侧变量名必须和右侧属性名同名,右边对象中对应的属性名才会解构出来
1.
// let {foo:foo,bar:bar} = {foo:'hello',bar:'world'};
// let {foo,bar} = {foo:'hello',bar:'world'};
// console.log(foo,bar); //hello world
2.重命名解构 ‘ :’ 对变量名进行重命名
// let {foo:baz} = {foo:'hello',bar:'world'};
// console.log(baz) //hello
3.嵌套解构 ----使用ab变量接收hello world
// let obj={p:['hello',{y:"world"}]};
// let {p:[a,{y:b}]} = obj;
// console.log(a,b); //hello world
4.默认值 给对象变量设置默认值
// let {x:y=2} = {x:1};
// console.log(y); //1
// let {x:y=2}={};
// console.log(y); //2
经典面试题:
const [a, b, c, ...d] = [1, 2, 3, 11, 999];
const { e, f, f1, g, ...h } = { f: 4, g: 5, i: 6, j: 7 };
console.log(a, b, c, d, e, f1, g, h);
//1 2 3 [11,999] undefined undefined 5 {i:6 j:7}
5.3 字符串、数值、布尔解构
字符串解构
1.使用数组解构可以获取指定字符;
//let [a,b,c]='hello';
//console.log(a,b,c); //h e l
2.使用集合解构可以转换为数组
//let [...arr]='hello';
//console.log(arr); //['h','e','l','l','o']
3.使用对象解构可以获取实例属性方法
//let {toString,valueOf,length}='hello'; //相当于把‘hello’当成String基本包装器类型
//console.log(toString,valueOf,length) //[Function: toString] [Function: valueOf] 5
数值解构 可以获取到数值包装器构造函数原型中指定的方法。
//let {toString,valueOf}=10;
//console.log(toString,valueOf) //[Function: toString] [Function: valueOf]
布尔值解构
//let {toString,valueOf}=true;
//console.log(toString,valueOf); //[Function: toString] [Function: valueOf]
5.4 函数参数解构
1.1 参数为对象的解构
// function foo({name,age,gender}){
// console.log(name,age,gender)
// }
// foo({name:'zhangsan',age:12,gender:'male'}); //zhangsan 12 male
1.2 参数为对象默认值的解构
// function foo({name,age=18,...rest}){
// console.log(name,age,rest);
// }
// foo({name:'lisi',age:12,gender:'male',weight:'40kg'}) //lisi 12 { gender: 'male', weight: '40kg' }
2.参数为数组的解构
// function foo([a,b,c]){
// console.log(a,b,c);
// }
// foo(['hello','terry','tom']) //hello terry tom
3. 尾参数
es6允许为函数的参数设置默认值 设置了默认值的参数自动成为函数尾参数
length属性将返回不包括 尾参数及其之后 的个数
function foo(a,d,e,f,b=1,c){
}
foo(1,2,3,4,5,6);
console.log(foo.length);//4
6、箭头函数********
6.1 对象简写
- 属性简写:属性名和变量名(属性值)一致时可简写为属性名
- 方法简写:省略 :function,即sayName () { }
let name = 'terry';
let age = 12;
let obj = {
//属性简写
//name:name,
//age:age,
name,
age,
//方法简写
// es5
// sayName:function(){
// console.log(this.name);//此时this指向obj
// }
// es6
sayName(){
console.log(this.name);//此时this指向obj
}
}
obj.sayName()();
6.2 箭头函数
- 主要目的:
普通函数得关心this的指向,修改等
而箭头函数不用担心,因为this仅指向外部作用域的this指向
普通函数 | 箭头函数 |
---|---|
var foo = function () {} | var foo = () => {} |
this指向全局对象,对象中方法指向调用者 | 无this属性,this指向声明该箭头函数外部作用域的this |
有原型对象 | 无原型对象 |
使用arguments保存实参 | 使用rest参数保存实参(本质上就是用拓展运算符来保存剩余参数到数组中) |
//普通函数this指向,保存实参
function test(){
console.log(this,arguments);
}
test(1,2,3);// 全局对象 [Arguments] { '0': 1, '1': 2, '2': 3 }
//箭头函数this指向,保存实参
// 箭头函数内部没有this属性 不再用arguments属性保存实际参数 用rest参数保存实际参数
let foo = (...res)=>{
// console.log(this,arguments,'arguments') //argument打印出来是类数组对象 [arguments]{...},指向模块中的一些其他信息
console.log(res); //[1,2,3]
}
foo(1,2,3)
// 普通函数有原型对象
// 箭头函数没有原型对象
function bar(){}
console.log(bar.prototype,foo.prototype); //{} undefined
console.log(bar.prototype.toString(),foo.prototype); //[object Object] undefined
箭头函数和普通函数区别?
1.普通函数内部this指向全局对象,方法指向调用者
2.箭头函数没有this,this访问声明箭头函数外部作用域中的this
3.普通函数使用arguments保存实际参数,箭头函数使用rest参数保存实际参数
4.普通函数有原型对象,箭头函数没有原型对象
5.外观上 ()=>{}
箭头函数的this指向:
let name = 'terry';
let age = 12;
//(4)let sayName = () => { console.log(this.name, this);} //undeifned {}
(5)module.exports.name = 'lisi';
(5)let sayName = () => { console.log(this.name, this);} //lisi {name:'lisi}
let obj = {
name,
age,
(1)es6 //sayName(){console.log(this.name)} //terry this指向obj
(2)转为箭头 //sayName:()=>{console.log(this)} //{} 指向声明该箭头函数(此处为obj)外部作用域this
(3)变形 // sayName(){
// console.log(this) //obj
// return ()=>{
// console.log(this.name) //terry
// }}
(4)(5)变形 sayName() { return sayName }
}
obj.sayName()();//调用return用两个()
console.log(this===module.exports) //true module表示当前模块,exports表示空对象
7、类 class********
ES6的类可以看成是 构造函数的另一种写法
es5—> 使用构造函数 --------------->生产实例对象
es6—> 通过class定义一个类 ----->生产实例对象
- 类默认会提供一个空的构造器 constructor(){}
- 写在构造器中属性和方法是实例私有属性和方法
- 写在类体中得方法是实例公共方法,写在类体中得属性是是实例私有属性
- 使用static关键字声明类得方法和属性是静态方法和属性 (只能由类本身访问)
class Person {
//类默认会提供一个空的构造器 constructor(){}
//但满足不了开发需求,所以要建一个(用于创建实例的同时,可以设置实例属性和方法)。没有构造器就创不了实例
constructor(name, age, gender) {
// 写在构造器中属性和方法是实例私有属性和方法
this.name = name;
this.age = age;
this.gender = gender;
this.text = xxx; 相当于18行代码
this.friends=xxx; 相当于19行代码
}
// 写在类体中得方法是实例公共方法 相当于写在原型对象中 Person.prototype
sayName() {
console.log(this.name)
}
// 写在类体中得属性是是实例私有属性
test = 'hello';
friends = [];
// 使用static关键字声明类得方法和属性 静态方法和静态属性 只能由类本身访问
static attr = 'Person类静态属性';
static method = function(){console.log('Person类静态方法');}
}
let p1 = new Person('terry',18,'male');
let p2 = new Person('larry',20,'female');
p1.sayName(); //terry
p2.sayName(); //larry
console.log(p1.sayName === p2.sayName); //true
p1.friends.push('tom');
console.log(p1.friends === p2.friends); //false
console.log(p1.test === p2.test); //true 还没被修改,但是hello
p1.test='我被修改了';
console.log(p1.test === p2.test); //false 修改之后,一个我被修改了,一个hello
8、继承 extends********
class可以通过extends关键字实现继承
/**
* ES6继承 分两类:
* 1.子类继承父类
* 2.子类原型对象继承父类原型对象
*/
class Animal{
constructor(type,weight,length){
this.type = type;
this.weight = weight;
this.length = length;
}
sayType(){
console.log(this.type,'这是animal实例公共方法')
}
static AnimalAttr = 'Animal静态属性';
static AnimalMethod = function(){ console.log('这是Animal静态方法') }
}
// 子类使用extends关键字实现对父类得继承
class Dog extends Animal{
// Dog类得内容可以为空,仅继承父的东西。但想设置子类自己的东西就得提供构造器来设置
// 如果子类提供了构造器必须显示调用super函数
constructor(type,weight,length,name,color){
super(type,weight,length);//类似于Animal.call()
this.name = name;
this.color = color;
};
}
let d1 = new Dog('狗','40kg','20cm','可乐','白色');
console.log(d1); //Dog { type: '狗', weight: '40kg', length: '20cm', name: '可乐', color: '白色'}
d1.sayType(); //狗 这是animal实例公共方法
console.log(Dog.AnimalAttr); //Animal静态属性
console.log(Dog.AnimalMethod()); //这是Animal静态方法
// 子类对父类继承 (子类有一个指针指向父类)
console.log(Dog.__proto__ === Animal); //true
// 子类原型对象继承父类得原型对象(子类的原型对象有一个指针指向父类的原型对象)
console.log(Dog.prototype.__proto__ === Animal.prototype); //true
console.log(d1.constructor); //[class Dog extends Animal]
es5继承方式:
/**
* 1.借用构造函数继承 / 经典继承
* 2.原型链继承
* 3.组合继承
*/
function Animal(type,weight,length){
this.type = type;
this.weight = weight;
this.length = length;
}
// 1.借用构造函数继承
function Dog(type,weight,length,name,color){
Animal.call(this,type,weight,length); //.call修改this指向
this.name = name;
this.color = color;
}
let d1 = new Dog('狗','40kg','20cm','可乐','白色');
console.log(d1);
// 2.原型链继承 将子构造函数原型指向父构造函数实例
// Animal.prototype = {
// constructor:Animal, // 将animal原型对象构造者改为animal
// sayType(){
// console.log(this.type)
// }
// }
// Dog.prototype = new Animal(); //Animal {}
// Dog.prototype.constructor = Dog;
// let d1 = new Dog('狗','40kg','20cm','可乐','白色');
// console.log(d1);
// d1.sayType();
9、模板字符串 ${ }********
从字符串中提取变量
let params = {
page:1,
pageSize:10
};
// let url = 'http://121.199.0.35:8888/index/pageQuery?page=1&pageSize=10';
// 模板字符串
let url = `http://121.199.0.35:8888/index/pageQuery?page=${params.page}&pageSize=${params.pageSize}`;
console.log(url)
10、迭代器 for…of 遍历********
迭代器是一种机制 是一种接口 。
只要数据结构实现了迭代器接口 就可以使用for …of遍历
作用:
- 为各种数据结构,提供一个统一的、简便的访问接口
- 使得数据结构的成员能够按某种次序排列
- ES6创造了一种新的遍历命令for…of循环,Iterator接口主要供for…of消费
检验是否为迭代器,用for-of遍历或输出let map=new map()括号里的值看结果:
- 有结果表示是迭代器
- 报错:Iterator value h is not an entry object表明是迭代器,但报错是因为其他
- 报错:xxx is not iterable表明不是迭代器。
遍历字符串:
- 1.for 循环
- 2.for in循环
- 3.split(‘’)
- 4.Array.from()
- 5.Array.prototype.slice.call(str,0)
- 6.[…res] = str;
- 7.for of循环遍历字符串
let str = 'hello';
for(let i=0;i<str.length;i++){
console.log(i,str[i]); //0 h 1 e 2 l 3 l 4 o
}
for(let key in str){
console.log(key,str[key]); //0 h 1 e 2 l 3 l 4 o
}
for(let key of str){
console.log(key); //h e l l o
}
迭代器本质:就是迭代器对象调用得next方法 迭代器对象创建一个指针 指向数据结构首位成员位置,第一次调用指向第一个成员 依次调用依次指向后面成员
let arr = ['terry','larry','ronda','jacky','briup'];
let keys = arr.keys();//返回值是迭代器对象 实现了迭代器接口 for ...of遍历
let values = arr.values();
let entries = arr.entries();
console.log(keys,values,entries); //Object [Array Iterator] {} Object [Array Iterator] {} Object [Array Iterator] {}
//迭代器本质就是迭代器对象调用得next方法 迭代器对象创建一个指针 指向数据结构首位成员位置
//第一次调用指向第一个成员 依次调用依次指向后面成员
// console.log(keys.next()); //{ value: 0, done: false } false表示不是最后一个成员
// console.log(keys.next()); //{ value: 1, done: false }
// console.log(keys.next()); //{ value: 2, done: false }
// console.log(keys.next()); //{ value: 3, done: false }
// console.log(keys.next()); //{ value: 4, done: false }
// console.log(keys.next()); //{ value: undefined, done: true }
// console.log(keys.next()); //{ value: undefined, done: true }
// console.log(values.next()); //{ value: 'terry', done: false }
// console.log(entries.next()); //{ value: [ 0, 'terry' ], done: false }
//自动调成员
// let result;
// while(!(result=keys.next()).done){
// console.log(result) //{ value: 0, done: false }......{ value: 4, done: false }
// }
for(let key of keys){
console.log(key); //0 1 2 3 4
}
for(let value of values){
console.log(value); //terry larry ronda jacky briup
}
for(let entry of entries){
console.log(entry); //[ 0, 'terry' ]......[ 4, 'briup' ]
}
function foo(){
for(let key of arguments){
console.log(key); //1 2 3 4
}}
foo(1,2,3,4)
let arr1 = [1,2,3,4];
for(let key of arr1){
console.log(key); //1 2 3 4
}
let obj = {name:'zhangsan', age:12,gender:'male'}
for(let key of obj){
console.log(key); //报错 obj is not iterable
}
10.1、forEach、for in、for of三者区别
forEach更多的用来遍历 :数组、map、set
for in 一般常用来遍历:对象或数组、字符串、类数组对象
for of遍历实现了迭代器接口的对象,遍历对象需要通过Object.keys()、values()、entries()
11、 新增方法********
11.1、对象
静态方法(构造函数本身访问):
- Object.is(a,b) 判断ab是否相等 (外观上)
- Object.assign(a,b) 两个参数实现对象复制、拷贝 。b复制给a,并返回a
- Object.assign(a,b,c) 三个参数代表的是合并对象,合并abc到a中,并返回a
- Object.getPrototypeOf() 获取原型对象中的方法
- Object.setPrototypeOf(a,b); 设置原型对象中的方法 (将a原型对象设置为b)
- Object.keys(a) 获取a的属性名组成数组
- Object.values(a) 获取b属性值组成数组
- Object.entries(a) 获取a属性名和属性值组成的二维数组
- Object.fromEntries( Object.entries(a) ) 将对象属性名和属性值组成二维数组转为对象
// 1.is 判断两个值是否相等 (外观上)
console.log(Object.is(1,1)); //true
console.log(Object.is(+0,-0)); //false 符号位也被进行判断
console.log(+0 === -0); //true
console.log(Object.is(NaN,NaN)); //true
console.log(NaN === NaN); //false
//2.1 Object.assign(a,b) 将b复制给a
let o = {};
let obj = { name:'zhangsan',age:12,
//对基本数据类型实现深拷贝 (对引用数据类型实现浅拷贝--半深拷贝)
clazz:{ no:'2023' }};
let res = Object.assign(o,obj);//将obj对象复制给o对象 同时返回o对象
console.log(res,res===o,res===obj); //{ name: 'zhangsan', age: 12, clazz: { no: '2023' } } true false
o.clazz.no = '2024';
console.log(o,obj); //{ name: 'zhangsan', age: 12, clazz: { no: '2024' } } { name: 'zhangsan', age: 12, clazz: { no: '2024' } }
//2.2 Object.assign(a,b,c) 将abc合并
let o = {name:'zhangsan'};
let obj = {age:12};
let obj1 = {gender:'male'};
let res = Object.assign(o,obj,obj1);//返回第一个参数
console.log(res,res===o);//{ name: 'zhangsan', age: 12, gender: 'male' } true
//3. Object.getPrototypeOf()
let obj = {
name:"zhangsan",
age:12
}
// 获取原型对象 getPrototypeOf
console.log(obj.__proto__); //[Object: null prototype] {}
console.log(obj.constructor.prototype); //[Object: null prototype] {}
console.log(Object.getPrototypeOf(obj)); //[Object: null prototype] {}
//4. setPropertypeOf(a,b)
let obj = {};
let obj1 = {name:'zhangsan',age:12};
Object.setPrototypeOf(obj,obj1);//将obj原型对象设置为obj1
console.log(obj.__proto__)//{ name: 'zhangsan', age: 12 }
console.log(obj.constructor.prototype);//[Object: null prototype] {} (注意 获取不到新原型对象)
console.log(Object.getPrototypeOf(obj));//{ name: 'zhangsan', age: 12 }
/**
* keys values entries
* keys 获取对象属性名组成数组
* values 获取对象属性值组成数组
* entries 获取对象属性名和属性值组成得数组
* fromEntries将对象属性名和属性值组成二维数组转为对象
*/
let obj = {
name:'zhangsan',
age:12,
gender:'male'
}
console.log(Object.keys(obj)); //[ 'name', 'age', 'gender' ]
console.log(Object.values(obj)); //[ 'zhangsan', 12, 'male' ]
console.log(Object.entries(obj)); //[ [ 'name', 'zhangsan' ], [ 'age', 12 ], [ 'gender', 'male' ] ]
console.log(Object.fromEntries(Object.entries(obj))); //{ name: 'zhangsan', age: 12, gender: 'male' }
11.2、数组
静态方法(构造函数本身访问):
- Array.from() 将类数组转换为数组
- Array.of() 创建数组实例
实例方法(原型中方法):
- .find() 返回第一个满足条件的数组元素或者undefined 。参数:回调函数
- .findIndex() 返回第一个满足条件的数组元素索引或者-1 。参数:回调函数
- .includes() 检测元素是否存在数组中
- .fill() 填充数组 会修改原数组
- .keys() 返回得是实现了迭代器接口对象 iterator。 加 .next()后获取的是索引
- .values() 返回得是实现了迭代器接口对象 iterator 。加 .next()后获取的是元素
- .entries() 返回得是实现了迭代器接口对象 iterator 。加 .next()后获取的是索引和元素
- .flat() 扁平化数组 数组层级展开
- .flatMap() 将数组展开 每个数组元素*2
// 1.将类数组对象转为数组对象 from
function foo(){
console.log(arguments,'类数组对象'); //[Arguments] { '0': 1, '1': 2, '2': 3, '3': 4 } 类数组对象
console.log(Array.from(arguments)); //[ 1, 2, 3, 4 ]
console.log([...arguments]); //[ 1, 2, 3, 4 ]
console.log(Array.prototype.slice.call(arguments,0)) //[ 1, 2, 3, 4 ]
}
foo(1,2,3,4)
// 2.of 创建数组实例
let arr1 = new Array(10); //[ 10 ]
let arr = Array.of(10); // [ <10 empty items> ]
console.log(arr,arr1,arr instanceof Array); //true
// 3.find 返回第一个满足条件的数组元素或者undefined 参数:回调函数
let arr = [1,2,3,4,5];
let res = arr.find(function(item,index,arr){
return item>3
});
console.log(res); //4
// 4.findIndex 返回第一个满足条件的数组元素索引或者-1 参数:回调函数
let arr = [1,2,3,4,5];
let res = arr.findIndex((item,index,arr)=>{
console.log(item,index,arr);
return item>5
});
console.log(res,arr);
// 1 0 [ 1, 2, 3, 4, 5 ]
// 2 1 [ 1, 2, 3, 4, 5 ]
// 3 2 [ 1, 2, 3, 4, 5 ]
// 4 3 [ 1, 2, 3, 4, 5 ]
// 5 4 [ 1, 2, 3, 4, 5 ]
// -1 [ 1, 2, 3, 4, 5 ]
// includes 检测数组元素是否存在数组中 存在返回true 不存在返回false
let arr = [1,2,3,4,('hello'/2)];
console.log(arr.includes('2')); //false 针对于NaN优化
console.log(arr.includes(('hello'/2))); //true
console.log(arr.indexOf(('hello'/2))); //-1
// fill 填充数组 修改原数组
let arr = [1,2,3,4];
let res = arr.fill(8);
console.log(arr,res); //[ 8, 8, 8, 8 ] [ 8, 8, 8, 8 ]
// keys values entries 返回得是实现了迭代器接口对象 iterator
let arr = [1,2,3,4,5];
console.log(arr.keys()); //Object [Array Iterator] {}
console.log(arr.values()); //Object [Array Iterator] {}
console.log(arr.entries()); //Object [Array Iterator] {}
console.log(arr.keys().next()); //{ value: 0, done: false }
console.log(arr.values().next()); //{ value: 1, done: false }
console.log(arr.entries().next()); //{ value: [ 0, 1 ], done: false }
// flat方法 扁平化数组 数组层级展开
let arr = [1,2,[3,4,5,[6,7,8,[9,10,[11]]]]];
console.log(arr.flat(1));//[ 1, 2, 3, 4, 5, [ 6, 7, 8, [ 9, 10, [Array] ] ] ]
console.log(arr.flat(Infinity));
// [
// 1, 2, 3, 4, 5,
// 6, 7, 8, 9, 10,
// 11
// ]
// flatMap flat和map方法结合 需求 将数组展开 每个数组元素*2
let arr = [1,2,[3,4]];
let res = arr.flatMap((item,index,arr)=>{
// console.log(item,index,arr)
if(typeof item=='number'){
return item * 2
}else{
return item.map((it)=>{return it*2})
}
})
console.log(res); //[ 2, 4, 6, 8 ]
11.3、数字
- .isFinite () 是否为有效值,会隐式转换
- Number.isFinite () 是否为有效值,不会隐式转换
- .isNaN () 数字返回false,其他返回true
- Number.isNaN() 非NaN返回false,其他返回true
- Number.parseInt() 返回整数
- Number.parseFloat() 返回小数
- Number.isInteger() 判断是否为整数
// isFinite 检测值是否是有效值 隐式转换
console.log(isFinite(null)); //true
console.log(isFinite("")); //true
console.log(isFinite('10')); //true
console.log(isFinite(true)); //true
console.log(isFinite([])); //true
console.log(isFinite('hello'/10)); //false
// Number.isFinite 检测有效值 对于有效值返回true 对于无效值返回false 不做隐式转换
console.log(Number.isFinite(null)); //false
console.log(Number.isFinite('')); //false
console.log(Number.isFinite('10')); //false
console.log(Number.isFinite(true)); //false
console.log(Number.isFinite(10)); //true
// isNaN 判断是否是一个数字 是数字返回false 其他返回true
console.log(isNaN(10)); //false
console.log(isNaN('hello'/10)); //true
console.log(isNaN('hello')); //true
// Number.isNaN 对于非NaN一律返回false 只有对NaN才返回true
console.log(Number.isNaN('hello'/10)); //true
console.log(Number.isNaN('hello')); //false 只对NaN返回true 其他都是false
console.log(isNaN('hello')); //true 只对数字返回false 其他都返回true
console.log(Number.isNaN(10)); //false
// parseInt parseFloat
console.log(Number.parseInt('10.1')); //10
console.log(Number.parseInt('10.123')); //10
console.log(Number.parseFloat('12.123')); //12.123
console.log(Number.parseFloat('12.123#')); //12.123
// 检测是否是整数 对于整数返回true 对于非整数返回false
console.log(Number.isInteger('12.1')); //false
console.log(Number.isInteger(12)); //true
console.log(Number.isInteger(12.1)); //false
console.log(Number.isInteger('12')); //false
11.4、字符串、函数
字符串:
- .trimStart(a) 去除a前面空格
- .trimEnd(a) 去除a后面空格
- .padStaart(num,[ ‘字符串’ ] ) 从头部添加字符串
- .padEnd(num,[ ‘字符串’ ] ) 从尾部添加字符串
函数:
- .toString() 将从头到尾返回源代码中的实际文本片段(包括注释,空格,语法详细信息)
// 单独去除字符串前后空格 trim
let str = ' hello world ';
console.log(str); // hello world
console.log(str.trim()); //hello world
// //单独去除前面空格 trimStart 别名trimLeft
console.log('2'+str.trimStart()+'2'); //2hello world 2
console.log('2'+str.trimLeft()+'2'); //2hello world 2
// // 单独去除后面空格 trimEnd 别名trimRight
console.log('2'+str.trimEnd()+'2') //2 hello world2
console.log('2'+str.trimRight()+'2') //2 hello world2
//pad..(填充后长度,填充字符(默认空格))
// padStart 从头部添加字符串
let str = 'es8';
console.log(str.padStart(4),'2'+str); // es8 2es8
console.log(str.padStart(4,'h')); //hes8
console.log(str.padStart(2)); //es8 填充长度小于原长度 ,返回源字符串
console.log(str.padStart(6,'ab')); //abaes8 填充长度大,就重复添加字符串
// padEnd 从尾部添加字符串
console.log(str.padEnd(4)+'2'); //es8 2
console.log(str.padEnd(4,'h')); //es8h
console.log(str.padEnd(2)); //es8
console.log(str.padEnd(6,'45')); //es8454
function foo(){
console.log('我是函数');
// 我是函数内的注释
return ;
}
console.log(foo.toString());// function foo(){
// console.log('我是函数');
// // 我是函数内的注释
// return ;
// }
let str = 'hello';
let reg = /l/g;//正则表达式/ /,g表示全部global
console.log(str.replace(reg,'L')); //heLLo
console.log(str.replaceAll('l','L')); //heLLo
12、新增数据类型********
基本数据类型
12.1、Symbol
var sy1 = Symbol();
表示独一无二的值,Symbol函数创建独一无二得值。
无论参数是什么数据类型,最终都会转为字符串
一般属性可以 obj.属性 来访问;symbol属性用 obj [ 属性 ] 来访问
symbol值创建的属性无法遍历
let sy1 = Symbol();//创建好一个独一无二得值
let sy2 = Symbol();
console.log(sy1,sy2) //Symbol() Symbol()
console.log(sy1 === sy2); //false
console.log(typeof sy1,sy1 instanceof Object); //symbol false
//symbol()中放参数表示:对独一无二值的描述
let sy1 = Symbol('symbol数据类型'); //Symbol("symbol数据类型")
let sy2 = Symbol({name:'zhangsan'}) //Symbol("[object Object]")
let sy3 = Symbol([1,2,3]); //Symbol("1,2,3")
let sy4 = Symbol(null); //Symbol("null")
console.log(sy1,sy2,sy3,sy4);
- Object.getOwnPropertySymbols(obj) 访问所有symbol创建的属性
- Symbol.for 创建一个symbol值的时候进行全局注册
- Symbol.keyFor 检测symbol值有没有在全局注册过 注册过返回symbol值得描述 没有返回undefined
Symbol的应用:
- 解决命名冲突
let sy1 = Symbol('name');
let sy2 = Symbol('age');
let obj = {
name:'zhangsan',
age:12,
[sy1]:'terry', //对象中的[]可以解析变量
[sy2]:18,
[Symbol('email')]:'xxx@briup.com'
};
console.log(obj[sy1]); //可以访问属性 terry
console.log(obj[sy2]); //18
console.log(obj[Symbol('email')]); //无效 undefined
//如何访问Symbol创建的属性?
console.log(Object.getOwnPropertySymbols(obj));//获取对象中所有symbol属性,访问symbol创建的所有属性
var ss = Object.getOwnPropertySymbols(obj);//[ Symbol(name), Symbol(age), Symbol(email) ]
console.log(obj[ss[0]],obj[ss[1]],obj[ss[2]]); //terry 18 xxx@briup.com
for(let key in obj){
console.log(key) // 遍历对象 使用symbol值创建属性无法遍历
}
- 消除 魔术字符串(非常依赖对字符串的命名,名字一错,代码就没效果)(与代码形成强耦合的某一个具体的字符串或者数值)
//魔术字符串:在代码之中多次出现、与代码形成强耦合的某一个具体的字符串或者数值。代码对字符串依赖性非常强 ,形成强耦合关系 形成了魔术字符串
function Area(shape,options){
let area = 0;
switch(shape){
case 'triangle':
area = options.width * options.height * 0.5;
break;
case 'square':
area = options*width * options.height;
break;
case 'circle':
area = Math.PI * options.r * options.r;
break;
default:
area = -1;
}
return area;
}
let res = Area('triangle',{width:100,height:100,r:50});
console.log(res);
//这个函数对字符串的依赖很大,若triangle打错,则出不来结果
//优化后:通过再封装一个对象,包含要求的那些
function Area(shape,options){
let area = 0;
switch(shape){
case Shape.SJX:
area = options.width * options.height * 0.5;
break;
case Shape.ZFX:
area = options.width * options.height;
break;
case Shape.CIRCLE:
area = Math.PI * options.r * options.r;
break;
default:
area = -1;
}
return area;
};
// 枚举:类型可以罗列出来 性别:男 女
let Shape = {
SJX:Symbol('三角形'),
ZFX:Symbol('正方形'),
CIRCLE:Symbol('圆')
}
let res = Area(Shape.ZFX,{width:100,height:100,r:50});
console.log(res);
3.全局注册表 symbol.for()
- 与Symbol() 不同的是,用 Symbol.for() 方法创建的的 symbol 会被放入一个全局 symbol 注册表中。
- Symbol.for() 并不是每次都会创建一个新的 symbol,它会首先检查给定的 key 是否已经在注册表中了。假如是,则会直接返回上次存储的那个。否则,它会再新建一个。
// 可以创建一个symbol值的时候进行全局注册
let sy1 = Symbol.for('name');
let sy2 = Symbol.for('name');
console.log(sy1 === sy2); //true
let sy3 = Symbol('age');
let sy4 = Symbol('age');
console.log(sy3 === sy4); //false
// Symbol.keyFor 检测symbol值有没有在全局注册过 注册过返回symbol值得描述 没有返回undefined
console.log(Symbol.keyFor(sy1)); //name
console.log(Symbol.keyFor(sy2)); //name
console.log(Symbol.keyFor(sy3)); //undefined
console.log(Symbol.keyFor(sy4)); //undefined
12.2、BigInt
引用数据类型
12.2、Set
var set=new Set();
类似数组,但成员值是唯一的,set会去重,没有重复的值(例:参数为hello,输出只有一个l)。
参数为:数组、具有迭代器接口的其他数据结构
set中对于引用数据类型,因其每个引用地址不同,所以即使外观相同,但也不会去重;若要删除,应为其赋值变量,从而通过删除变量来删除对应数组。
- set.add(参) 添加成员
- set.delete(参) 删除成员
- set.keys() 遍历
- set.values() 遍历
- set.entries() 遍历
- set.forEach(函数) 遍历
- set.size 返回set成员个数
- set.has(参) 判断set集合中有没有这个成员
- set.clear() 清空set成员
- set方法可以用于数组去重
// let set = new Set([1,2,3,4,3,2,1]); //Set(4) { 1, 2, 3, 4 }
// let set1 = new Set('hello'); //Set(4) { 'h', 'e', 'l', 'o' }因为成员只有1个,所以只有一个l
// let set2 = new Set({name:'zhangsan'}); //报错
let set = new Set();
// 添加成员 add
set.add('hello');
set.add(10); num类型的10可以添加,但不能作为Set()的参数
set.add('hello');
set.add([]); 引用数据类型在堆区中有自己的引用地址
set.add([]); 所以这两个[]不相同,因为引用地址不同
// console.log(set); //Set(4) { 'hello', 10, [], [] }
let arr = []; 删除之前的数组,要给他赋值一个变量
set.add(arr);
// console.log(set); //Set(5) { 'hello', 10, [], [], [] }
// 删除成员 delete
console.log(set.delete('hello')); //true
console.log(set.delete(arr)); //true
console.log(set); //Set(3) { 10, [], [] }
// keys values entries
console.log(set.keys()); //[Set Iterator] { 10, [], [] }
console.log(set.values()); //[Set Iterator] { 10, [], [] }
console.log(set.entries()); //[Set Entries] { [ 10, 10 ], [ [], [] ], [ [], [] ] }
for(let key of set.keys()){
console.log(key); //10 [] []
}
// forEach 遍历set集合
set.forEach((key,value)=>{
console.log(key,value) //10 10 [] [] [] []
})
// 返回set成员个数
console.log(set.size,'返回成员个数'); //3 返回成员个数
// 判断set集合中有没有这个成员
console.log(set.has(null)) //false
// 清空set成员
set.clear();
console.log(set) //Set(0) {}
// set应用 数组去重
let set1 = new Set([1,2,3,4,5,4,3,2,1]);
console.log(Array.from(set1)); // [ 1, 2, 3, 4, 5 ]
console.log([...set1]) // [ 1, 2, 3, 4, 5 ]
12.3、Map
let map=new Map();
类似于对象,也是键值对形式的数据结构,键可以是时任意数据类型,实现了迭代器接口
/ | Map | Object |
---|---|---|
键类型 | 任意数据类型 | 只能是string / symbol类型 |
键顺序 | 有序的,按照插入键得顺序依次返回 | 无序的,但是也保留字符串或者symbol值得插入顺序 |
键值对个数 | 通过size获取 | 手动计算 |
迭代 | 实现了迭代器接口,可以直接使用for…of遍历 | 没有实现迭代器接口,无法直接for…of遍历 |
性能 | 频繁增删键值对用map更好 | 频繁增删键值对在object中未作出优化 |
- map.set(参) 添加成员
- map.delete(参) 删除成员
- map.get(参) 获取成员键所对应的值
- map.size()
- map.keys() 获取键组成迭代器对象
- map.values() 获取值组成迭代器对象
- map.entries() 获取键和值组成迭代器对象
- map.forEach(函数) 遍历
- 应用:写购物车
// let obj = {
// name:'zhangsan',
// null:'hello',
// [Symbol('email')]:'xxxx.com'
// }
let obj = {
name:'zhangsan',
age:12
}
// console.log(Object.entries(obj)); //[ [ 'name','zhangsan' ],[ 'age', 12 ] ]将对象转成键值对
let map = new Map(Object.entries(obj));
// 添加成员 set
//map.set(1,1)
map.set({name:'map'},'hello');
let foo = function(){}
map.set(foo,null);
console.log(map); //Map(4) { 'name' => 'zhangsan', 'age' => 12, { name: 'map' } => 'hello', [Function: foo] => null}
// 删除成员
map.delete('name');
// map.delete(function(){});//这里也删不掉,同set原理一样,引用地址不同
map.delete(foo);
console.log(map); //Map(2) { 'age' => 12, { name: 'map' } => 'hello' }
// 获取成员键所对应的值
console.log(map.get('age')); //12
console.log(map.get(foo)); //undefined
console.log(map.size); //2
console.log(map.keys());//获取键组成迭代器对象 [Map Iterator] { 'age', { name: 'map' } }
console.log(map.values());//获取值组成迭代器对象 [Map Iterator] { 12, 'hello' }
console.log(map.entries());//获取键和值组成迭代器对象 [Map Entries] { [ 'age', 12 ], [ { name: 'map' }, 'hello' ] }
// // 遍历map
map.forEach((value,key)=>{
console.log(value,key) //12 age hello { name: 'map' }
})
//原型对象
console.log(Map.prototype,Set.prototype); //Object [Map] {} Object [Set] {}
12.4、promise(详情看13.3)
13、三个异步编程解决方案********
13.1、Promise
- 作用:封装ajax与axios
- 是一个承诺对象,存放着将来才会执行的代码(当给出它是成功还是失败后才会执行,所以不给它结果它就不会执行,从而达到控制代码执行时间的效果),
- 从语法的角度来说是一个对象,主要获取异步操作的消息
- (异步编程 同步解决)避免了层层嵌套的回调函数,可以链式调用降低操作难度。
- 成功执行then方法,失败执行catch方法
var xxx= new Promise( 回调函数(resolve,reject) )
- resolve----表示请求成功执行的回调函数–由then方法提供
- reject —表示请求失败执行的回调函数 —由catch方法提供
promise实例对象的三种状态:
- pending ---- 未操作/进行中
- fullfilled ---- 已成功
- rejected ---- 已失败
promise实例的两种方法:
- promise.then(): 当实例状态为fullfilled时候执行回调函数 resolve()
- promise.catch(): 当实例状态为rejected时候执行回调函数 reject()
<script>
console.log(Promise,'Promise构造函数'); //function Promise() Promise构造函数
let promise = new Promise((resolve, reject) => {
// 发起异步请求 if(res.status===200){ }else{ }
// 模拟异步请求
if (3 < 2) {
// // 异步请求成功 resolve 由then方法回调函数提供
resolve('请求成功')
} else {
// // 异步请求失败 reject 由catch方法回调函数提供
reject('请求失败')
}
});
// 顺序:这里调用reject,promise的实例就变成rejected失败,然后下面就会执行catch方法的这个回调函数
console.log(Promise, promise instanceof Object); //function Promise() true
// resolve函数是由then方法提供得回调函数----处理promise实例状态为fullfilled得回调函数
// reject函数 是由catch方法提供得回调函数
promise.then((res)=>{
console.log(res,'成功得回调') //请求成功,成功得回调
}).catch((error)=>{
console.log(error,'失败得回调')
}).finally(()=>{
console.log('finally最终执行') //finally最终执行(无论成功与否都会执行)
});
// // 也可以:then可以接收两个回调函数
// promise.then((res)=>{
// // 第一个回调函数表示promise实例状态为fullfilled执行回调函数
// console.log(res,'1111')
// },(error)=>{
// // 第二个回调函数表示promise实例状态为rejected执行回调函数----reject
// console.log(error,'2222')
// })
// console.log(Promise,'33333')
//调用之后还能继续调用(链式调用)的原因:因为调完一个方法之后输出的还是promise实例对象,还可以调用原型对象中的方法
// let result = promise.then(()=>{}).catch(()=>{}).finally(()=>{});
// console.log(result); //Promise { <state>:"pending" }
</script>
- 调用之后还能继续调用 (链式调用)的原因 :因为调完一个方法之后输出的还是promise实例对象,还可以调用原型对象中的方法
promise静态方法:
前4个参数为:[ 多个promise实例 ]或一些实现了迭代器接口的数据结构
- Promise.all() 得到一个promise实例对象,所有实例请求成功或者所有实例都为fullfilled 实例才是fullfilled
- Promise.any() 得到一个promise实例对象,任意一个实例请求成功或者实例变为fullfilled 实例就是fullfilled
- Promise.race() 哪个实例状态先变为fullfilled 就返回哪一个实例
- Promise.allSettled() 对每一个promise实例都进行处理
- Promise.resolve() 得到状态发生改变(fullfilled)promise实例
- Promise.reject() 得到状态发生改变(rejected)promise实例
- promise的应用:
- 封装ajax(如下代码) 或 axios(axios一般不用,因为可以使用async和awite处理 / 已经基于promise做过封装,不需要咱们做封装,直接调用axios.get/post,就能得到基于promise得axios请求)
通过封装工厂函数 得到一个基于promise得ajax请求
// 原生:一种方法得封装一个函数
// function getSwipe(){var xhr=new XMLHttpRequest();}
// function getSwipe(){var xhr=new XMLHttpRequest();}
function getPromise(method,url,data,params){
// data---post携带参数 params---get请求携带参数
return new Promise((resolve,reject)=>{
var xhr = new XMLHttpRequest();
xhr.open(method,url);
xhr.send();
xhr.onreadystatechange = function(){
if(xhr.readyState===4){
if(xhr.status===200){
resolve(xhr.response)
}else{
reject(xhr.responseText)
}
}
}
})
}
let p1 = getPromise('get','http://121.199.0.35:8888/index/carousel/findAll');
let p2 = getPromise('get','http://121.199.0.35:8888/index/category/findAll');
//promise静态方法:
// let res = Promise.all([p1,p2]);
// let res = Promise.any([p1,p2]);
// let res = Promise.race([p1,p2]);
// let res = Promise.allSettled([p1,p2]);
// console.log(res);
// res.then(res=>{
// console.log(res)
// })
// console.log(Promise.resolve(),'111');
// console.log(Promise.reject(),'222');
console.log(p1,p2);
p1.then(res=>{
console.log(JSON.parse(res),'111')
})
p2.then(res=>{
console.log(JSON.parse(res),'222')
});
13.2、async异步函数和awite
-
作用:1、异步编程同步处理;2、简化promise API的一种解决方案;
-
async函数是使用async关键字声明的函数,与await一起连用让我们可以用一种更简洁的方式写出基于Promise的异步行为,而无需刻意地链式调用promise。
-
await关键字只在async函数内有效
-
该异步函数内部封装了generator函数,输出的是promise对象(也是经过promise封装后才会输出promise对象)
详细介绍
1. async 是异步的意思,await则可以理解为 async wait。所以可以理解async就是用来声明一个异步方法,而 await是用来等待异步方法执行
2. async作为一个关键字放在函数前面,表示该函数是一个异步函数(异步函数意味着该函数的执行不会阻塞后面代码的执行);
而 await 用于等待一个异步方法执行完成;
3. 当函数内部执行到一个 await 语句的时候,如果语句返回一个 promise 对象,那么函数将会等待 promise 对象的状态变为 resolve 后再继续往下执行。并会阻塞该函数内后面的代码。
4. 因此async/await的作用就是将异步逻辑,转化为同步的顺序来书写,并且这个函数可以自动执行。
5. 为了优化 .then 链而开发出来的。
/**
* 异步编程解决方案 : 1.异步编程 同步解决 2.简化promise API一种解决方案
* 内部封装了generator函数 与await配合使用
* 返回的是一个Promise{}
*/
<script>
async function findAll() {
//同步写法
let res = await axios.get('http://121.199.0.39:8888/index/carousel/findAll');//这个请求很长,如果用同步打印则可能输出不完整,但若是用异步等他请求完再输出则是完整的
console.log(res, '获取响应111111111');
//异步写法 axios.get().then().catch()//链式调用promise then处理请求成功情况,catch处理请求失败的情况
// axios.get('http://121.199.0.35:8878/index/carousel/findAll').then(res=>{ 基于axios请求发回来的都是promise的实例对象,就可以调用promise的实例方法,而then也是再原型对象上
// console.log(res.data,'获取响应')
// }).catch(error=>{
// console.log(error,'444');
// })
}
findAll();
</script>
13.3、Generator函数
- 语法不同于普通函数,内部有多个使用yiled表示的状态,每个状态都是独立的(1中定义,2中并不能输出1的定义,除非传参)。
- 范围:上一个yield语句冒号结束~~~下一个yield语句冒号结束
- 调用Generator 函数会返回一个迭代器对象,可以通过调用迭代器 .next() 依次遍历Generator函数内部的每一个状态。(调用一次,执行一个状态)
- function关键字与函数名之间有个星号
<script>
function * generator(){
// generator函数内部使用yield表示一个状态 一个yield就是一个状态 一个yield就是一个代码运行节点
yield '1';
yield '2';
yield '3';
return '4'
// generator函数最后一个状态可以使用return返回
}
let res = generator();//调用generator函数得到一个迭代器对象 输出res为: Generator { }
console.log(res.next());//Object { value: "1", done: false } 调用一次next方法执行一个状态
console.log(res.next());//Object { value: "2", done: false }
console.log(res.next());//Object { value: "3", done: false }
console.log(res.next());//Object { value: "4", done: true }
//普通函数无法实现依次返回
// function foo(){
// // 执行代码 返回
// return 1
// // 执行代码 返回
// return 2
// // 执行代码 返回
// return 3
// }
// console.log(foo()); 只会return 1
</script>
想要在第二个状态中使用第一个状态的返回值:
- 直接发起状态执行不能获取第一状态的返回值
- 给第二个.next()传固定参数,可以获取固定参数却不能获取第一状态的返回值
- 所以:给第二个.next()传的参数应为第一个状态的返回值就可以
//正确写法:
<script>
function * generator(){
let result = yield findAll();
console.log(result,'result为获取的第一个状态返回值');
yield '2';
}
let res = generator();//调用generator函数得到迭代器对象
res.next();//发起第一个状态执行
// 封装异步请求
async function findAll(){
let response = await axios.get('http://121.199.0.35:8888/index/carousel/findAll');
console.log(response,'获取响应');
res.next(response.data); //response.data为第一个状态的返回值
}
</script>
//错误写法:
<script>
function *generator(){
// 模拟异步请求 第一个状态
log();
let res = yield 'hello';
// 第二个状态
console.log(res,'res为获取的第一个状态返回值');
log();
yield '2';
log();
yield '3';
}
let result = generator();
result.next();//执行第一个状态 123456789
// result.next();//执行第二个状态 undefined 获取第一个状态返回值 123456789
result.next(100);//执行第二个状态 100 获取第一个状态返回值 123456789
// 模拟异步函数
function log(){
for(let i=1;i<10;i++){
console.log(i)
}
}
</script>
14、事件循环********
首先了解:
js是单线程语言:同一时间只能做一件事情,异步代码会被放在任务队列中等待执行,先执行同步任务,再执行异步任务。
js任务分为同步任务和异步任务
- 同步任务:,不耗时,立即执行的任务;在主线程上排队执行,形成一个执行栈;只有前一个任务执行完毕,才能继续执行下一个任务。
- 异步任务:耗时,不进入主线程,而进入“任务队列”的任务;只有等主线程任务全部执行完毕。“任务队列”的任务才会进入主线程执行。分为微任务、宏任务
- 微任务:较短时间内可以完成的任务(promise.then 、catch、 finally 、 async、 await之后的代码语句相当于创建微任务)
- 宏任务:需要相对较长时间才能完成的任务(定时器setTimeout、 间歇调用、 I/O操作 、文件读取(fs.readFile()) 、网络请求)
执行顺序:同步任务 > 微任务 > 宏任务
面试的例题:求输出
//setTimeout第二个参数可以省略,默认为0 //2 4 5 7 10 3 8 6 1 9
setTimeout(function () {
console.log('1');
});
// new Promise((resolve,reject)=>{})
new Promise(function (resolve){ //这里new promise()表示声明的同时也调用
console.log('2');//调用构造函数
resolve();
}).then(function () {
console.log('3');//then方法是耗时任务
});
console.log('4');
async function async1() {
console.log(5);
const result = await async2();
//await之后的语句都会变成微任务
console.log(6);
}
async function async2() {
console.log(7);
}
Promise.resolve().then(() => {
console.log(8);
});
setTimeout(() => {
console.log(9);
});
async1();
console.log(10);
// 主线程: 异步队列:读取文件 fs.readFile() 网络请求
// 打印2 微任务:打印3 宏任务:定时器打印1
// 打印4 打印8 定时打印9
// 打印5 打印6
// 打印7
// 打印10 3 8 6 1 9
15、防抖和节流********
可在lodash官网/bootcdn官网中直接调用,不用自己封装
函数目的:为了优化高频率js事件得手段
- 防抖:在一定时间内,事件一直频繁触发,会重新计算时间,直到超过设定时间,函数才会执行最后一次。一般针对输入框的事件防抖
- 节流:在一定时间内,事件一直频繁触发,会每隔设定时间函数才会执行一次。一般针对浏览器的窗口事件节流
防抖:
<script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.js"></script>
</head>
<body>
<input type="text">
<script>
console.log(_);
var input = document.querySelector('input');
// 监听输入框事件,会一直触发
// input.oninput =function(){
// console.log(this.value)
// }
//防抖(采用lodash库里的.debounce方法)
// input.oninput = _.debounce(function(){
// console.log(this.value)
// },1000)
//自己写防抖函数
let timer;
input.oninput = function(){
if(timer){
clearTimeout(timer)
}
var that = this;
timer = setTimeout(function(){
console.log(that.value)
},1000)
}
//完整自写防抖(将上述防抖函数封装)
input.oninput = debounce(function () {
console.log(this.value)
}, 1000)
function debounce(callback, wait) {
let timer;
return function () {
if (timer) {
clearTimeout(timer)
}
var that = this;
timer = setTimeout(function () {
console.log(this)
callback.call(that);
}, wait)
}
}
</script>
</body>
节流:
<script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.js"></script>
<style>
body {
height: 3000px;
}
</style>
</head>
<body>
<script>
//节流正确写法
// window.onscroll = _.throttle(function () {
// console.log('我正在滚动')
// }, 1000);
//自己封装节流函数
window.onscroll = throttle(function () {
console.log('我正在滚动')
}, 1000);
function throttle(callback, wait) {
let id;
return function () {
if (!id) {
id = setTimeout(function () {
callback();
id = null;
}, wait)
}
}
}
</script>
</body>