Appearance
手写实现 js 方法
promise
javascript
// 先定义三个常量表示状态
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
// 新建 myPromise 类
class myPromise {
constructor(executor) {
// 储存状态的变量,初始值是 pending
this.status = PENDING;
// 成功之后的值
this.value = null;
// 失败之后的原因
this.reason = null;
// 更改成功后的状态
this.resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED; // 修改状态
this.value = value; // 保存成功后的值
// 判断 并 执行回调函数
while (this.onFulfilledCallback.length) {
this.onFulfilledCallback.shift()(value);
}
}
};
// 更改失败后的状态
this.reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED; // 修改状态
this.reason = reason; // 保存失败后的原因
// 判断 并 执行回调函数
while (this.onRejectedCallback.length) {
this.onRejectedCallback.shift()(reason);
}
}
};
// 存储成功回调函数
this.onFulfilledCallback = [];
// 存储失败回调函数
this.onRejectedCallback = [];
try {
// executor 是一个执行器,进入会立即执行
// 并传入resolve和reject方法
executor(this.resolve, this.reject);
} catch (error) {
this.reject(error);
}
}
then(onFulfilled, onRejected) {
// if (this.status === FULFILLED) {
// onFulfilled(this.value);
// } else if (this.status === REJECTED) {
// onRejected(this.reason);
// } else if (this.status === PENDING) {
// // 等到执行成功失败函数的时候再传递
// this.onFulfilledCallback.push(onFulfilled);
// this.onRejectedCallback.push(onRejected);
// }
// 如果不传,就使用默认函数
const realOnFulfilled =
typeof onFulfilled === "function" ? onFulfilled : (value) => value;
const realOnRejected =
typeof onRejected === "function"
? onRejected
: (reason) => {
throw reason;
};
// then 链式调用
const promise2 = new myPromise((resolve, reject) => {
const fulfilledMicrotask = () => {
// 创建一个微任务等待 promise2 完成初始化
queueMicrotask(() => {
try {
// 获取成功回调函数的执行结果
const x = realOnFulfilled(this.value);
// 传入 resolvePromise 集中处理
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
};
const rejectedMicrotask = () => {
// 创建一个微任务等待 promise2 完成初始化
queueMicrotask(() => {
try {
// 调用失败回调,并且把原因返回
const x = realOnRejected(this.reason);
// 传入 resolvePromise 集中处理
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
};
// 判断状态
if (this.status === FULFILLED) {
fulfilledMicrotask();
} else if (this.status === REJECTED) {
rejectedMicrotask();
} else if (this.status === PENDING) {
// 等待
// 因为不知道后面状态的变化情况,所以将成功回调和失败回调存储起来
// 等到执行成功失败函数的时候再传递
this.onFulfilledCallback.push(fulfilledMicrotask);
this.onRejectedCallback.push(rejectedMicrotask);
}
});
return promise2;
}
all(promises) {
if (!Array.isArray(promises)) {
throw new Error("promises must to be an array!!!");
}
return new myPromise(function (resolve, reject) {
let promiseNum = promises.length;
let resolvedCount = 0;
let resolveValues = new Array(promiseNum);
try {
for (let i = 0; i < promiseNum; i++) {
promises[i].then(function (value) {
resolveValues[i] = value;
if (++resolvedCount === promiseNum) {
resolve(resolveValues);
}
});
}
} catch (error) {
reject(error);
}
});
}
race(promises) {
if (!Array.isArray(promises)) {
throw new Error("promises must be an array!!!");
}
let resolved = false;
return new myPromise(function (resolve, reject) {
try {
promises.forEach((p) =>
p.then((data) => {
if (!resolved) {
resolved = true;
resolve(data);
}
})
);
} catch (error) {
reject(error);
}
});
}
}
function resolvePromise(promise2, x, resolve, reject) {
// 如果相等了,说明return的是自己,抛出类型错误并返回
if (promise2 === x) {
return reject(
new TypeError("Chaining cycle detected for promise #<Promise>")
);
}
// 判断x是不是 myPromise 实例对象
if (x instanceof myPromise) {
// 执行 x,调用 then 方法,目的是将其状态变为 fulfilled 或者 rejected
// x.then(value => resolve(value), reason => reject(reason))
// 简化之后
x.then(resolve, reject);
} else {
// 普通值
resolve(x);
}
}
// 导出
// export default myPromise;call apply bind
javascript
let Person = {
name: "Tom",
say(a, b) {
this.a = a;
this.b = b;
console.log(this);
console.log(`我叫${this.name}, a: ${this.a}, b: ${this.b}`);
}
};
// ----------------------------------------------------------------------------
/**
* call
* @param {*} ctx
* 1.ctx 存在就使用 ctx window
* 2.使用 Object(ctx) 将 ctx 转换成对象,并通过 ctx.fn 将 this 指向 ctx
* 3.循环参数,注意从 1 开始,第 0 个是上下文,后面才是我们需要的参数
* 4.将参数字符串 push 进 args
* 5.拿到结果返回前,删除掉 fn
*/
/* eslint-disable */
Function.prototype.myCall = function (ctx) {
ctx = ctx || window;
const fn = Symbol(); // Symbol属性来确定fn唯一。
const args = [...arguments].slice(1);
// console.log(this);
ctx[fn] = this; // this 是调用 call 的函数 f say() {}
const result = ctx[fn](...args);
delete ctx[fn];
return result;
};
// ----------------------------------------------------------------------------
let PersonA = {
name: "Tom A"
};
Person.say.myCall(PersonA, "A1", "A2");
// ----------------------------------------------------------------------------
/**
* apply
* @param {*} ctx
* @param {*} args
* 1.apply 无需循环参数列表,传入的 args 就是数组
* 2.但是 args 是可选参数,如果不传入的话,直接执行
*/
/* eslint-disable */
Function.prototype.myApply = function (ctx, args) {
ctx = ctx || window;
const fn = Symbol(); // Symbol属性来确定fn唯一。
ctx[fn] = this; // this 是调用 call 的函数 f say() {}
let result;
if (!args) {
// 判断 args 是否存在
result = ctx[fn]();
} else {
result = ctx[fn](...args);
}
delete ctx[fn];
return result;
};
// ----------------------------------------------------------------------------
let PersonB = {
name: "Tom B"
};
Person.say.myApply(PersonB, ["B1", "B2"]);
// ----------------------------------------------------------------------------
/**
* bind
* 1. 改变this指向
* 2. 接受参数列表、
* 3. return 一个函数
* 4. 遇到new这样优先级高于bind方法的时候,bind改变this指向会无效。
*/
/* eslint-disable */
Function.prototype.myBind = function (ctx) {
var self = this; // 因为之后要return一个函数,这里保存一下this防止混乱
var args = Array.prototype.slice.call(arguments, 1); // 获取参数
var fb = function () {
var bindArgs = Array.prototype.slice.call(arguments);
const target = this instanceof fb ? this : ctx;
return self.apply(target, args.concat(bindArgs));
};
var fn = function () {};
fn.prototype = this.prototype; // 让实例可以继承原型
fb.prototype = new fn();
return fb;
};
// ----------------------------------------------------------------------------
let PersonC = {
name: "Tom C"
};
const fnC = Person.say.myBind(PersonC, "C1");
fnC("C2");New
javascript
/**
* new
* 1.Constructor 就是 new 时传入的第一个参数,剩余的 arguments 是其他的参数
* 2.使用obj.__proto__ = Constructor.prototype 继承原型上的方法
* 3.将剩余的 arguments 传给 Constructor ,绑定 this 指向为 obj,并执行
* 4.如果构造函数返回的是引用类型,直接返回该引用类型,否则返回 obj
*/
const myNew = function () {
let Constructor = Array.prototype.shift.call(arguments);
let obj = new Object();
obj.__proto__ = Constructor.prototype;
// console.log(Constructor, obj);
let res = Constructor.apply(obj, arguments);
return res instanceof Object ? res : obj;
};
function fun(a, b) {
this.a = a;
this.b = b;
this.c = function () {
console.log(this.a, this.b);
return this;
};
}
const test = myNew(fun, "A", "B");
console.log(test.c());