前端工作和面试中常常会遇到批改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)