改变this指向-call、apply、bind的内部实现
手写call
// call 的内部实现
Function.prototype.myCall = function(context) {
// 判断调用者是否为函数
if(typeof this !== 'function') {
throw new TypeError('Error')
}
// 不传参默认为 window
context = context || window
// 新增 fn 属性,将值设置为需要调用的函数
context.fn = this
// 将arguments 转化为数组将call 的传参提取出来
const args = Array.from(arguments).slice(1)
// 传参调用函数
const result = context.fn(...args)
// 删除函数
delete context.fn
// 返回执行结果
return result;
}
// 普通函数
function print(age) {
console.log(this.name+ " " + age);
}
// 自定义对象
var obj = {
name: 'JavaScript'
}
// 调用 myCall 方法
print.myCall(obj, 1,2,3) // JavaScript 1
手写apply
// apply 内部实现
Function.prototype.myApply = function(context) {
// 判断调用者是否为函数
if(typeof this !== 'function') {
throw new TypeError('Error')
}
// 不传参默认为 window
context = context || window
// 新增 fn 属性,将值设置为需要调用的函数
context.fn = this
// 返回执行结果
let result;
// 判断是否有参数传入
if(arguments[1]) {
result = context.fn(...arguments[1])
}else {
result = context.fn()
}
// 删除函数
delete context.fn
// 返回执行结果
return result;
}
// 普通函数
function print(age,age2,age3) {
console.log(this.name+" "+ age + " "+ age2+" "+age3);
}
// 自定义对象
var obj = {
name:'js' }
// 调用函数的 call 方法
print.myApply(obj,[1,2,3]) // js 1 2 3
手写bind
Function.prototype.myBind = function (context) {
// 判断调用者是否为函数
if(typeof this !== 'function') {
throw new TypeError('Error')
}
// 截取传递的参数
const args = Array.from(arguments).slice(1)
// _this 指向调用的函数
const _this = this;
// 返回一个函数
return function F() {
// 因为返回了一个函数,我们可以new F(), 所以需要判断
// 对于new 的情况来说,不会被任何方式改变 this
if(this instanceof F) {
return new _this(...args, ...arguments)
} else {
return _this.apply(context, args.concat(...arguments))
}
}
}
// 普通函数
function print3(age,age2,age3) {
// new 的方式调用 bind 参数输出换做 [...arguments]
console.log(this.name+" "+ age + " "+ age2+" "+age3);
}
// 自定义对象
var obj = {
name: 'myBind'
}
// 调用函数的 call 方法
let F = print3.myBind(obj,1,2,3);
F() // myBind 1 2 3
// 返回对象
let obj1 = new F();
console.log(obj1); // undefined 1 2 3
三者区别
参数
-
第一个参数:都是this要指向的对象
-
第二个参数:
call->参数可用逗号分隔, 也可传数组
apply->必须放在数组里面传进去
bind->参数可用逗号分隔, 也可传数组
返回值
bind返回值为一个新的函数,其余返回结果都一致