call、apply、bind 手写
实现 call 方法:
Function.prototype.call2 = function (context, ...args) {
//没传参数或者为 null 是默认是 window
var context = context || (typeof window !== "undefined" ? window : global);
// 首先要获取调用 call 的函数,用 this 可以获取
context.fn = this;
const result = eval("context.fn(...args)");
delete context.fn;
return result;
};
// 测试
var value = 3;
var foo = {
value: 2,
};
function bar(name, age) {
console.log(this.value);
console.log(name);
console.log(age);
}
bar.call2(null);
// 浏览器环境: 3 undefinde undefinde
// Node环境:undefinde undefinde undefinde
bar.call2(foo, "cc", 18); // 2 cc 18
实现 apply 方法:
Function.prototype.apply2 = function (context, arr) {
var context = context || (typeof window !== "undefined" ? window : global);
context.fn = this;
var result;
if (!arr) {
result = context.fn();
} else {
result = eval("context.fn(...arr)");
}
delete context.fn;
return result;
};
// 测试:
var value = 3;
var foo = {
value: 2,
};
function bar(name, age) {
console.log(this.value);
console.log(name);
console.log(age);
}
bar.apply2(null);
// 浏览器环境: 3 undefinde undefinde
// Node环境:undefinde undefinde undefinde
bar.apply2(foo, ["cc", 18]); // 2 cc 18
实现 bind 方法:
Function.prototype.bind2 = function (ctx, ...args) {
const fn = this;
return function (...restArgs) {
return fn.apply(ctx, [...args, ...restArgs]);
};
};
// 测试
var test = {
name: "jack",
};
var demo = {
name: "rose",
getName: function () {
return this.name;
},
};
console.log(demo.getName()); // 输出 rose 这里的 this 指向 demo
// 运用 bind 方法更改 this 指向
var another2 = demo.getName.bind2(test);
console.log(another2()); // 输出 jack 这里 this 指向了 test 对象了
new 手写
// 构造器函数
let Parent = function (name, age) {
this.name = name;
this.age = age;
};
Parent.prototype.sayName = function () {
console.log(this.name);
};
//自己定义的new方法
let newMethod = function (Parent, ...rest) {
// 1.以构造器的prototype属性为原型,创建新对象;
let child = Object.create(Parent.prototype);
// 2.将this和调用参数传给构造器执行
let result = Parent.apply(child, rest);
// 3.如果构造器没有手动返回对象,则返回第一步的对象
return typeof result === "object" ? result : child;
};
//创建实例,将构造函数Parent与形参作为参数传入
const child = newMethod(Parent, "echo", 26);
child.sayName(); //'echo';
//最后检验,与使用new的效果相同
console.log(child instanceof Parent); //true
console.log(child.hasOwnProperty("name")); //true
console.log(child.hasOwnProperty("age")); //true
console.log(child.hasOwnProperty("sayName")); //false
根据 ES6 构造函数的书写方式,写出 ES5 的
ES6 的 class 和普通构造函数的区别有以下几点:
- ES6 中的 class 必须通过 new 来调用,不能当做普通函数调用,否则报错因此,在答案中,加入了 new.target 来判断调用方式
- ES6 的 class 中的所有代码均处于严格模式之下因此,在答案中,无论是构造函数本身,还是原型方法,都使用了严格模式
- ES6 中的原型方法是不可被枚举的因此,在答案中,定义原型方法使用了属性描述符,让其不可枚举
class Example {
constructor(name) {
this.name = name;
}
init() {
const fun = () => { console.log(this.name) }
fun();
}
}
const e = new Example('Hello');
e.init();
function Example(name) {
"use strict";
if (!new.target) {
throw new TypeError("Class constructor cannot be invoked without new");
}
this.name = name;
}
Object.defineProperty(Example.prototype, "init", {
enumerable: false,
value: function () {
"use strict";
if (new.target) {
throw new TypeError("init is not a constructor");
}
var fun = function () {
console.log(this.name);
};
fun.call(this);
},
});
防抖,节流手写
/**
* 函数防抖
* @param {function} fn 一段时间后,要调用的函数
* @param {number} wait 等待的时间,单位毫秒
*/
function debounce(fn, wait) {
// 设置变量,记录 setTimeout 得到的 id
let timerId = null;
return function (...args) {
if (timerId) {
// 如果有值,说明目前正在等待中,清除它
clearTimeout(timerId);
}
// 重新开始计时
timerId = setTimeout(() => {
fn.call(this, ...args);
}, wait);
};
}
//节流
function throttle(fn, wait) {
let previous = 0;
return function (...args) {
let now = +new Date();
if (now - previous > wait) {
fn.apply(this, ...args);
previous = now;
}
};
}
async 手写
原始
function A() {
return new Promise((resolve) => {
setTimeout(() => {
resolve(3);
}, 1000);
});
}
async function B() {
const b = await A();
const c = await A();
return b + c;
}
B().then((data) => console.log(data));
转换结果
//不变
function A() {
return new Promise((resolve) => {
setTimeout(() => {
resolve(3);
}, 1000);
});
}
//转换
function B() {
var fn = function* () {
const b = yield A();
const c = yield A();
return b + c;
};
return new Promise(function (resolve, reject) {
var gen = fn();
function _next(value) {
asyncGeneratorStep(gen, resolve, reject, _next, value);
}
_next(undefined);
});
}
//转换
function asyncGeneratorStep(gen, resolve, reject, _next, arg) {
try {
var info = gen.next(arg);
var value = info.value;
} catch (error) {
reject(error);
return;
}
if (info.done) {
resolve(value);
} else {
Promise.resolve(value).then((data) => {
_next(data);
});
}
}
//不变
B().then(function (data) {
return console.log(data);
});
深拷贝手写
export function deepClone(obj) {
let newObj = {};
if (Array.isArray(obj)) {
newObj = [];
}
for (let k in obj) {
let value = obj[k];
if (typeof value === "object" && value !== null) {
newObj[k] = deepClone(value);
} else {
//其他值 包括function的情况 都直接赋值
newObj[k] = value;
}
}
return newObj;
}
flat 手写
let arr = [
1,
[2, 3, [4, 5, [12, 3, "zs"], 7, [8, 9, [10, 11, [1, 2, [3, 4]]]]]],
];
//自定义flat方法,注意:不可以使用箭头函数,使用后内部的this会指向window
Array.prototype.myFlat = function (num) {
//遍历所有子元素并判断类型,若为数组则继续递归,若不为数组则直接加入新数组
const result = [];
this.forEach((item) => {
if (Array.isArray(item)) {
//形参num,表示当前需要拆分多少层数组,传入Infinity则将多维直接降为一维
num--;
if (num < 0) {
let newArr = result.push(item);
return newArr;
}
//使用三点运算符解构,递归函数返回的数组,并加入新数组
result.push(...item.myFlat(num));
} else {
result.push(item);
}
});
return result;
};
console.log(arr.flat(Infinity)); //[1, 2, 3, 4, 5, 12, 3, "zs", 7, 8, 9, 10, 11, 1, 2, 3, 4];
function flat(arr, depth = 1) {
let result = [];
for (let i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i]) && depth > 0) {
result = result.concat(flat(arr[i], depth - 1));
} else {
result.push(arr[i]);
}
}
return result;
}
reduce 手写
Array.prototype.myReduce = function (callback, initialValue) {
if (!Array.isArray(this) || !this.length || typeof callback !== "function") {
return [];
} else {
let value = initialValue ? initialValue : this[0];
for (let index = initialValue ? 0 : 1; index < this.length; index++) {
const element = this[index];
value = callback(value, element, index, this);
}
return value;
}
};
let arr = [1, 2, 3, 4, 5];
let res = arr.myReduce((pre, cur, i, arr) => {
console.log(pre, cur, i, arr);
return pre + cur;
}, 10);
console.log(res); //25
promise 手写
// 记录Promise的三种状态
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
/**
* 运行一个微队列任务
* 把传递的函数放到微队列中
* @param {Function} callback
*/
function runMicroTask(callback) {
// 判断node环境
// 为了避免「变量未定义」的错误,这里最好加上前缀globalThis
// globalThis是一个关键字,指代全局对象,浏览器环境为window,node环境为global
if (globalThis.process && globalThis.process.nextTick) {
process.nextTick(callback);
} else if (globalThis.MutationObserver) {
const p = document.createElement("p");
const observer = new MutationObserver(callback);
observer.observe(p, {
childList: true, // 观察该元素内部的变化
});
p.innerHTML = "1";
} else {
setTimeout(callback, 0);
}
}
/**
* 判断一个数据是否是Promise对象
* @param {any} obj
* @returns
*/
function isPromise(obj) {
return !!(obj && typeof obj === "object" && typeof obj.then === "function");
}
class MyPromise {
/**
* 创建一个Promise
* @param {Function} executor 任务执行器,立即执行
*/
constructor(executor) {
this._state = PENDING; // 状态
this._value = undefined; // 数据
this._handlers = []; // 处理函数形成的队列
try {
executor(this._resolve.bind(this), this._reject.bind(this));
} catch (error) {
this._reject(error);
console.error(error);
}
}
/**
* 向处理队列中添加一个函数
* @param {Function} executor 添加的函数
* @param {String} state 该函数什么状态下执行
* @param {Function} resolve 让then函数返回的Promise成功
* @param {Function} reject 让then函数返回的Promise失败
*/
_pushHandler(executor, state, resolve, reject) {
this._handlers.push({
executor,
state,
resolve,
reject,
});
}
/**
* 根据实际情况,执行队列
*/
_runHandlers() {
if (this._state === PENDING) {
// 目前任务仍在挂起
return;
}
while (this._handlers[0]) {
const handler = this._handlers[0];
this._runOneHandler(handler);
this._handlers.shift();
}
}
/**
* 处理一个handler
* @param {Object} handler
*/
_runOneHandler({ executor, state, resolve, reject }) {
runMicroTask(() => {
if (this._state !== state) {
// 状态不一致,不处理
return;
}
if (typeof executor !== "function") {
// 传递后续处理并非一个函数
this._state === FULFILLED ? resolve(this._value) : reject(this._value);
return;
}
try {
const result = executor(this._value);
if (isPromise(result)) {
result.then(resolve, reject);
} else {
resolve(result);
}
} catch (error) {
reject(error);
console.error(error);
}
});
}
/**
* Promise A+规范的then
* @param {Function} onFulfilled
* @param {Function} onRejected
*/
then(onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => {
this._pushHandler(onFulfilled, FULFILLED, resolve, reject);
this._pushHandler(onRejected, REJECTED, resolve, reject);
this._runHandlers(); // 执行队列
});
}
/**
* 仅处理失败的场景
* @param {Function} onRejected
*/
catch(onRejected) {
return this.then(null, onRejected);
}
/**
* 无论成功还是失败都会执行回调
* @param {Function} onSettled
*/
finally(onSettled) {
return this.then(
(data) => {
onSettled();
return data;
},
(reason) => {
onSettled();
throw reason;
}
);
}
/**
* 更改任务状态
* @param {String} newState 新状态
* @param {any} value 相关数据
*/
_changeState(newState, value) {
if (this._state !== PENDING) {
// 目前状态已经更改
return;
}
// 下面这个判断是为了处理value为Promise的情况
// 这一段代码课程中没有涉及,特此注释说明
if (isPromise(value)) {
value.then(this._resolve.bind(this), this._reject.bind(this));
return;
}
this._state = newState;
this._value = value;
this._runHandlers(); // 状态变化,执行队列
}
/**
* 标记当前任务完成
* @param {any} data 任务完成的相关数据
*/
_resolve(data) {
this._changeState(FULFILLED, data);
}
/**
* 标记当前任务失败
* @param {any} reason 任务失败的相关数据
*/
_reject(reason) {
this._changeState(REJECTED, reason);
}
/**
* 返回一个已完成的Promise
* 特殊情况:
* 1. 传递的data本身就是ES6的Promise对象
* 2. 传递的data是PromiseLike(Promise A+),返回新的Promise,状态和其保持一致即可
* @param {any} data
*/
static resolve(data) {
if (data instanceof MyPromise) {
return data;
}
return new MyPromise((resolve, reject) => {
if (isPromise(data)) {
data.then(resolve, reject);
} else {
resolve(data);
}
});
}
/**
* 得到一个被拒绝的Promise
* @param {any}} reason
*/
static reject(reason) {
return new MyPromise((resolve, reject) => {
reject(reason);
});
}
/**
* 得到一个新的Promise
* 该Promise的状态取决于proms的执行
* proms是一个迭代器,包含多个Promise
* 全部Promise成功,则返回的Promise成功,数据为所有Promise成功的数据,并且顺序是按照传入的顺序排列
* 只要有一个Promise失败,则返回的Promise失败,原因是第一个失败的Promise的原因
* @param {iterator} proms
*/
static all(proms) {
return new MyPromise((resolve, reject) => {
try {
const results = [];
let count = 0; // Promise的总数
let fulfilledCount = 0; // 已完成的数量
for (const p of proms) {
let i = count;
count++;
MyPromise.resolve(p).then((data) => {
fulfilledCount++;
results[i] = data;
if (fulfilledCount === count) {
// 当前是最后一个Promise完成了
resolve(results);
}
}, reject);
}
if (count === 0) {
resolve(results);
}
} catch (error) {
reject(error);
console.error(error);
}
});
}
/**
* 等待所有的Promise有结果之后
* 该方法返回的Promise完成
* 并且按照顺序将所有结果汇总
* @param {iterator} proms
*/
static allSettled(proms) {
const ps = [];
for (const p of proms) {
ps.push(
MyPromise.resolve(p).then(
(value) => ({
status: FULFILLED,
value,
}),
(reason) => ({
status: REJECTED,
reason,
})
)
);
}
return MyPromise.all(ps);
}
/**
* 返回的Promise与第一个有结果的一致
* @param {iterator} proms
*/
static race(proms) {
return new MyPromise((resolve, reject) => {
for (const p of proms) {
MyPromise.resolve(p).then(resolve, reject);
}
});
}
}
instanceof 手写
instanceof (A,B) = {
let L = A.__proto__;
let R = B.prototype;
if(L === R) {
// A的内部属性 __proto__ 指向 B 的原型对象
return true;
}
return false;
}