共计 3210 个字符,预计需要花费 9 分钟才能阅读完成。
前端工作和面试中常常会遇到批改 this 指向的问题, 这就避不开对
call
、apply
、bind
的探讨
用法
1. call
语法: function.call(thisArg, arg1, arg2, ...)
其中: thisArg
可选值; 指定为 null
或 undefined
时会主动替换为全局对象 (严格模式下将会是 undefined
); 指定为数字、字符串或布尔类型, 则会转换成他们的包装对象. 是 function
函数运行时指向的 this
值. arg1, arg2, ...
指定的参数, 能够有多个
返回值: 调用有指定 this
值和参数的函数的后果。
应用场景:
// 判断数据类型
const a = 1
const b = '1'
const c = true
const d = () => {}
toString.call(a) // 输入 '[object Number]'
toString.call(b) // 输入 '[object String]'
toString.call(c) // 输入 '[object Boolean]'
toString.call(d) // 输入 '[object Function]'
// 将伪数组转换为数组
function transformArgement() {const arr = Array.prototype.slice.call(arguments)
return arr
}
console.log(transformArgement(1, 2, 3, 4, 5)) // 输入 [1, 2, 3, 4, 5]
// 通过调用父构造函数的 call 办法来实现继承
function Calc (args) {const arr = Array.prototype.slice.call(args)
// 最大值
this.max = Math.max.apply(null, arr)
// 最小值
this.min = Math.min.apply(null, arr)
// 求和
this.sum = arr.reduce((pre, cur) => pre + cur)
}
function Ages() {Calc.call(this, arguments)
this.property = 'ages'
}
const team01 = new Ages(10, 20, 12, 80, 4, 100, 19)
console.log(team01.min) // 输入 4
console.log(team01.max) // 输入 100
console.log(team01.sum) // 输入 245
2. apply
语法: function.apply(thisArg, argsArray)
其中: thisArg
用法同上. argsArray
指定的参数, 能够为一个数组或者类数组对象,其中的数组元素将作为独自的参数传给 function
函数
返回值: 调用有指定 this
值和参数的函数的后果。
应用场景:
// 数组增加数组元素
const arr1 = [1, 2, 3]
const arr2 = ['1', '2', '3']
Array.prototype.push.apply(arr1, arr2)
console.log(arr1) // 输入 [1, 2, 3, '1', '2', '3']
// 数组转化
const slice = Array.prototype.slice;
function args() {return slice.apply(arguments)
}
args(1, 2, 3, 4) // 返回 [1, 2, 3, 4]
// 内置函数的扩大
const arrNum = [1, 4, 6, 3, 2]
console.log(Math.min.apply(null, arrNum))
console.log(Math.max.apply(null, arrNum))
3. bind
语法: function.bind(thisArg, arg1, arg2, ...)
其中: thisArg
调用绑定函数时作为 this
参数传递给指标函数的值。如果应用 new
运算符结构绑定函数,则疏忽该值。当应用 bind
在 setTimeout
中创立一个函数(作为回调提供)时,作为 thisArg
传递的任何原始值都将转换为 object
。如果 bind
函数的参数列表为空,或者 thisArg
是 null
或 undefined
,执行作用域的 this
将被视为新函数的 thisArg
. arg1, arg2, ...
指定的参数, 能够有多个
返回值: 返回一个原函数的拷贝,并领有指定的 this
值和初始参数。
应用场景:
// 创立绑定函数
// 老手常常犯的一个谬误是将一个办法从对象中拿进去,而后再调用,冀望办法中的 this 是原来的对象
this.a = 100
const obj = {
a: 1,
fn: function() {return this.a}
}
obj.fn() // 输入 1
const f = obj.fn.bind(obj)
f() // 输入 1
// React 组件中为函数绑定组件实例
class Foo extends Component {constructor(props) {super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {console.log('Click happened');
}
render() {return <button onClick={this.handleClick}>Click Me</button>;
}
}
// 偏函数实现
function add(arg1, arg2) {return arg1 + arg2}
const addBase10 = add.bind(null, 10)
console.log(addBase10(100)) // 输入 110
console.log(addBase10(200)) // 输入 210
// 类数组转化
const unboundSlice = Array.prototype.slice
const slice = Function.prototype.apply.bind(unboundSlice)
function args() {return slice(arguments)
}
args(1, 2, 3, 4) // 返回 [1, 2, 3, 4]
区别
从下面的用法比照不难看出, call
与 apply
次要是入参不同, 他们都是返回调用有指定 this
值和参数的函数的后果(即立刻调用)。而 bind
的入参加 call
雷同, 然而他的返回值与 call
和 apply
都不同, 是一个原函数的拷贝,并领有指定的 this
值和初始参数。
延长
应用 js 实现 bind
办法
- 首先须要把办法增加到函数原型
-
办法能够承受 n 个参数, 其中第一个参数为调用办法的函数的
this
指向, 其余参数将作为调用函数的入参Function.prototype.customBind = function (targetThis) { // 类型校验 if (toString.call(this) !=== '[object Function]') {throw new Error(`${this} mast be a Function`) } // 保留 this const _this = this // 保留参数(剔除第一个参数 - 须要 this 指向的参数) const args = Array.prototype.slice.call(arguments, 1) const fn = function () { // 保留调用时传入的参数 const fnArgs = Array.prototype.slice.call(arguments) return _this.apply(targetThis, args.concat(fnArgs)) } return fn } function fn() {console.log(this) // 输入 {a: 10} console.log(this.a, arguments) // Arguments [1, 2, 3, 4, 5, 6] } const o = {a: 10} fn.customBind(o, 1, 2, 3)(4, 5, 6)