1. 防抖
function debounce (fn, delay = 300) {
let timer = null;
return function (...arg)=> {
if(timer) {
cleanTimeout(timer);
return;
}
timer = setTimeout(()=>{
fn.apply(this,arg)
}, delay)
}
}
2. 节流
// 判断执行时间是否大于延迟时间再执行
function throttle(fn, delay = 300) {
let oldTime = 0;
return function(...args) {
let nowTime = Date.now();
if (nowTime-oldTime > delay) {
oldTime = nowTime;
fn.apply(this,args);
}
}
}
3. 深拷贝
function deepClone(obj, cache = new WeakMap()) {
// 非object判断
if (obj === null || typeof obj !== 'object') return obj
// 其他object判断
if (obj instanceof Date) return new Date(obj)
if (obj instanceof RegExp) return new RegExp(obj)
// 缓存判断是否有
if (cache.get(obj)) return cache.get(obj)
let cloneObj = Object.prototype.toString.call(obj) === '[object Array]' ? [] : {};
cache.set(obj, cloneObj);
// 递归克隆
for (let key in obj) {
if (obj.hasOwnProperty(key)) continue;
cloneObj[key] = deepClone(obj[key], cache);
}
return cloneObj;
}
4. 手写call
Function.prototype.myCall= function (obj) {
// obj为空时,就执行当前函数
let newObj = obj || window;
newObj.fn = this;
// 如果有参数
let otherArg = [];
for (let i = 1;i < arguments.length;i++) {
// 拼接参数
otherArg.push('arguments[' + i + ']');
}
// 执行方法
let result = eval('newObj.fn(' + otherArg + ')');
// 不应该改变原对象
delete newObj.fn;
// 结果要返回
return result;
}
5. 手写apply
与call的思路差不多,区别是第二参数传的是数组 因此参数调用的是arguments[1][i]
Function.prototype.myApply = function(obj) {
let newObj = obj || window;
newObj.fn = this;
let otherArg = [];
// 遍历的是arguments[1],存放的是数组,i要从0开始
for (let i = 0;i<arguments[1].length;i++) {
otherArg.push('arguments[1][' + i + ']');
}
let result = eval('newObj.fn(' + otherArg + ')');
// 别忘了删除fn
delete newObj.fn;
return result;
}
6. 手写bind
bind函数传值跟call一样,
- bind返回的是一个函数
- 也可以new一个bind返回的函数
Function.prototype.myBind = function(obj) {
// 1. 防止this不是function
if (typeof this !== 'function') {
throw new TypeError('错误');
}
// 2. 保存this;参数对象转数组;定义fun
const that = this;
const argArr = Array.prototype.slice.call(arguments, 1);
const fun1 = function() {};
const fun2 = function() {
if (arguments.length) {
for (let i = 0;i<arguments.length;i++) {
argArr.push(arguments[i]);
}
}
// 4. 判断是不是new了
if (this instanceof fun1) {
that.apply(this, argArr);
} else {
that.apply(that, argArr);
}
}
// 3. 把原型链串联好,让new 好的实例可以使用到that的原型对象属性
// 使用原型式继承来避免直接修改原型对象
fun1.prototype = that.prototype
fun2.prototype = new fun1;
return fun2;
}
7. 手写一个new
- 创建一个空对象,对象__proto__属性指向new的函数的prototype
- 执行构造函数中的代码,因为构造函数可能是通过this给新对象添加新的成员属性或方法。
- 返回对象
function _new(Fun) {
return function () {
// 写法1
var obj = {};
// 修改指向,认爹
obj.__proto__ = Fun.prototype;
// 写法2
// 建个对象,顺便修改指向,认爹
var obj = Object.create(Fun.prototype);
// 写法3
// 建个对象,
var obj = {};
Object.setPrototypeOf(obj, Fun.prototype); // 修改指向,认爹
let result = Fun.apply(obj, arguments);
var isObject = typeof result === "object" && result !== null;
var isFunction = typeof result === "function";
if (isObject || isFunction) {
return result;
}
return obj;
};
}
function Person(other) {
this.age = 30;
this.name = "sds";
this.other = other;
}
var newEd = _new(Person)("other prams1111");
console.log(newEd);
8. instanceof实现原理
instanceof主要用于判断某个实例是否属于某个类型、是否其父辈类型的实例 因此只要右边变量的prototype在左边的prototype的原型链上即可
const myInstanceof = (judValue, rootValue) => {
let judValueProto = judValue.__proto__;
let rootValueProto = rootValue.prototype;
while(true) {
if (judValueProto === null) return false
if (judValueProto === rootValueProto) return true
judValueProto = judValueProto.__proto__;
}
}
9. 手写快排
①拿出一个中间数
②遍历判断值在左或右并存入数组
③继续递归左右数组并合并,记得合并中间数
④返回合并结果
const quickSort = arr => {
if (arr.length <= 1) return arr;
let center = arr.shift(),
leftArr = [],
rightArr = [],
result = [];
for (let i = 0;i <=arr.length; i++) {
arr[i] <= center && leftArr.push(arr[i]);
arr[i] > center && rightArr.push(arr[i]);
}
result = quickSort(leftArr).concat(center, quickSort(rightArr));
return result;
}