call/apply/bind
作用: 指定函数作用域上下文(this指向)。
区别: call/apply立即执行,bind会返回一个绑定了上下文的函数。
参数异同:
-
参数一:可选
- 如果是null/undefined,非严格模式下则函数内部的this指向window,严格模式下this为undefined。
- 如果是其他基本类型的值,则this指向相应的基本包装类型的实例。
-
函数参数:可选
- call、bind从第二个参数开始依次传入指定参数。
- apply则传递一个数组或类数组。如果传入的是null/undefined、函数、普通对象(length的值为undefined)则参数默认是空数组。如果是其他基本类型的值,则抛出
TypeError
类型的错误。
Object:
作为构造函数使用时会为给定值创建一个对象包装器。如果给定值是 null/undefined,将会创建并返回一个空对象;如果给定值是其他基本类型的值,返回基本包装类型实例;如果是复杂类型,则原封不动返回。
当以非构造函数形式被调用时,Object 等同于 new Object()。
call
ES5实现:
Function.prototype.callX = function (content) {
// 调用者必须是函数
if (typeof this !== 'function') {
throw new TypeError(this + ' is not a function')
}
// 参数一不允许是null或undefined
content = content == null ? globalThis : Object(content)
content._symbolCall = this
var args = []
for (var i = 1; i < arguments.length; i++) {
args.push('arguments[' + i + ']')
}
// 数组和字符串拼接会调用数组的toString()方法
var result = eval('content._symbolCall(' + args + ')')
delete content._symbolCall
return result
}
content
如果是null/undefined,则this指向window,如果是其他基本类型的值,则通过Object()
转成基本包装类型的实例,如果是对象类型(包括函数),则原封不动返回。
ES6实现:
Function.prototype.callY = function (content, ...rest) {
if (typeof this !== 'function') {
throw new TypeError(`${this} is not a function`)
}
content = content == null ? globalThis : Object(content)
// 保证属性的唯一性,以防污染content自身属性
const symbol = Symbol('fn')
content[symbol] = this
const result = content[symbol](...rest)
delete content[symbol]
return result
}
apply
ES5实现:
Function.prototype.applyX = function (content, arr) {
if (typeof this !== 'function') {
throw new TypeError(this + ' is not a function')
}
content = content == null ? globalThis : Object(content)
var arrType = typeof arr
// arr是null、undefined或没有length属性的对象会忽略 显式判断length是否为undefined排除以为0的情况
if (arr == null || arrType === 'function' || (arrType === 'object' && arr.length === undefined)) {
arr = []
} else if (arrType !== 'object') {
// 抛出错误
throw new TypeError('CreateListFromArrayLike called on non-object')
}
content._symbolApply = this
var args = ''
for (var i = 0; i < arr.length; i++) {
args += ', ' + arr[i]
}
args = args.substring(2)
var restult = eval('content._symbolApply(' + args + ')')
delete content._symbolApply
return restult
}
ES6实现:
Function.prototype.applyY = function (content, arr) {
if (typeof this !== 'function') {
throw new TypeError(this + ' is not a function')
}
content = content == null ? globalThis : Object(content)
const arrType = typeof arr
if (arr == null || arrType === 'function' || (arrType === 'object' && arr.length === undefined)) {
arr = []
} else if (arrType !== 'object') {
throw new TypeError('CreateListFromArrayLike called on non-object')
}
const symbol = Symbol('fn')
content[symbol] = this
const restult = content[symbol](...Array.from(arr))
delete content[symbol]
return restult
}
以上使用Array.from()
而不用...
扩展运算,是因为扩展运算只能转换数组或具有Iterator
接口的对象。而Array.from()
还可以转具有length
属性的类数组。
bind
ES5实现:
Function.prototype.$bind = function (content) {
if (typeof this !== 'function') {
throw new TypeError(`${this} is not a function`)
}
var argsArr = arguments
content = content == null ? globalThis : Object(content)
content._symbolBind = this
return function () {
var args = []
for (var i = 1; i < argsArr.length; i++) {
args.push('argsArr[' + i + ']')
}
for (var i = 1; i < arguments.length; i++) {
args.push('arguments[' + i + ']')
}
var result = eval('content._symbolBind(' + args + ')')
delete content._symbolBind
return result
}
}
ES6实现:
Function.prototype.bindX = function (content, ...rest) {
if (typeof this !== 'function') {
throw new TypeError(`${this} is not a function`)
}
content = content == null ? globalThis : Object(content)
const symbol = Symbol('fn')
content[symbol] = this
return function (...rest1) {
const result = content[symbol](...rest1, ...rest2)
delete content[symbol]
return result
}
}
借助apply:
Function.prototype.bindY = function (content, ...args1) {
return (...args2) => this.apply(content, [...args1, ...args2])
}
借助call:
Function.prototype.bindZ = function (...args1) {
return (...args2) => this.call(...arguments, ...rest)
}