1.实现一个浅拷贝
function clone(source) {
if (typeof source === "object" && source !== null) {
//注意识别target是数组还是对象
const cloneTarget = Array.isArray(source) ? [] : {};
//for in可以遍历对象的所有属性包括继承的
for (let prop in source) {
if (source.hasOwnProperty(prop)) {
cloneTarget[prop] = source[prop];
}
}
} else return source;
}
2.实现一个深拷贝(递归拷贝newTarget的属性)
if (typeof source === "object" && source !== null) {
//注意识别target是数组还是对象
const cloneTarget = Array.isArray(source) ? [] : {};
//for in可以遍历对象的所有属性包括继承的
for (let prop in source) {
if (source.hasOwnProperty(prop)) {
//和浅拷贝的实现唯一不同的地方,递归拷贝
cloneTarget[prop] = deepclone(source[prop]);
}
}
} else return source;
3.实现一个深拷贝(进阶版,考虑函数,正则,循环引用等特殊情况)
var isObject = (source) => {
return (
source !== null &&
(typeof source === "object" || typeof source === "function")
);
};
function deepclone(source, map = new Map()) {
// 返回已缓存的对象
if (map.get(source)) return source;
// 直接返回!isObject的对象
if (!isObject(source)) return source;
const constructor = source.constructor;
// 检测当前对象source是否与正则,日期等对象匹配
if (/^(RegExp|Date)$/i.test(constructor.name)) {
// 返回一个新的(正则类/日期类)的对象
return new constructor(source);
}
const cloneTarget = Array.isArray(source) ? [] : {};
// 缓存对象,给对象作标记
map.set(source, true);
for (let prop in source) {
if (source.hasOwnProperty(prop)) {
// map为循环引用的对象做标记,防止无限递归
cloneTarget[prop] = deepclone(source[prop], map);
}
}
return cloneTarget;
}
循环引用,简单来说,就是对象a的属性值指向对象b,对象b的属性值指向对象a。
4.实现instanceOf
function instanceOf(obj, target) {
target = target.prototype;
obj = obj.__proto__; // 处理兼容性问题,可以使用Object.getPrototypeOf(),更推荐使用后者
while (true) {
if (obj === target) return true;
if (obj === null) return false;
obj = obj.__proto__;
}
}
function instanceOf(obj, target) {
target = target.prototype;
obj = Object.getPrototypeOf(obj);
while (true) {
if (obj === target) return true;
if (obj === null) return false;
obj = Object.getPrototypeOf(obj);
}
}
5.实现节流防抖函数
定义
- 节流: n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效
- 防抖: n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时
一个经典的比喻:
想象每天上班大厦底下的电梯。把电梯完成一次运送,类比为一次函数的执行和响应
假设电梯有两种运行策略
debounce和throttle,超时设定为15秒,不考虑容量限制电梯第一个人进来后,15秒后准时运送一次,这是节流
电梯第一个人进来后,等待15秒。如果过程中又有人进来,15秒等待重新计时,直到15秒后开始运送,这是防抖
(1)实现防抖函数
①触发防抖函数后,n秒后执行fn
function debounce(fn, delay) {
// 闭包,形成私有变量
let timer = null;
return function () {
// 清除定时器,避免多次触发fn
clearTimeout(timer);
timer = setTimeout(() => {
fn.call(this, arguments);
}, delay);
};
}
②触发防抖函数后,若是第一次触发,则立即执行fn,否则n秒后执行fn
function debounce2(fn, delay, immediate) {
let timer = null;
return function () {
// 第一次触发时timer为null
let now = !timer;
// immediate为true
if (immediate) {
if (now) {
// timer为null,立即执行fn函数
fn.call(this, arguments);
}
timer = setTimeout(() => {
timer = null;
}, delay);
}
// else代码块的处理逻辑与①相同
else {
clearTimeout(timer);
timer = setTimeout(() => {
timer = null;
fn.call(this, arguments);
}, delay);
}
};
}
(2)实现节流函数,n秒内只执行一次
function throttle(fn, delay) {
let preTime = Date.now();
let timer = null;
return function () {
// 时间戳,记录当前的时间
let nowTime = Date.now();
// 从上一次到现在剩余的时间
let leftTime = delay - (nowTime - preTime);
clearTimeout(timer);
if (leftTime < 0) {
fn.call(this, arguments);
preTime = Date.now();
} else {
timer = setTimeout(() => {
preTime = Date.now();
fn.call(this, arguments);
}, leftTime);
}
};
}
6.实现Object.create()函数
Object.prototype.create = function (target) {
if (target !== "object" || !target) throw Error("must be an object");
let func = function () {};
func.prototype = target;
return new func();
};
7.实现new函数
function new(target, ...arguments) {
let obj = Object.create(target.prototype);
// obj.__proto__ === target.prototype
let result = target.call(obj, ...arguments);
// 需要考虑构造函数的返回值是函数或者对象
if (result && /^(function|object)$/.test(result)) return result;
return obj;
}
8.实现call,apply,bind
(1)call
/**
* context: 要改变的函数中的this指向,写谁就是谁
* args:传递给函数的实参信息
* this:要处理的函数 fn
*/
Function.prototype.call = function () {
// arguements类数组对象
var context = Array.prototype.slice.call(arguments, 0)[0];
// var context = Array.of(arguments)[0],等效于上一行代码
context = context || window; // 兼容性处理
var args = Array.prototype.slice.call(arguments, 1);
context["fn"] = this; // context添加属性fn,fn指向要处理的函数this
var res = context["fn"](...args);
delete context["fn"];
return res;
};
(2)apply
Function.prototype.apply = function (context, args) {
context = context || window;
context.fn = this;
var res = context.fn(...args);
delete context.fn;
return res;
};
(3)bind
Function.prototype.bind = function (context, ...args) {
context = context || window;
var self = this;
var args1 = Array.of(...args);
function f1() {}
function f2() {
var args2 = Array.prototype.slice.call(arguments);
return self.apply(
this instanceof f2 ? this : context,
args1.concat(args2)
);
}
f1.prototype = self.prototype;
f2.prototype = new f1();
return f2;
};
未完待续...