改变函数内部this指针的指向函数(bind,apply,call)
原计划写闭包的,今天事有点多,写不完,明天找时间总结总结。
今晚说一下改变this指向的指向函数。
一.改变函数内部this指针的指向函数
- 通过apply和call改变函数的this指向,他们两个函数的第一个参数都是一样的表示要改变指向的那个对象;第二个是传入的参数,apply是数组,而call则是arg1,arg2…这种形式。
- 通过bind改变this作用域会返回一个新的函数(我理解的是bind不改变原函数,再var一个新的函数拿来用。),这个函数不会马上执行。call 是把第二个及以后的参数作为 func 方法的实参传进去,而 func1 方法的实参实则是在 bind 中参数的基础上再往后排。
function func(a, b, c) {
console.log(a, b, c);
}
var func1 = func.bind(null,'linxin');//重新命名
func('A', 'B', 'C'); // A B C
//参数从bind的第二个参数插入到新函数fun1的第一个参数之前
func1('A', 'B', 'C'); // linxin A B
func1('B', 'C'); // linxin B C
func.call(null, 'linxin'); // linxin undefined undefined
二.箭头函数中this指向举例
var a=11;
function test2(){
this.a=22;
let b=()=>{console.log(this.a)}
b();
}
var x=new test2();
//输出22定义时绑定
三.如何实现一个 bind,apply,call函数
对于实现以下几个函数,可以从几个方面思考
- 不传入第一个参数,那么默认为 window
- 改变了 this 指向,让新的对象可以执行该函数。那么思路是否可以变成给新的对象添加一个函数,然后在执行完以后删除?
1.bind:
- fun.bind(thisArg[, arg1[, arg2[, …]]])
- 他是直接改变这个函数的this指向并且返回一个新的函数,之后再次调用这个函数的时候this都是指向bind绑定的第一个参数。bind传餐方式跟call方法一致。
- thisArg 当绑定函数被调用时,该参数会作为原函数运行时的 this 指向。当使用new 操作符调用绑定函数时,该参数无效。
- arg1, arg2, … 当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。
Function.prototype.myBind = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
var _this = this
var args = [...arguments].slice(1)
// 返回一个函数
return function F() {
// 因为返回了一个函数,我们可以 new F(),所以需要判断
if (this instanceof F) {
return new _this(...args, ...arguments)
}
return _this.apply(context, args.concat(...arguments))
}
2.call:
- fun.call(thisArg, arg1, arg2, …)
- call跟apply的用法几乎一样,唯一的不同就是传递的参数不同,call只能一个参数一个参数的传入。
- thisArg: 在fun函数运行时指定的this值。需要注意的是,指定的this值并不一定是该函数执行时真正的this值,如果这个函数处于非严格模式下,则指定为null和undefined的this值会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的this会指向该原始值的自动包装对象。
- arg1, arg2, … 指定的参数列表
Function.prototype.myCall = function (context) {
var context = context || window
// 给 context 添加一个属性
// getValue.call(a, 'yck', '24') => a.fn = getValue
context.fn = this
// 将 context 后面的参数取出来
var args = [...arguments].slice(1)
// getValue.call(a, 'yck', '24') => a.fn('yck', '24')
var result = context.fn(...args)
// 删除 fn
delete context.fn
return result
}
3.apply
- apply则只支持传入一个数组,哪怕是一个参数也要是数组形式。最终调用函数时候这个数组会拆成一个个参数分别传入。
- thisArg 在 fun 函数运行时指定的 this 值。需要注意的是,指定的 this 值并不一定是该函数执行时真正的 this 值,如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的自动包装对象。
- argsArray 一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 fun 函数。如果该参数的值为null 或 undefined,则表示不需要传入任何参数。从ECMAScript 5 开始可以使用类数组对象。
Function.prototype.myApply = function (context) {
var context = context || window
context.fn = this
var result
// 需要判断是否存储第二个参数
// 如果存在,就将第二个参数展开
if (arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
delete context.fn
return result
}
四.总结
- 当我们使用一个函数需要改变this指向的时候才会用到call
apply
bind - 如果你要传递的参数不多,则可以使用fn.call(thisObj, arg1, arg2 …)
- 如果你要传递的参数很多,则可以用数组将参数整理好调用fn.apply(thisObj, [arg1, arg2 …])
- 如果你想生成一个新的函数长期绑定某个函数给某个对象使用,则可以使用const newFn = fn.bind(thisObj); newFn(arg1, arg2…)
- call和apply第一个参数为null/undefined,函数this指向全局对象,在浏览器中是window,在node中是global
this指针问题没有说,因为我也有些模糊,(后面更了的话建议先看this部分)
最近找时间学明白了,总结一下。
今天到这里,再见!