-
Event Loop(事件循环/事件轮询)
console.log("Hi")
setTimeout(() => {
console.log("Hello")
}, 2000);
console.log("yo")
// Hi yo Hello
由于js是单线程,从上向下依次执行每行代码,同步代码直接推入call stack(执行栈)中执行,执行完成后从执行栈中移除,当遇到异步代码(定时器、ajax)时,异步代码的回调函数会在浏览器Web API进行实现,等到相应时机(定时器结束、网络请求返回)时,将回调放入事件队列。
所谓Event Loop机制,指的是一种内部循环,用来一轮又一轮地处理事件队列之中的事件,即执行对应的回调函数。
-
Promise
Promise时ES6中新增的一个类,目的是更加优雅地书写复杂的异步任务,避免了回调嵌套产生的“回调地狱”问题。
Promise的三种状态:pedding(准备)、fullfilled(已完成)、rejected(已失败) -
宏任务和微任务
宏任务: setTimeout、setInterval、Ajax、DOM事件 (DOM渲染后执行)
微任务: Promise、async\await (DOM渲染前执行)
微任务执行时机要早于宏任务
在Event loop中执行事件队列的回调完成,执行栈清空后,会尝试进行DOM渲染,而宏任务与微任务的执行时机就是在这一行为前后。 -
手写Promise
class MyPromise {
state = "pending"; //初始状态
value = undefined; //成功结果值
reason = undefined; //失败原因
resolveCallbacks = []; //pending状态,存储成功回调
rejectCallbacks = []; //pending状态,存储失败回调
//构造函数传入一个函数,函数有两个参数,分别为resolve、reject
constructor(fn) {
const resolveHandler = (value) => {
if (this.state === "pending") {
this.state = "fulfilled";
this.value = value;
this.resolveCallbacks.forEach((fn) => fn(this.value));
}
};
const rejectHandler = (reason) => {
if (this.state === "pending") {
this.state = "rejected";
this.reason = reason;
this.rejectCallbacks.forEach((fn) => fn(this.reason));
}
};
try {
fn(resolveHandler, rejectHandler);
} catch (e) {
rejectHandler(e);
}
}
//then方法只在状态变为fulfilled或rejected执行,pending状态存入数组中
then(fn1, fn2) {
//对传入参数进行处理,必须为函数
fn1 = typeof fn1 === "function" ? fn1 : (v) => v;
fn2 = typeof fn2 === "function" ? fn2 : (e) => e;
if (this.state === "pending") {
return new MyPromise((resolve, reject) => {
this.resolveCallbacks.push(() => {
try {
const newValue = fn1(this.value);
resolve(newValue);
} catch (e) {
reject(e);
}
});
this.rejectCallbacks.push(() => {
try {
const newReason = fn2(this.reason);
reject(newReason);
} catch (e) {
reject(e);
}
});
});
}
//成功或者失败状态 直接返回一个新的MyPromise对象
if (this.state === "fulfilled") {
return new MyPromise((resolve, reject) => {
try {
const newValue = fn1(this.value);
resolve(newValue);
} catch (e) {
reject(e);
}
});
}
if (this.state === "rejected") {
return new MyPromise((resolve, reject) => {
try {
const newReason = fn2(this.reason);
reject(newReason);
} catch (e) {
reject(e);
}
});
}
}
//catch是then方法的一个语法糖,只传入第二个参数
catch(fn) {
return this.then(null, fn);
}
}
//实现静态方法
MyPromise.resolve = function (value) {
return new MyPromise((resolve, reject) => resolve(value));
};
MyPromise.reject = function (reason) {
return new MyPromise((resolve, reject) => reject(reason));
};
//只有当promiseList全部resolve才会返回fulfilled状态的MyPromise
MyPromise.all = function (promiseList = []) {
return new MyPromise((resolve, reject) => {
const length = promiseList.length;
const result = [];
let resolveCount = 0;
promiseList.forEach((p) => {
p.then((value) => {
result.push(value);
resolveCount++;
if (resolveCount === length) {
resolve(result);
}
}).catch((e) => {
reject(e);
});
});
});
};
//以第一个状态改变的MyPromise对象为准返回一个 fulfilled或rejected状态的MyPromise对象
MyPromise.race = function (promiseList = []) {
return new MyPromise((resolve, reject) => {
let resolved = false;
promiseList.forEach((p) => {
p.then((value) => {
if (!resolved) {
resolved = true;
resolve(value);
}
}).catch((e) => {
reject(e);
});
});
});
};
-
数据类型
基本数据类型:number、string、symbol、boolean、undefinded
引用数据类型:object、null(特殊引用类型,指向空地址)、Function(特殊引用类型,不存储数据)
区别: 基本数据类型值存储在栈中,而引用数据类型只在栈中存储引用地址,实际数据存储在堆中
6. #### 递归深拷贝
意义: 引用类型赋值只能实现浅拷贝,即拷贝引用类型的内存地址,修改拷贝对象会造成原有对象改变,所以对于引用类型数据进行复制需要深拷贝
function deepClone(obj={}){
//非数组和对象类型直接返回
if(typeof obj !=="object" || obj==null){
return obj
}
//初始克隆结果
let result
if(obj instanceof Array){
result=[]
}else{
result={}
}
//遍历取值/赋值
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
const element = deepClone(obj[key]);
result[key]=element
}
}
return result
}
-
原型和原型链
class Animal {
constructor(name){
this.name=name
}
eat(){
console.log(`${this.name} eat something`)
}
}
class Cat extends Animal {
constructor(name,age){
super(name)
this.age=age
}
mew(){
console.log('mi mi')
}
}
const cat=new Cat("mimi",18)
console.log(cat.__proto__===Cat.prototype) //true
console.log(cat.__proto__.__proto__===Animal.prototype) //true
__proto__: 隐式原型
prototype: 显式原型
获取实例的属性和方法,优先从自身查找,如果没有,则通过实例的__proto__指向类的显式原型prototype进行查找,如果类中没有则又通过类的显式原型的__proto__向继承父类prototype中查找,以此类推,直到顶层Object为止。
instanceof: 用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上
-
闭包
闭包是在编程中的一种概念,它指的是有权访问另一个函数作用域中的变量的函数
函数自由变量的查找,在函数定义的地方向上级作用域查找,而不是在函数执行的地方向上查找
函数内this的指向在函数执行的确定
//手写bind方法
Function.prototype.bind1 = function () {
//将参数解析为数组
const args = Array.from(arguments);
//获取参数列表的第一项,即this
const t = args.shift();
//fn1.bind(...)中的fn1
const self = this;
//返回一个函数
return () => {
return self.apply(t, args);
};
};
function fn(){
console.log(this) // {a:1}
}
const fn1=fn.bind1({a:1})
fn1()
闭包中的数据只在作用域中被修改和访问,可以隐藏数据
//闭包的应用 cache工具 隐藏数据
function createCache(){
const data={}
return {
get:function(key){
return data[key]
},
set:function(key,value){
data[key]=value
}
}
}
const c=createCache()
c.set("name","jack")
const name=c.get("name") //jack