本文记录一些 js 一些基础手写题
手写 call
思路:(apply 实现思路类似,参数处理不同)
1: 第一个参数为undefined或null的时候,那么会转变为window
2:改变了this执行,让新的对象可以执行该函数
Function.prototype.myCall = function (context) {
// 如果不传的话默认上下文是window
context = context || window
// 给content创建一个fn属性,并将值设置为需要调用的函数 this 指需要调用的函数
context.fn = this
// 取出参数
const args = [...arguments].slice(1)
const result = context.fn(...args)
// 删除多余函数
delete context.fn
return result
}
手写 bind
思路:
bind返回一个函数,对于函数来说有两种方式调用,一种是直接调用,一种是通过new的方式
(1) 直接调用,参数需要注意以下情况:因为bind可以实现类似这样的代码f.bind(obj,1)(2)
(2) new的方式, 不会被任何方式改变this
Function.prototype.myBind = function(context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
//返回一个绑定this的函数,这里我们需要保存this
const _this = this
// 取出参数
const args = [...arguments].slice(1)
//返回一个函数
return function F() {
//因为返回一个函数,我们可以new F()需要判断能当做构造函数吗
if (this instanceof F) {
return new _this(...args, ...arguments)
}
// 直接调用,
return _this.apply(context, args.concat(...arguments))
}
}
手写 New
思路:
(1)创建一个空对象
(2)将新对象挂载到原型链上
(3)改this指向
(4)如果构造函数返回一个对象,则将该对象返回,否则返回步骤1创建的对象
function myNew(obj, ...args) {
const newObj = Object.create(obj.prototype);//Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__
const result = obj.apply(newObj, args);
return (typeof result === 'object' && result !== null) ? result : newObj;
}
手写 instanceof
思路:
根据原型链进行查找判断leftValue是否为rightValue的实例,思想是在leftValue的原型链上,即leftValue.__proto__上寻找是否存在rightValue.prototype
function newInstanceOf (leftValue, rightValue) {
if (typeof leftValue !== 'object' || rightValue == null) {
return false;
}
let rightProto = rightValue.prototype;
leftValue = leftValue.__proto__;
while (true) {
if (leftValue === null) return false;
if (leftValue === rightProto) return true;
leftValue = leftValue.__proto__;
}
}
手写 Promise/A+规范
思路:
(1)可以接收一个executor作为入参
(2)具备pending、resolved和rejected这三种状态
(2)then链式调用,所以需要维护队列(包括resolve和reject的队列)
(2)批量执行这个动作包装成异步任务,确保它一定可以在同步代码之后执行
function CutePromise(executor) {
// value 记录异步任务成功的执行结果
this.value = null;
// reason 记录异步任务失败的原因
this.reason = null;
// status 记录当前状态,初始化是 pending
this.status = 'pending';
// 缓存两个队列,维护 resolved 和 rejected 各自对应的处理函数
this.onResolvedQueue = [];
this.onRejectedQueue = [];
// 把 this 存下来,后面会用到
var self = this;
// 定义 resolve 函数
function resolve(value) {
// 如果不是 pending 状态,直接返回
if (self.status !== 'pending') {
return;
}
// 异步任务成功,把结果赋值给 value
self.value = value;
// 当前状态切换为 resolved
self.status = 'resolved';
// 用 setTimeout 延迟队列任务的执行
setTimeout(function(){
// 批量执行 resolved 队列里的任务
self.onResolvedQueue.forEach(resolved => resolved(self.value));
});
}
// 定义 reject 函数
function reject(reason) {
// 如果不是 pending 状态,直接返回
if (self.status !== 'pending') {
return;
}
// 异步任务失败,把结果赋值给 value
self.reason = reason;
// 当前状态切换为 rejected
self.status = 'rejected';
// 用 setTimeout 延迟队列任务的执行
setTimeout(function(){
// 批量执行 rejected 队列里的任务
self.onRejectedQueue.forEach(rejected => rejected(self.reason));
});
}
// 把 resolve 和 reject 能力赋予执行器
executor(resolve, reject);
}
// then 方法接收两个函数作为入参(可选)
CutePromise.prototype.then = function(onResolved, onRejected) {
// 注意,onResolved 和 onRejected必须是函数;如果不是,我们此处用一个透传来兜底
if (typeof onResolved !== 'function') {
onResolved = function(x) {return x};
}
if (typeof onRejected !== 'function') {
onRejected = function(e) {throw e};
}
// 依然是保存 this
var self = this;
// 判断是否是 resolved 状态
if (self.status === 'resolved') {
// 如果是 执行对应的处理方法
onResolved(self.value);
} else if (self.status === 'rejected') {
// 若是 rejected 状态,则执行 rejected 对应方法
onRejected(self.reason);
} else if (self.status === 'pending') {
// 若是 pending 状态,则只对任务做入队处理
self.onResolvedQueue.push(onResolved);
self.onRejectedQueue.push(onRejected);
}
return this
};
最后
个人感觉类似手写的问题很容易忘记,需要注意的是理解其中的原理,然后再根据原理反推源码,这个比死记硬背代码(不会真有人能背下来吧)轻松多了。继续加油