一、手写call函数
Function.prototype.myCall = function (context, ...args) {
if (typeof this !== 'function') {
throw new TypeError('not a function');
}
if (context === undefined || context === null) {
// 如果传入的上下文对象是undefined或null的话,则直接使用window对象作为上下文对象
context = window;
} else {
context = Object(context);
}
var specialPrototype = Symbol('specialPrototype'); // 给上下文对象添加的临时属性
context[specialPrototype] = this; // 将this函数添加到上下文对象上
var result = context[specialPrototype](...args); // 调用上下文对象上的方法获取执行结果
delete context[specialPrototype]; // 删除上下文对象上添加的临时属性
return result; // 返回函数执行结果
}
二、手写bind函数
Function.prototype.myBind = function (thisArg) {
if (typeof this !== 'function') {
throw new TypeError('not a function');
}
var that = this;
var args = Array.prototype.slice.call(arguments, 1);
var fn = function () {}
var fnBound = function () {
const _this = this instanceof fn ? this : thisArg;
const newArgs = args.concat(Array.prototype.slice.call(arguments));
return that.apply(_this, newArgs);
}
if (this.prototype) {
fn.prototype = this.prototype;
}
fnBound.prototype = new fn();
fnBound.prototype.constructor = this;
return fnBound;
};
三、手写实现new功能的函数
function myNew(fn, ...args) {
// 创建一个空的对象,将实例化对象的原型指向构造函数的原型对象
const instance = Object.create(fn.prototype);
// 将构造函数的this指向实例化对象
const res = fn.apply(instance, args);
// 判断返回值,如果函数返回值为基本数据类型时, 则new出的对象依然是创建出的对象
return res instanceof Object ? res : instance;
}
四、手写reduce函数
Array.prototype.myReduce = function (fn, initialValue) {
// 判断调用对象是否为数组
if (Object.prototype.toString.call(this) !== '[object Array]') {
throw new TypeError('not a array');
}
// 判断调用数组是否为空数组
if (this.length === 0) {
throw new TypeError('empty array');
}
// 判断传入的第一个参数是否为函数
if (typeof fn !== 'function') {
throw new TypeError(`${fn} is not a function`);
}
// 回调函数参数初始化
var sourceArray = this;
var result, currentValue, currentIndex;
if (initialValue !== undefined) {
result = initialValue;
currentIndex = 0;
} else {
result = sourceArray[0];
currentIndex = 1;
}
// 开始循环
while (currentIndex < sourceArray.length) {
if (Object.prototype.hasOwnProperty.call(sourceArray, currentIndex)) {
currentValue = sourceArray[currentIndex];
result = fn(result, currentValue, currentIndex, sourceArray);
}
currentIndex++;
}
// 返回结果
return result;
}
五、手写防抖函数
function debounce(fn, delay=300) {
if (typeof fn !== 'function') {
throw new TypeError(`${fn} is not a function`);
}
let timer = null;
return (...args) => {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
}
}
六、手写节流函数
function throttle(fn, duration=500) {
if (typeof fn !== 'function') {
throw new TypeError(`${fn} is not a function`);
}
let lastTime = new Date().getTime();le
return (...args) => {
const now = new Date().getTime();
if (now - lastTime >= duration) {
fn.apply(this, args);
lastTime = now;
}
}
}
七、手写Promise类 (实现了Promise/A+ 的大部分规范)
// 定义promise的三个状态值
var promiseStatus = {
PENDING: "pending",
FULFILLED: "fulfilled",
REJECTED: "rejected",
}
function MyPromise(task) {
if(typeof task !== "function") {
throw new TypeError(`${task} is not a function`);
}
this.status = promiseStatus.PENDING; // 设置初始状态
this.value = undefined;
this.thenCallback = undefined;
this.catchCallback = undefined;
var _this = this; // 缓存this对象
var resolve = function(value) {
if (_this.status === promiseStatus.PENDING) {
_this.status = promiseStatus.FULFILLED;
_this.value = value;
if(value instanceof MyPromise) {
value.then(function(res) {
if (_this.thenCallback) {
_this.thenCallback(res);
} else if (_this.catchCallback) {
_this.catchCallback(res);
}
});
} else {
// 这里使用setTimeout来模拟异步任务,实际promise是微任务,回调函数会放在微任务队列中
setTimeout(function() {
if (_this.thenCallback) {
_this.thenCallback(_this.value);
} else if (_this.catchCallback) {
_this.catchCallback(_this.value);
}
});
}
}
}
var reject = function(errValue) {
if (_this.status === promiseStatus.PENDING) {
_this.status = promiseStatus.REJECTED;
_this.value = errValue;
// 这里使用setTimeout来模拟异步任务,实际promise是微任务,回调函数会放在微任务队列中
setTimeout(function() {
if (_this.catchCallback) {
_this.catchCallback(_this.value);
} else if (_this.thenCallback) {
_this.thenCallback(_this.value);
}
});
}
}
try {
task(resolve, reject);
} catch(err) {
reject(err);
}
}
MyPromise.prototype.then = function(onFulfilledCallback, onRejectedCallback) {
var _this = this;
// 返回promise对象,保证链式调用
return new MyPromise(function(resolve, reject) {
if (typeof onFulfilledCallback === "function") {
_this.thenCallback = function(value) {
/**
* 因为在使用链式调用的时候可能第一个调用的不是then
* 所以我们在做检测时会借助then来将catch的信息向下传递
* 所以我们检测到触发thenCallback的Promise对象的状态是rejected时
* 我们就继续调用下一个Promise对象的reject
*/
if (_this.status === promiseStatus.REJECTED) {
reject(value);
} else {
// 用户传入的方法执行时都要用try包裹
try {
var res = onFulfilledCallback(value);
if(res instanceof MyPromise && res.status === promiseStatus.REJECTED) {
res.catch(function(errValue) {
reject(errValue);
});
} else {
resolve(res);
}
} catch(err) {
reject(err);
}
}
};
}
if (typeof onRejectedCallback === "function") {
_this.catchCallback = function(errValue) {
/**
* 因为在使用链式调用的时候可能第一个调用的不是catch
* 所以我们在做检测时会借助catch来将then的信息向下传递
* 所以我们检测到触发catchCallback的Promise对象的状态是fulfilled时
* 我们就继续调用下一个Promise对象的resolve
*/
if (_this.status === promiseStatus.FULFILLED) {
resolve(errValue);
} else {
// 用户传入的方法执行时都要用try包裹
try {
var res = onRejectedCallback(errValue);
if(res instanceof MyPromise && res.status === promiseStatus.REJECTED) {
res.catch(function(errValue) {
reject(errValue);
});
} else {
resolve(res);
}
} catch(err) {
reject(err);
}
}
}
}
});
}
MyPromise.prototype.catch = function(onRejectedCallback) {
return this.then(null, onRejectedCallback);
}
MyPromise.prototype.finally = function (onFinallyCallback) {
return this.then(function (res) {
onFinallyCallback();
return res;
}, function(err) {
onFinallyCallback();
throw new Error(err);
});
}
MyPromise.resolve = function(value) {
return new MyPromise(function(resolve, reject) {
resolve(value);
});
}
MyPromise.reject = function(errValue) {
return new MyPromise(function(resolve, reject) {
reject(errValue);
});
}
MyPromise.all = function (promiseArr) {
var resArr = [];
return new MyPromise(function(resolve, reject) {
promiseArr.forEach(function(item, index) {
item.then(function(res) {
resArr[index] = res;
var allResolve = promiseArr.every(function(_item) {
return _item.status === promiseStatus.FULFILLED;
})
if (allResolve) {
resolve(resArr);
}
}).catch(function(err) {
reject(err);
})
});
});
}
MyPromise.race = function (promiseArr) {
return new MyPromise(function(resolve, reject) {
promiseArr.forEach(function(item, index) {
item.then(function(res) {
resolve(res);
}).catch(function(err) {
reject(err);
});
});
});
}
MyPromise.allSettled = function (promiseArr) {
var resAll = [];
return new MyPromise(function (resolve, reject) {
promiseArr.forEach(function (item, index) {
item.then(function (res) {
const obj = {
status: promiseStatus.FULFILLED,
value: res,
}
resArr[index] = obj;
var allResolve = promiseArr.every(function(_item) {
return _item.status !== promiseStatus.PENDING;
});
if (allResolve) {
resolve(resArr);
}
}).catch(function (err) {
const obj = {
status: promiseStatus.REJECTED,
value: err,
}
resArr[index] = obj;
var allResolve = promiseArr.every(function (_item) {
return _item.status !== promiseStatus.PENDING;
});
if (allResolve) {
resolve(resArr);
}
});
})
});
}
八、手写XMLHttpRequest发送请求
function request(method, url, params){
// 初始化实例
let xhr;
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
}else {
xhr = new ActiveXObject("microsoft.XMLHTTP");
}
method = method ? method.toUpperCase() : 'GET';
if (method === 'POST') {
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
}
xhr.open(method , url, true);
xhr.onreadystatechange = function () {
// 只有readyState === 4 和 status === 200,才会正常返回数据
if (xhr.readyState === 4) {
if (xhr.status === 200) {
// 通过JSON.parse()把test.json里面的数据转换为json对象
console.log(JSON.parse(xhr.responseText))
} else {
console.log('其他情况')
}
}
}
xhr.sent(method === 'GET' ? null : JSON.stringify(params));
}
九、手写深拷贝deepClone函数
function deepClone (value, map = new WeakMap()) {
let newValue = value;
if (value && typeof obj === 'object') {
// 使用map防止循环引用,检查map中有没有,如果有被记录则直接返回
const oldValue = map.get(value);
if ( oldeValue ) {
return oldValue;
}
// 如果没有被记录,则创建新的对象
newValue = value.constructor == Array ? [] : {};
// 记录该引用
map.set(value, newValue);
for (let key in value) {
const item = value[key];
newValue[key]=item && typeof item === 'object' ? arguments.callee(item, map) : item;
}
}
return newValue;
}
十、手写一个比较完美的继承
function inheritPrototype(subType, superType){
var protoType = Object.create(superType.prototype); //创建对象
protoType.constructor = subType; //增强对象
subType.prototype = protoType; //指定对象
}
function SuperType(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
console.log('name', this.name);
}
function SubType(name, age){
SuperType.call(this, name);
this.age = age;
}
inheritPrototype(SubType, SuperType)
SubType.prototype.sayAge = function(){
console.log('age', this.age);
}
var instance = new SubType("Bob", 18);
instance.sayName();
instance.sayAge();
更多个人文章
- 两个跨域页面进行跳转传参的终极方案
- 深入理解Event Loop的运行机制
- hashHistory和browserHistory的区别
- 面试秘籍之排序算法