js 要会手写的API,各个函数后面有相应用法
1. 实现深拷贝
简易版深拷贝
function deepClone (obj = {
}) {
if (typeof obj !== "object" || obj === null) {
return obj;
}
let result;
if (obj instanceof Array) {
result = [];
} else if (obj instanceof Object) {
result = {
};
}
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
// 判断当前这个key是在obj对象上,而不是在原型上
result[key] = deepClone(obj[key]);
}
}
return result;
}
let obj1 = {
name: 'obj1',
age: 18
};
let obj2 = deepClone(obj1);
obj2.age = 20;
console.log(obj1);
console.log(obj2);
2. 实现节流
节流:
高频事件触发,但在 n 秒内只会执行一次,所以节流会稀释函数的执行频率。
思路:每次触发事件时都判断
当前是否有等待执行的延时函数
function throttle(fn, delay = 200) {
let timer = null;
return function () {
if (timer) {
return ;
}
timer = setTimeout(() => {
fn.call(this, arguments);
timer = null;
}, delay);
}
}
// 下面以盒子拖拽为例,HTML代码就不写了
let box = document.getElementById('box');
box.addEventListener('drag', throttle(function (e) {
console.log(e[0].offsetX, e[0].offsetY)
}), 100);
3. 实现防抖
防抖:
触发高频事件后 n 秒内函数只会执行一次,如果 n 秒内高频事件再次被触发,则重新计算时间
思路:每次触发事件时都取消
之前的延时调用方法
function debounce(fn, delay = 1000) {
let timer = null;
return function() {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
fn.call(this, arguments);
timer = null;
}, delay);
}
}
// 下面以一个输入框防抖为例,HTML代码就不写了
let input1 = document.getElementById('input1');
input1.addEventListener('keyup', debounce(function() {
console.log(input1.value);
}), 800)
4. 实现Object.create()
Object.create()
方法用于创建一个新对象。被创建的对象继承另一个对象的原型,在创建新对象时可以指定一些属性。
注:使用Object.create()
方法创建对象时,如果不是继承一个原有的对象,而是创建一个全新的对象
,就要把proto设置为null,即 Object.create(null)
// 通过原型链__proto__来访问到属性
function create(proto) {
function F() {
}
F.prototype = proto;
F.prototype.constructor = F;
return new F();
}
const person = {
age: 1,
name: 'xiaowu'
};
let p1 = create(person);
console.log(p1);
console.log(p1.__proto__);
5. 实现new关键字
new 的实现思路:
- 创建一个新的对象,并继承了fn的原型,该对象也就是当前fn的
实例
改变this指向
,让其指向新创建处理的实例- 返回值res为
object类型
则作为new方法的返回值返回,否则返回上述实例
function myNew(fn, ...args) {
let obj = Object.create(fn.prototype);
let res = fn.apply(obj, args);
return res instanceof Object ? res : obj;
}
function Cat (name, age) {
this.name = name;
this.age = age;
}
Cat.prototype.say = function () {
console.log('我的名字为' + this.name + ', 年龄是' + this.age)
};
let cat1 = myNew(Cat, 'xiao', 12);
6.实现call/apply
call()
方法使用一个指定的 this
值和单独给出的一个或多个参数来调用一个函数。
与 apply()
方法类似,只有一个区别,就是 call()
方法接受的是一个参数列表
,
而 apply()
方法接受的是一个包含多个参数的数组
。
Function.prototype.myCall = function(context = window, ...args) {
// 判断调用对象
if (typeof this !== 'function') {
console.error('type error');
}
let result = null;
context.fn = this;
// 调用函数
if (args) {
result = context.fn(args);
} else {
result = context.fn();
}
delete context.fn;
return result;
};
let obj1 = {
name: 'obj1',
print: function() {
console.log(this.name);
}
};
let obj2 = {
name: 'obj2'};
obj1.print.myCall(obj2);
Function.prototype.myApply = function(context = window, args) {
// 判断调用对象
if (typeof this !== 'function') {
console.error('type error');
}
let result = null;
// 将调用函数设为对象的方法
context.fn = this;
if (args) {
result = context.fn(args);
} else {
result = context.fn();
}
delete context.fn;
return result;
}
7. 实现instanceof
instanceof
用于判断 “对象” 是否是指定构造函数的 “实例”,即判断一个构造函数的prototype属性所指向的对象是否存在另外一个要检测对象的原型链上
function myInstanceof(L, R) {
const baseTypes = ['string', 'number', 'boolean', 'null', 'undefined', 'symbol'];
if (baseTypes.includes(typeof (L))) {
return false;
}
let RP = R.prototype;
L = L.__proto__;
while (true) {
if (L === null) {
return false;
}
if (L === RP) {
return true;
}
L = L.__proto__;
}
}
myInstanceof([1, 2, 3], Array); // true
myInstanceof({
name: 'obj1'}, Array); // false
8. 实现 Promise.all
将多个promise实例包装成一个新的promise实例,
返回的值的顺序是以参数为顺序,与异步请求时间无关
Promise.myAll = function(promises) {
return new Promise((resolve, reject) => {
const len = promises.length;
let results = new Array(len),
count = 0;
for (let i = 0; i < len; i++) {
Promise.resolve(promises[i])
.then(value => {
count++;
results[i] = value;
if (count === len) {
resolve(results);
}
})
.catch(err => {
reject(err);
})
}
})
};
let p1 = new Promise((resolve) => {
setTimeout(resolve, 1000, 'p1');
});
let p2 = new Promise((resolve) => {
setTimeout(resolve, 2000, 'p2');
});
let p3 = new Promise((resolve) => {
setTimeout(resolve, 3000, 'p3');
});
let pAll = Promise.myAll([p1, p2, p3]);
pAll.then(
success => {
console.log(success); // [ 'p1', 'p2', 'p3' ]
},
error => {
console.log(error); // 一旦一个失败,不再往下执行,不管时间是不是最长,也不会再输出之前正确的回调
}
)
9. 实现 Promise.race
通俗的讲,谁先完成就是输出谁,
返回的值是以时间短的回调的值, error也一样
Promise.myRace = function(promises) {
return new promises((resolve, reject) => {
for (const p of promises) {
Promise.resolve(p)
.then(res => {
resolve(res);
})
.catch(err => {
reject(err);
})
}
})
};
let p1 = new Promise((resolve, reject) => {
setTimeout(reject, 1000, 'p1');
});
let p2 = new Promise((resolve) => {
setTimeout(resolve, 2000, 'p2');
});
let p3 = new Promise((resolve) => {
setTimeout(resolve, 3000, 'p3');
});
let pRace = Promise.myRace([p1, p2, p3]);
pRace.then(
success => {
console.log(`pRace ${
success}`); // 'p2'
},
error => {
console.log(`pRace ${
error}`);
}
)
10. 手写用Promise封装的完整 Ajax
function ajax(mode, url, data, flag) {
return new Promise((resolve, reject) => {
let xhr = null;
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
} else {
xhr = new ActiveXObject();
}
mode = mode.toUpperCase();
if (mode === 'GET') {
xhr.open(mode, url + '?' + data, flag); // flag 设置为true,代表异步
xhr.send(null);
} else if (mode === 'POST') {
xhr.open(mode, url, flag);
xhr.setRequestHeader('Content-type','application/x-www-form-urlencoded');
xhr.send(data);
}
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
resolve(xhr.responseText);
} else if (xhr.status === 404) {
reject(new Error('404 Not Found'));
}
}
};
})
}
补充:
- xhr.readyState
- 0 - (未初始化) 还没有调用 send() 方法
- 1 - (载入) 已调用 send() 方法,正在发送请求
- 2 - (载入完成) send() 方法执行完成,已经接收到全部响应内容
- 3 - (交互) 正在解析响应内容
- 4 - (完成) 响应内容解析完成,可以在客户端调用
- xhr.status(记录几个常用状态码)
- 2 开头的状态码
200
服务器已成功处理了请求
201
请求成功并且服务器创建了新的资源
202
服务器已接受请求,但尚未处理
203
服务器已成功处理了请求,但返回的信息可能来自另一资源
204
服务器成功处理了请求,但没有返回任何内容
206
服务器已经成功处理了部分 GET 请求
- 3 开头的状态码
301
永久性重定向,表示请求的资源被分配了新的URL,之后应使用更改的URL
302
临时性重定向,表示请求的资源被分配了新的URL,希望本次访问使用新的URL
303
请求者应当对不同的位置使用单独的 GET 请求来检索响应时,服务器返回此代码
304
客户端发送了一个带条件的 GET 请求且该请求已被允许,而文档的内容(自上次访问以来或者根据请求的条件)并没有改变,则服务器应当返回这个状态码
307
请求的资源临时从不同的URL 响应请求
- 4 开头的状态码
400
服务器无法理解当前请求或请求参数有误
401
当前请求需要用户验证
403
服务器拒绝本次访问(访问权限出现问题)
404
请求失败,请求所希望得到的资源未被在服务器上发现。
- 5 开头的状态码
500
服务器遇到错误,无法完成请求
503
临时的服务器维护或者过载,服务器当前无法处理请求。通常,这只是暂时状态
更多状态码请前往百度百科进行查询
记录总结一下自己学到的东西。可能写得不是很好,有错误的东西请大家指正,互相学习!!