call, apply, bind 讲解 (含自己实现)
call, apply, bind 讲解 (含自己实现)this指向基本知识例 1var name = '伞兵一号', age = 18;var obj = {name: '伞兵二号',objAge: this.age,myFun: function() {console.log( this.name + '年龄' + this.age)}}obj.objAge;// 19obj.myFun()/
call, apply, bind 讲解 (含自己实现)
this指向基本知识
例 1
var name = '伞兵一号', age = 18;
var obj = {
name: '伞兵二号',
objAge: this.age,
myFun: function() {
console.log( this.name + '年龄' + this.age)
}
}
obj.objAge; // 19
obj.myFun() // 伞兵二号年龄 undefined (obj中没有age这个属性)
例 2
var name = '伞兵一号';
function shows() {
console.log(this.name)
}
shows() // 伞兵一号
比较一下这两者 this 的差别,第一个打印里面的 this 指向 obj,第二个全局声明的 shows() 函数 this 是 window ;
三者的作用
1,call()、apply()、bind() 都是用来重定义 this 这个对象的!
如:
var name = '小王', age = 12;
var obj = {
name: '小张',
age: 13,
myFun: function() {
console.log(this.name + '年龄' + this.age);
}
}
var test = {
name: '小红',
age: 14
}
obj.myFun.call(test); // 小红年龄14
obj.myFun.apply(test); // 小红年龄14
obj.myFun.bind(test)(); // 小红年龄14
以上出了 bind 方法后面多了个 () 外 ,结果返回都一致!
由此得出结论,bind 返回的是一个新的函数,你必须调用它才会被执行。
2,对比call 、bind 、 apply 传参情况下
const name = '小王'; const
age = 12;
const obj = {
name: '小张',
age: 13,
myFun(sex, high) {
console.log(`${this.name} 年龄 ${this.age} 性别 ${sex} 身高 ${high}`);
},
};
const test = {
name: '小红',
age: 14,
};
obj.myFun.call(db,'成都','上海'); // 小红 年龄 14 性别 男 身高 188
obj.myFun.apply(db,['成都','上海']); // 小红 年龄 14 性别 男 身高 188
obj.myFun.bind(db,'成都','上海')(); // 小红 年龄 14 性别 男 身高 188
obj.myFun.bind(db,['成都','上海'])(); // 小红 年龄 14 性别 男 身高 188 undefined
三者的区别
从上面四个结果不难看出:
call 、bind 、 apply 这三个函数的第一个参数都是 this 的指向对象,第二个参数差别就来了:
call 的参数是直接放进去的,第二第三第 n 个参数全都用逗号分隔,直接放到后面 obj.myFun.call(db,‘男’, … ,‘string’ )。
apply 的所有参数都必须放在一个数组里面传进去 obj.myFun.apply(db,[‘男’, …, ‘string’ ])。
bind 除了返回是函数以外,它 的参数和 call 一样。
当然,三者的参数不限定是 string 类型,允许是各种类型,包括函数 、 object 等等!
手写call
Function.prototype.myCall = function(context) {
if (typeof this !== "function") {
throw new Error("Function.prototype.myCall - what is trying to be bound is not callable");
}
// context代表传进来的第一个参数,也就是我们要把函数的this绑定到context上
let that = context || window;
// 获取剩余参数
let args = Array.prototype.slice.call(arguments, 1)
that.func = this; // 这个this就是调用call的这个对象,也就是我们需要改变this指向的函数f,因为是f调用了myCall,根据this规则,this指向调用者也就是myCall
that.func(...args); // 现在this就绑定到context上了,我们传入参数执行就好
delete that.func; // 回收func,不占用内存
}
// 测试
function getUser(age){
console.log(this.name);
console.log(age)
}
let user = {
name: 'Jason'
}
getUser.myCall(user,24) // Jason 24
补充:argument类数组转换为数组的方法
//方法一
let arg = Array.prototype.slice.call(argment,1)
//方法二
let arg = Array.prototype.concat.apply([],arguments)
//方法三
let arg = Array.from(arguments)
// 方法四
let arg = [...arguments]
手写aplly
实现和call
差不多,只不过传入的只有两个参数,一个是需要绑定this
的对象,一个存放参数的数组
Function.prototype.myApply = function(context, arg) {
if (typeof this !== "function") {
throw new Error("Function.prototype.myApply - what is trying to be bound is not callable");
}
let that = context || window;
if (!Array.isArray(arg)) {
console.log("参数必须是一个数组");
return;
}
that.func = this;
that.func(...arg);
delete that.fun;
}
// 测试
function getUser(age){
console.log(this.name);
console.log(age)
}
let user = {
name: 'Jason'
}
getUser.myApply(user,[24]) // Jason 24
手写bind
通过上述讲解可以看出 bind
有如下特性:
- 1、指定
this
- 2、传入参数
- 3、返回一个函数
- 4、柯里化
Function.prototype.myBind = function (context) {
// 调用 bind 的不是函数,需要抛出异常
if (typeof this !== "function") {
throw new Error("Function.prototype.myBind - what is trying to be bound is not callable");
}
const that = context || window;
// 保留需要改变this的函数
const selfThis = this;
// 实现第2点,因为第1个参数是指定的this,所以只截取第1个之后的参数
let args = Array.prototype.slice.call(arguments, 1);
// 实现第3点,返回一个函数
return function() {
// 实现第4点,这时的arguments是指bind返回的函数传入的参数
// 即 return function 的参数
args = [...args, ...arguments];
// 实现第1点
that.func = selfThis;
that.func(...args);
delete that.func;
}
}
更多推荐
所有评论(0)