# 手写代码
面试官为啥总是让我们手撕 call、apply、bind? (opens new window)
# call
call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。
// mdn 示例
function Product(name, price) {
this.name = name
this.price = price
}
function Food(name, price) {
Product.call(this, name, price)
this.category = 'food'
}
console.log(new Food('cheese', 5))
// 手写call
function myCall(obj, ...args) {
const context = obj || window
const fn = Symbol()
context[fn] = this
let res
res = context[fn](...arg)
delete context[fn]
return res
}
Function.prototype.myCall = myCall
function Food(name, price) {
Product.call(this, name, price)
this.category = 'food'
}
console.log(new Food('cheese', 5))
# apply
apply() 方法调用一个具有给定 this 值的函数,以及以一个数组(或一个类数组对象)的形式提供的参数。
const array = ['a', 'b']
const elements = [0, 1, 2]
array.push.apply(array, elements)
console.log(array) // ["a", "b", 0, 1, 2]
function myApply(obj, args) {
const context = obj || window
const fn = Symbol()
context[fn] = this
let res
res = context[fn](...args)
delete context[fn]
return res
}
Function.prototype.myApply = myApply
array.push.myApply(array, [3, 4, 5])
console.log(array)
# bind
bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
const module = {
x: 42,
getX: function () {
return this.x
},
}
const unboundGetX = module.getX
console.log(unboundGetX()) // The function gets invoked at the global scope
// expected output: undefined
const boundGetX = unboundGetX.bind(module)
console.log(boundGetX())
// expected output: 42
function myBind(obj, ...argsOut) {
const context = obj || window
const fn = Symbol()
context[fn] = this
return function (...argsIn) {
return context[fn](...argsOut, ...argsIn)
}
}
Function.prototype.myBind = myBind
const myBindGetX = unboundGetX.myBind(module)
console.log(boundGetX())
function twoPlus(a, b) {
return 2 + a + b
}
// const sum = twoPlus.myBind(null, 1)
const sum = twoPlus.bind(null, 1)
sum(2)
# new
new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象类型之一
// 正常new的使用
function Car(make, model, year) {
this.make = make
this.model = model
this.year = year
}
const car1 = new Car('Eagle', 'Talon TSi', 1993)
console.log(car1.make)
// 第二版的代码
function objectFactory() {
// 创建一个新的对象
var obj = new Object()
// 取出参数的第一项 构造函数
Constructor = [].shift.call(arguments)
console.log(arguments)
// 将构造函数的原型变为新对象的原型 (每个函数都有prototype 指向原型对象)让实例对象具有构造函数原型对象的属性
obj.__proto__ = Constructor.prototype
// 调用函数改变this的为新的对象 让实例对象具有构造函数的属性
var ret = Constructor.apply(obj, arguments)
// 当实际构造函数有返回值为对象时 返回该对象 否则返回新对象
return typeof ret === 'object' ? ret : obj
}
const car2 = objectFactory(Car, 'Eagle', 'Talon TSi', 1994)
# interface
// son interface Father
function interfaceFn(obj, pro) {
const left = obj.__proto__
const right = pro.prototype
while (true) {
if (left === right) {
return true
} else if (left === null) {
return false
} else {
left = left.__proto__
}
}
}
# 防抖 debounce
频繁触发只触发
一次
lodash.debounce (opens new window)
- 首次执行
- 取消
function debounce(fn, time) {
let timer
return function () {
clearTimeout(timer)
const args = arguments
timer = setTimeout(() => {
fn.apply(this, args)
}, time)
}
}
# 节流 throttle
频繁的触发控制触发频率 (可以触发
多次
) 返回的是一个函数
lodash.throttle (opens new window)
- 首次执行
- 结束执行
const input = () => {}
const fn = throttle(input, 1000)
document.addEventListener('scroll', fn())
function throttle(fn, time) {
let start = 0
return function () {
let end = new Date()
if (end - start > time) {
fn.apply(this, arguments)
start = end
}
}
}