Appearance
call、apply、bind
call、apply 详解
var a = {
user:"gzh",
fn:function(){
console.log(this.user);
}
}
var b = a.fn;
b(); //undefined
结果是 undefined ?? 因为 函数中 this 永远指向其执行环境的上下文。所以 b = a.fn = function(){console.log(this.user)}; b() 此时的 this 就指向全局环境
那我们如何修改成我们想要的指向 a 对象
// 第一种 利用bind,因为bind是不会立即执行函数的,所有我们可以在申明的时候修改this的指向
var b = a.fn.bind(a);
// call、apply 会立即执行函数 所以我们不能再申明的时候修改this,只能在调用的时候修改
b.call(a);
b.apply();
apply、call 的区别是什么??
上面说都可以利用后续参数进行传参。 apply 的传参 只能是一个且要为数组
b.apply(a,[1,2,3])
call 的后续参数就可以不一定为数组了,只能一个一个作为入参而已
a.call(a,1,2,3) function(arg1,arg2,arg3){} 1,2,3
bind 详解
var a = {
user:"gzh",
fn:function(){
console.log(this.user);
}
}
var b = a.fn;
b.bind(a) //什么都没有打印
我们发现没有立即打印 gzh 再次 b() //undefined
可见 bind 修改 this 的指向后 不会执行函数,而是生成一个新的函数
var c = b.bind(a);
c() //gzh
作用
1、异步函数绑定 this
js
var _this = this
var ev = element.addEventListener("click", function () {
console.log(this) //此时this指向 元素element而不是当前的作用域
//所以我们习惯 _this
})
var ev = element.addEventListener(
"click",
function () {
console.log(this)
}.bind(this)
) //在react中使用甚多
2、Math 函数的执行
js
console.log(Math.max(1, 2, 3, 2))
// Matn.max.call(Math,1,2,3,2);
3、修改 this 指向
我们如何修改函数中 this 的指向 这个我们就想到了 obj.fn()
源码
当我们自己去实现 call、apply、bind 的时候需要知道其核心的功能是那些,即上述所述的
- 修改 this 的指向
- 可以传入参数
- 执行的时机不同
下面我们先按照上面的核心功能实现
js
Function.prototype.mCall = function (context) {
// 如果没有传入第一个参数 那么this就指向window对象
context = context || window
context.fn = this
context.fn()
}
这时候我们就想到几个问题。
- 后面的参数是作为函数的入参。
- 如果存在 fn 这个属性怎么办
- context 对象上多了 fn 属性
所以我们修改一下
js
Function.prototype.mCall = function (context) {
// 如果没有传入第一个参数 那么this就指向window对象
context = context || window
// 1. 后面的参数是作为函数的入参
const argus = [...arguments].slice(1)
// 2. 如果存在fn这个属性怎么办 这个时候想到 Symbol 基本类型
const fnName = Symbol("fn")
context[fnName] = this
context[fnName](argus)
// 3. context对象上多了fn属性
delete context[fnName]
}
apply 的实现
apply 与 call 的不同就是后面的入参是一个数组而不是多个参数
js
Function.prototype.mApply = function (context) {
// 如果没有传入第一个参数 那么this就指向window对象
context = context || window
// 1. 后面的参数是作为函数的入参
const argus = arguments[1]
argus = Array.isArray(argus) ? argus : undefined
// 2. 如果存在fn这个属性怎么办 这个时候想到 Symbol 基本类型
const fnName = Symbol("fn")
context[fnName] = this
context[fnName](argus)
// 3. context对象上多了fn属性
delete context[fnName]
}
bind 的实现
call 这是返回的是一个修改函数的 this 的指向的函数,但是现在不会立即执行 这时候我们就需要想到闭包
支持柯里化传参
js
Function.prototype.bind = function (context) {
// 如果没有传入第一个参数 那么this就指向window对象
context = context || window
// 2. 如果存在fn这个属性怎么办 这个时候想到 Symbol 基本类型
let argus = [...arguments].slice(1)
const fnName = Symbol("fn")
context[fnName] = this
return function () {
let newArgus = [...arguments]
context[fnName](...argus.concat(newArgus))
// 3. context对象上多了fn属性
delete context[fnName]
}
}
call、apply、bind 的区别于作用
call、apply、bind 都可以改变 this 的指向(指向第一个参数)。
call、apply、bind 都可以利用后续参数进行传参(bind 很少用)。
bind 是返回对应函数,以后调用;call、apply 是立即执行,返回值是函数的返回值。