1、实现instanceOf操作符
/** 手写实现instanceof */
function myInstanceOf(origin, target) {
// 1. 获取对象的prototype
let proto = Object.getPrototypeOf(origin);
// 2. 获取目标构造函数的原型对象
let prototype = target.prototype;
// 3. 循环对比对象的原型链是否和构造函数的原型对象相等,遇到相等则返回true,否则遍历结束返回false
while(proto) {
if(proto === prototype) {
return true;
}
proto = Object.getPrototypeOf(proto);
}
return false;
}
let a = new Array();
console.log(a instanceof Object); // true
console.log(myInstanceOf(a, Object)); // true
console.log(myInstanceOf(a, Array)); // true
console.log(myInstanceOf(a, String)); // false
2、实现new操作符
/** 手写实现new操作符 */
function myNew(Fn) {
// 1. 创建一个空的对象
let obj = function() {};
// 2. 将构造函数的原型链赋值给对象
obj = Object.create(Fn.prototype);
// 3. 将构造函数的this指针指向对象
let args = [...arguments].slice(1);
let result = Fn.apply(obj, args);
// 4. 判断result是否是引用类型,是的话返回result,否则返回obj;
if(typeof result === 'function' || result instanceof Object) {
return result;
}
return obj;
}
function Foo (name, age){
this.name = name;
this.age = age;
}
Foo.prototype.sayAge = function () {
return this.age;
}
const foo = myNew(Foo, 'allesa', 32);
const bar = myNew(Foo, 'jason', 33);
console.log(foo); // Foo { name: 'allesa', age: 32 }
console.log(bar); // Foo { name: 'jason', age: 33 }
console.log(foo.name); // allesa
console.log(foo.sayAge()); // 32
console.log(bar.name); // jason
console.log(bar.sayAge()); // 33
3、实现浅拷贝函数
/** 手写浅拷贝 shallowClone */
/**
* 已有浅拷贝实现:
* 1. Array.slice()、Array.concat()
* 2. Object.assign()
* 3. 扩展运算符:obj2 = { ...obj1 };
*/
function shallowClone (obj) {
if(!obj || typeof obj !== 'object') return;
let newObj = Array.isArray(obj) ? [] : {};
// 遍历对象属性,进行浅拷贝
for(let key in obj) {
if(obj.hasOwnProperty(key)) {
newObj[key] = obj[key];
}
}
return newObj;
}
let obj1 = {
a: 1,
b: [1, 3, 5],
c: { d: 3 }
};
let obj2 = shallowClone(obj1);
console.log(obj1.b === obj2.b); // true
4、实现深拷贝函数
/** 手写深拷贝函数 deepClone */
/**
* 已有深拷贝实现:
* 1. 使用JSON函数:JSON.parse(JSON.stringfy(obj))
* 2. 使用lodash:_.deepClone(obj)
*/
function deepClone (obj) {
if(!obj || typeof obj !== 'object') return;
let newObj = Array.isArray(obj) ? [] : {};
// 遍历对象进行深拷贝
for(let key in obj) {
if(obj.hasOwnProperty(key)) {
newObj[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key];
}
}
return newObj;
}
let obj1 = {
a: 2,
b: [2,3,4],
c: {
d: 1,
}
}
let obj2 = deepClone(obj1);
console.log(obj1.b === obj2.b); // false
5、实现防抖函数
/** 手写防抖函数debounce:事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则重新计时 */
function debounce(Fn, timeout) {
let timer = null;
return function() {
// 如何计时器存在的话,则清除之前已有的计时器,重新创建新的定时器
if(timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
Fn.apply(this, arguments);
}, timeout);
}
}
function click(count) {
console.log(count, Date.now());
}
let debounceFunc = debounce(click, 2000);
debounceFunc(1); // 无输出
// 在2s内再次出触发事件,只执行最后一次
setTimeout(() => debounceFunc(2), 1000); // 2 1715827126184
// 在2s外触发,执行
setTimeout(() => debounceFunc(3), 4000); // 3 1715827129183
6、实现节流函数
/** 手写节流 throttle: 一段时间内,事件只触发一次 */
// 时间戳对比实现
function timestampThrottle(Fn, wait) {
let lastTimestamp = null;
return function() {
let nowTimestamp = Date.now();
if(lastTimestamp === null || nowTimestamp - lastTimestamp >= wait) {
lastTimestamp = Date.now();
Fn.apply(this, arguments);
}
}
}
// 定时器实现
function throttle(Fn, wait) {
let timer = null;
return function() {
if(!timer) {
Fn.apply(this, arguments);
timer = setTimeout(() => {
timer = null;
}, wait);
}
}
}
function click(count) {
console.log(count, Date.now());
}
let throttleFunc = timestampThrottle(click, 2000);
throttleFunc(1); // 1 1715827197169
// 在2s内再次出触发事件,不执行
setTimeout(() => throttleFunc(2), 1000); // 无输出
// 在2s外触发,执行
setTimeout(() => throttleFunc(3), 4000); // 3 1715827201174
7、实现Promise.all函数
/** 手写Promise.all函数 */
function myPromiseAll(promises) {
if(!(promises instanceof Array)) {
throw new Error('params must be array');
}
let resultList = [];
return new Promise((resolve, reject) => {
for(let i=0; i<promises.length; i++) {
Promise.resolve(promises[i]).then(value => {
resultList.push(value);
if(resultList.length === promises.length) {
resolve(resultList);
}
}).catch(reason => reject(reason));
}
})
}
// 成功
Promise.all([1, 2, Promise.resolve(3), Promise.resolve(4)]).then(result => console.log(result)).catch(reason => console.log(reason)); // [ 1, 2, 3, 4 ]
myPromiseAll([1, 2, Promise.resolve(3), Promise.resolve(4)]).then(result => console.log(result)).catch(reason => console.log(reason)); // [ 1, 2, 3, 4 ]
// 失败
Promise.all([1, 2, Promise.resolve(3), Promise.reject(4)]).then(result => console.log(result)).catch(reason => console.log(reason)); // 4
myPromiseAll([1, 2, Promise.resolve(3), Promise.reject(4)]).then(result => console.log(result)).catch(reason => console.log(reason)); // 4
8、实现柯里化函数
/** 手写柯里化函数curry */
// 柯里化函数的作用是将fn(a, b, c)方式的函数调用转换为 fn(a)(b)(c)形式
// es6实现
function curryEs6(fn, ...args) {
return fn.length <= args.length ? fn(...args) : curryEs6.bind(null, fn, ...args);
}
// 普通函数实现
function curryCommon(fn, args) {
let len = fn.length;
args = args || [];
return function () {
let newArgs = args.concat([...arguments]);
if(newArgs.length >= len) {
return fn.apply(this, newArgs);
} else {
return curryCommon.call(this, fn, newArgs);
}
}
}
function sum(a, b, c) {
return a+b+c;
}
let commonCurry = curryCommon(sum);
console.log(commonCurry(1)(2)(3)); // 6
let esCurry = curryEs6(sum, 1);
console.log(esCurry(2)(3)); // 6
9、实现Function.prototype.bind函数
/** 手写Function.prototype.bind函数 */
Function.prototype.bind = function (context) {
// 1. 限制调用方必须是函数类型
if(typeof this !== 'function') {
throw 'Caller must be function';
}
// 2. 预存this和入参
let fn = this, args = [...arguments].slice(1);
// 3. 返回一个新的函数
return function Fn() {
// 4. 调用apply修改this的指向,此时需要判断是否使用new 操作符创建,new 的话要返回新对象this,否则使用context
return fn.apply(this instanceof Fn ? this : context, args.concat(...arguments));
}
}
function Foo(name, age) {
this.name = name;
this.age = age;
}
function Bar(){}
Bar.prototype.sayHello = function (sex) {
console.log(`Hello, my name is ${this.name}, age ${this.age}, sex ${sex}`);
}
let sayHelloFoo = Bar.prototype.sayHello.bind(new Foo('张三', 22), 'male');
sayHelloFoo(); // Hello, my name is 张三, age 22, sex male
let sayHelloEmpty = Bar.prototype.sayHello.bind({name: '李四', age: 32});
sayHelloEmpty('female'); // Hello, my name is 李四, age 32, sex female
// 通过new 的对象调用
let FooBind = Foo.bind({}, '王五');
let person = new FooBind(33);
console.log(person.name, person.age); // 王五 33
10、实现Function.prototype.call函数
/** 手写Function.prototype.call函数 */
Function.prototype.call = function(context) {
// 1. 判断调用该方法的必须为函数类型
if(typeof this !== 'function') {
throw 'Call must called by function';
}
// 2. 如果没有传递context,则默认使用window
context = context || window;
// 3. 在context上定义执行函数,获取context的上下文
context.fn = this;
// 4. 获取调用参数并调用context.fn执行调用函数逻辑
let args = [...arguments].slice(1);
let result = context.fn(...args);
// 5. 获取了结果后删除context.fn
delete context.fn;
// 6. 返回结果
return result;
}
function Foo() {
this.name = '张三';
this.age = 32;
}
function Bar() {
this.name = '李四';
this.age = 22;
}
Bar.prototype.sayHello = function () {
console.log(`Hello, My name is ${this.name}, age ${this.age}`);
}
let foo = new Foo();
let bar = new Bar();
bar.sayHello(); // Hello, My name is 李四, age 22
bar.sayHello.call(foo); // Hello, My name is 张三, age 32
11、实现Function.prototype.apply函数
/** 手写Function.prototype.apply函数 */
Function.prototype.apply = function (context) {
// 1. 先判断调用方类型是否为函数
if(typeof this !== 'function') {
throw 'Caller must be a function';
}
// 2. 如果传递了目标对象则使用目标对象,如果没传递默认使用window
context = context || window;
// 3. 在目标对象上添加函数指向调用方,获取目标对象的上下文
context.fn = this;
// 4. 获取入参,并调用context.fn将入参传入进去执行,获取结果
let result = null;
if(arguments[1]) {
result = context.fn(...arguments[1]);
} else {
result = context.fn();
}
// 5. 删除context.fn定义
delete context.fn;
// 6. 返回执行结果
return result;
}
function Foo() {
this.name = '张三';
this.age = 32;
}
function Bar() {
this.name = '李四';
this.age = 22;
}
Bar.prototype.sayHello = function () {
console.log(`Hello, My name is ${this.name}, age ${this.age}`);
}
let foo = new Foo();
let bar = new Bar();
bar.sayHello(); // Hello, My name is 李四, age 22
bar.sayHello.apply(foo); // Hello, My name is 张三, age 32
12、实现ajax请求
/** 手写ajax请求 */
function ajax(url) {
// 1. 创建xhr对象
let xhr = new XMLHttpRequest();
// 2. 指定get请求和url并打开链接
xhr.open('GET', url, true);
// 3. 监听readyState变化,判断场景处理结果
xhr.onreadystatechange = function () {
if(this.readyState === 4) {
// 请求成功
if(this.status === 200) {
console.log(this.response);
} else {
console.error(this.statusText);
}
}
}
// 4. 监听错误事件
xhr.onerror = function() {
console.error(this.statusText);
}
// 5. 设置header以及发送数据
xhr.responseType = 'json';
xhr.setRequestHeader('Accept', 'application/json')
xhr.send(null);
}