# call/apply/bind
这几个函数的最主要作用就是:改变 this 指向。
基于这个作用,可以延伸出很多额外的作用:
- 合并数组
let arr1 = [1, 2, 3]
let arr2 = [4, 5, 6]
// 合并 arr2 到 arr1
Array.prototype.push.apply(arr1, arr2)
console.log(arr1) // [ 1, 2, 3, 4, 5, 6 ]
- 获取数组中的最大值和最小值
const maxNum = Math.max.apply(Math, [1, 2, 3, 4])
console.log(maxNum) // 4
- 类数组使用数组方法
const arrLike = {
0: 'a',
1: 'b',
length: 2
}
// arrLike.push('c') // TypeError: arrLike.push is not a function
Array.prototype.push.call(arrLike, 'c')
console.log(arrLike) // { '0': 'a', '1': 'b', '2': 'c', length: 3 }
- ......
另外,bind 还能用于柯里化。
function add(num1) {
return function (num2) {
return num1 + num2
}
}
const sum = add(1)(2)
console.log(sum) // 3
# 区别
call 和 apply 主要区别在于传递参数方式不同:
- call:多个参数的列表
- apply:一个包含多个参数的数组
bind 跟 call/apply 不同点主要在于:bind 返回值是一个函数。
# call
// es3
Function.prototype.call1 = function (context) {
context = context || window // 获取目标绑定对象
context.fn = this // 执行函数
// 获取函数参数
var args = []
for (var i = 1; i < arguments.length; i++) {
args.push('arguments[' + i + ']')
}
var result = eval('context.fn(' + args + ')') // 执行函数
delete context.fn
return result
}
// es6
Function.prototype.call2 = function (context, ...args) {
context = context || window // 获取目标绑定对象
context.fn = this // 执行函数
const result = context.fn(...args) // 执行函数
delete context.fn
return result
}
# apply
// es3
Function.prototype.apply1 = function (context, arr) {
context = context || window // 获取目标绑定对象
context.fn = this // 执行函数
var result
if (!arr || !arr.length) {
// 无函数参数
result = context.fn()
} else {
// 获取函数参数
var args = []
for (var i = 0; i < arr.length; i++) {
args.push('arr[' + i + ']')
}
result = eval('context.fn(' + args + ')') // 执行函数
}
delete context.fn
return result
}
// es6
Function.prototype.apply2 = function (context, arr) {
context = context || window // 获取目标绑定对象
context.fn = this // 执行函数
let result
if (!arr || !arr.length) {
// 无函数参数
result = context.fn()
} else {
result = context.fn(...arr) // 执行函数
}
delete context.fn
return result
}
# bind
Function.prototype.bind1 = function (context) {
if (typeof this !== 'function') {
throw new Error(
'Function.prototype.bind - what is trying to be bound is not callable'
)
}
context = context || window
var self = this // 执行函数
// 保存第一层参数
var args = Array.prototype.slice.call(arguments, 1) // arguments 是类数组,需要通过 Array.prototype.slice.call 获取函数参数
var noopFn = function () {} // 空函数,用于修改返回的执行函数 boundFn 的 prototype
var boundFn = function () {
// 返回的函数
// 获取第二层参数
var boundArgs = Array.prototype.slice.call(arguments)
return self.apply(
this instanceof noopFn ? this : context, // 判断是否为实例化对象
args.concat(boundArgs)
)
}
// 修改返回的执行函数 boundFn 的 prototype 为执行函数的 prototype
// 这样实例就可以继承绑定函数原型的属性和方法
noopFn.prototype = this.prototype
boundFn.prototype = new noopFn()
return boundFn
}
# instanceof
// 实现 instanceof
function myInstanceof (left, right) {
let leftPrototype = left.__proto__
let rightPrototype = right.prototype
while (true) {
if (leftPrototype === null) {
return false
}
if (leftPrototype === rightPrototype) {
return true
}
leftPrototype = leftPrototype.__proto__
}
}
# new
function (Constructor, ...args) {
// 创建一个对象
const obj = new Object()
// 链接到原型
obj.__proto__ = Constructor.prototype
// 修改 this 指向并执行函数
const res = Constructor.apply(obj, args)
// 返回结果
return typeof res === 'object' ? obj : res
}