JS函数调用的this指向与apply,call,bind调用模式
2024-01-08 21:38:42
1、函数调用的四种模式与this指向
- 普通函数调用:this指向全局对象
- 对象方法调用:this指向该调用的对象
- 构造函数调用:this指向构造函数new的对象
- call,apply和bind间接调用:显式绑定this,传入的第一个参数绑定的对象
2、this指向例子(?????)
var name = "lucy";
var obj = {
name: "martin",
say: function () {
console.log(this.name);
}
};
//1
obj.say(); // martin,this 指向 obj 对象
//2
setTimeout(obj.say,0); // lucy,this 指向 window 对象
//3
setTimeout(obj.say.bind(obj),0); //martin,this指向obj对象
- obj.say()为对象方法调用,this指向obj,所以this.name为Martin
- say放在setTimeout方法当中,在定时器中的是作为回调函数执行的,回到执行栈执行时候是在全局执行上下文的环境中执行,所以this.name为Lucy
- 为了改变情况2的指向,这时候需要bind函数obj.say,bind(obj)显式绑定this到obj上面
2、apply,call,bind的区别
- apply接收两个参数,第一个参数是this指向,第二个参数是数组。该方法的this指向只临时改变一次,如下:
function fn(...args){
console.log(this,args);
}
let obj = {
myname:"张三"
}
fn.apply(obj,[1,2]); // this会变成传入的obj,传入的参数必须是一个数组;
fn(1,2) // this指向window
- call接收多个参数,第一个参数是this指向,后面的是参数列表。也是this指向只临时改变一次
function fn(...args){
console.log(this,args);
}
let obj = {
myname:"张三"
}
fn.call(obj,1,2); // this会变成传入的obj,传入的参数必须是一个数组;
fn(1,2) // this指向window
- bind接收多个参数,第一个参数是this指向,后面的是参数列表(但是这个参数列表可以分为多次传入)。改变this指向后不会立即执行,而是返回一个永久改变this指向的函数。
function fn(...args){
console.log(this,args);
}
let obj = {
myname:"张三"
}
const bindFn = fn.bind(obj); // this 也会变成传入的obj ,bind不是立即执行需要执行一次
bindFn(1,2) // this指向obj
fn(1,2) // this指向window
?总结:三者的区别
- 三者都是可以改变函数的this指向
- 三者第一个参数都是this指向的对象,如果没有这个参数或者参数为undefined或null,则默认指向全局Window
- 三者都是可以传递参数,apply是数组,call、bind是列表。其中apply和call都是一次性传入参数,而bind可以分为多次传入
- bind是返回绑定this之后的函数,而apply、call是立即执行。所以bind的this指向是永久改变,apply,call则是临时绑定。
这里可以结合函数柯里化-CSDN博客?这篇文章去理解他们的应用。
4、实现
- apply函数实现
- 判断调用对象是否为函数,即便是定义在函数的原型上面,但是可能出现使用call等方法调用的情况。
- 判断传入上下文对象是否存在,如果不存在,设置为window
- 将函数作为上下文对象的一个属性
- 判断参数值是否传入
- 使用上下文对象来调用这个方法,并保存返回结果
- 删除刚才新的属性
- 返回结果
Function.prototype.myApply = function(context){
//判断调用对象是否为函数
if(typeof this!="function"){
throw new TypeError("Error");
}
let result =null;
context = context||window;
context.fn = this;
if(arguments[1]){
result = contetx.fn(...arguments[1]);
}else{
result = context.fn()
}
delete context.fn;
return result;
}
- call函数实现
- 判断调用对象是否为函数,即便是定义在函数的原型上面,但是可能出现使用call等方法调用的情况。
- 判断传入上下文对象是否存在,如果不存在,设置为window
- 处理传入的参数,截取第一个参数后的所有参数
- 将函数作为上下文对象的一个属性
- 使用上下文对象来调用这个方法,并保存返回结果
- 删除刚才新的属性
- 返回结果
Function.prototype.myCall = function(context){
//判断调用对象是否为函数
if(typeof this!="function"){
throw new TypeError("Error");
}
let args = [...arguments].slice(1),result =null;
context = context||window;
context.fn = this;
result = contetx.fn(...args);
delete context.fn;
return result;
}
- bind函数实现
- 判断调用对象是否为函数,即便是定义在函数的原型上面,但是可能出现使用call等方法调用的情况。
- 保存当前函数的引用,获取其余传入参数值
- 创建一个函数返回
- 函数内部使用apply来绑定函数调用,需要判断函数作为构造函数的情况,这个时候需要传入当前函数的this给apply调用,其余情况都传入指定的上下文对象
Function.prototype.myBind = function(context){
//判断调用对象是否为函数
if(typeof this!="function"){
throw new TypeError("Error");
}
let args = [...arguments].slice(1),fn = this;
return function Fn(){
return fn.apply(
this instanceof Fn ? this : context,
args.concat(...arguments)
)
};
}
修改this指向、动态传参
// 方式一:只在bind中传递函数参数
fn.bind(obj,1,2)()
// 方式二:在bind中传递函数参数,也在返回函数中传递参数
fn.bind(obj,1)(2)
文章来源:https://blog.csdn.net/qq_42794545/article/details/135461474
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!