# JS 手写题
# 防抖
function debounce(fn, delay) {
var timer = null
return function(...args) {
var that = this
clearTimeout(timer)
timer = setTimeout(function() {
fn.apply(that, args)
}, delay)
}
}
# 节流
// 基础版1:时间戳(第一次触发会执行,但不排除不执行的可能)
function throttle(fn, delay) {
var prev = Date.now()
return function(...args) {
var dist = Date.now() - prev
if (dist >= delay) {
fn.apply(this, args)
prev = Date.now()
}
}
}
// 基础版2:定时器(最后一次也会执行)
function throttle(fn, delay) {
var timer = null
return function(...args) {
var that = this
if(!timer) {
timer = setTimeout(function() {
fn.apply(this, args)
timer = null
}, delay)
}
}
}
// 进阶版:开始执行、结束执行
function throttle(fn, delay) {
var timer = null
var prev = Date.now()
return function(...args) {
var that = this
var remaining = delay - (Date.now() - prev) // 剩余时间
if (remaining <= 0) { // 第 1 次
fn.apply(that, args)
prev = Date.now()
} else { // 第 1 次之后
timer && clearTimeout(timer)
timer = setTimeout(function() {
fn.apply(that, args)
}, remaining)
}
}
}
# apply
// 方式1
function apply(fn, context, arr) {
var s = Symbol()
context[s] = fn
const result = context[s](...arr)
Reflect.deleteProperty(context, s)
return result
}
// 方式2
Function.prototype.myApply = function(context, arr) {
let result
if (typeof context !== 'object') {
result = this(...arr)
return result
}
context.fn = this
result = context.fn(...arr)
Reflect.deleteProperty(context, 'fn')
return result
}
# call
Function.prototype.myCall = function(context, ...rest) {
var result
context.fn = this
result = context.fn(...rest)
Reflect.deleteProperty(context, 'fn')
return result
}
# bind
Function.prototype.myBind = function(context, ...args0) {
var originFn = this
return function F(...args1) {
var result
// 判断是否通过 new 调用
if (new.target === F) { // this instanceof F
return new originFn(...args0, ...args1)
} else {
return originFn.apply(context, args0.concat(args1))
}
}
}
这篇文章推荐,讲挺好如何模拟实现 JS 的 bind 方法 (opens new window)
// 第三版 实现new调用
Function.prototype.bindFn = function bind(thisArg){
if(typeof this !== 'function'){
throw new TypeError(this + ' must be a function');
}
// 存储调用bind的函数本身
var self = this;
// 去除thisArg的其他参数 转成数组
var args = [].slice.call(arguments, 1);
var bound = function(){
// bind返回的函数 的参数转成数组
var boundArgs = [].slice.call(arguments);
var finalArgs = args.concat(boundArgs);
// new 调用时,其实this instanceof bound判断也不是很准确。es6 new.target就是解决这一问题的。
if(this instanceof bound){
// 这里是实现上文描述的 new 的第 1, 2, 4 步
// 1.创建一个全新的对象
// 2.并且执行[[Prototype]]链接
// 4.通过`new`创建的每个对象将最终被`[[Prototype]]`链接到这个函数的`prototype`对象上。
// self可能是ES6的箭头函数,没有prototype,所以就没必要再指向做prototype操作。
if(self.prototype){
// ES5 提供的方案 Object.create()
// bound.prototype = Object.create(self.prototype);
// 但 既然是模拟ES5的bind,那浏览器也基本没有实现Object.create()
// 所以采用 MDN ployfill方案 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/create
function Empty(){}
Empty.prototype = self.prototype;
bound.prototype = new Empty();
}
// 这里是实现上文描述的 new 的第 3 步
// 3.生成的新对象会绑定到函数调用的`this`。 -- 重点
var result = self.apply(this, finalArgs);
// 这里是实现上文描述的 new 的第 5 步
// 5.如果函数没有返回对象类型`Object`(包含`Functoin`, `Array`, `Date`, `RegExg`, `Error`),
// 那么`new`表达式中的函数调用会自动返回这个新的对象。
var isObject = typeof result === 'object' && result !== null;
var isFunction = typeof result === 'function';
if(isObject || isFunction){
return result;
}
return this;
}
else{
// apply修改this指向,把两个函数的参数合并传给self函数,并执行self函数,返回执行结果
return self.apply(thisArg, finalArgs);
}
};
return bound;
}
# 实现 new
function myNew(fn, ...args) {
// 1. 生成新对象,并绑定原型
var obj = Object.create(fn.prototype)
// 2. 绑定参数
var result = fn.apply(obj, args)
// 3. 返回结果
return typeof result === 'object' ? result : obj
}
# 深拷贝
// 基础版
function deepClone(origin) {
if (origin == undefined || typeof origin != 'object') {
return origin
}
var result = new origin.constructor()
for (var k in origin) {
result[k] = typeof origin[k] === 'object' ? deepClone(origin[k]) : origin[k]
}
return result
}
// 解决循环引用版
function deepClone(origin, wm = new WeakMap()) {
if (origin == undefined || typeof origin != 'object') {
return origin
}
var val = wm.get(origin)
// 如果 wm 存在,则直接返回
if (val) {
return wm.get(origin)
}
var target = new origin.constructor()
wm.set(origin, target)
for (var k in origin) {
target[k] = typeof origin[k] === 'object' ? deepClone(origin[k], wm) : origin[k]
}
return target
}