this指向,call、apply、bind详解

this指向,call、apply、bind详解

this 总是(非严格模式下)指向一个对象,而具体指向哪个对象是在运行时基于函数的执行环境动态绑定的,而非函数被声明时的环境;

具体在实际应用中,this指向大概可以分为以下几种

1.作为对象的方法调用

当函数作为对象的方法被调用时,this指向该对象

1
2
3
4
5
6
7
8
9
var obj = {
a: 'test',
getName: function(){
console.log(this === obj);
console.log(this.a);
}
};

obj.getName(); // true 'test'

2.作为普通函数调用时

当函数不作为对象的属性被调用,而是以普通函数的方式,this总是指向全局对象(在浏览器中,通常是Window对象)

1
2
3
4
5
6
7
8
window.name = 'test';

var getName = function(){
console.log(this.name);
};

getName(); // test

这里有一个迷惑性的代码

1
2
3
4
5
6
7
8
9
window.name = '老王'
var obj = {
name: 'yuguang',
getName: function(){
console.log(this.name);
}
};
var getNew = obj.getName;
getNew(); // 老王

这里的getNew相当于一个函数调用了,而不是对象中的方法

3.构造器调用

除了一些内置函数,大部分Js中的函数都可以成为构造器,它们与普通函数没什么不同

构造器普通函数的区别在于被调用的方式
当new运算符调用函数时,总是返回一个对象,this通常也指向这个对象

1
2
3
4
5
var MyClass = function(){
this.name = 'tian';
}
var obj = new MyClass();
obj.name; // tina

但是,如果显式的返回了一个object对象,那么此次运算结果最终会返回这个对象。

1
2
3
4
5
6
7
8
var MyClass = function () {
this.name = 1;
return {
name: 2
}
}
var myClass = new MyClass();
console.log('myClass:', myClass); // { name: 2}

只要构造器不显示的返回任何数据,或者返回非对象类型的数据,就不会造成上述问题。
这里就涉及到new的实现过程了,之前的博客写过了,这里不再赘述,请参考

https://tian-1-2.github.io/typblog.github.io/2022/05/23/2022523-new%E6%89%8B%E5%86%99%E5%AE%9E%E7%8E%B0%E8%BF%87%E7%A8%8B/

4、call或apply调用

跟普通的函数调用相比,用call和apply可以动态的改变函数的this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var obj1 = {
name: 1,
getName: function (num = '') {
return this.name + num;
}
};

var obj2 = {
name: 2,
};
// 可以理解成在 obj2的作用域下调用了 obj1.getName()函数
console.log(obj1.getName()); // 1
console.log(obj1.getName.call(obj2, 2)); // 2 + 2 = 4
console.log(obj1.getName.apply(obj2, [2])); // 2 + 2 = 4

5**.箭头函数**

箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this。

因此,在下面的代码中,传递给getVal函数内的this并不是调用者自身,而是外部的this

1
2
3
4
5
6
7
8
this.val = 2;
var obj = {
val: 1,
getVal: () => {
console.log(this.val);
}
}
obj.getVal(); // 2

以上五种是this指向的基本情况

下面在详细介绍一下call和apply

call和apply

1.call和apply区别

先来看区别,是因为它们几乎没有区别,下文代码实例call和apply都可以轻易的切换。

当它们被设计出来时要做到的事情一摸一样,唯一的区别就在于传参的格式不一样

apply接受两个参数
第一个参数指定了函数体内this对象的指向
第二个参数为一个带下标的参数集合(可以是数组或者类数组)

1
func.apply(obj,args)//obj要改变指向的对象,args为数组或者类数组

call接受的参数不固定
第一个参数指定了函数体内this对象的指向
第二个参数及以后为函数调用的参数

1
func.call(obj,args1,args2...)//obj要改变指向的对象,args1,args2...多个参数依次传入

因为在所有(非箭头)函数中都可以通过arguments对象在函数中引用函数的参数。此对象包含传递给函数的每个参数,它本身就是一个类数组,我们apply在实际使用中更常见一些。

call是包装在apply上面的语法糖,如果我们明确的知道参数数量,并且希望展示它们,可以使用call。

当使用call或者apply的时候,如果我们传入的第一个参数为null,函数体内的this会默认指向宿主对象,在浏览器中则是window。

bind

bind的作用是只修改this指向,但不会立即执行fn;

会返回一个修改了this指向后的fn。

需要调用才会执行:bind(thisArg, arg1, arg2, arg3, ...)()

bind的传参和call相同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let obj = {
name: "xiaoming",
age: 24,
sayHello: function (job, hobby) {
console.log(`我叫${this.name},今年${this.age}岁。我的工作是: ${job},我的爱好是: ${hobby}。`);
}
}
// obj.sayHello('程序员', '看美女'); // 我叫xiaoming,今年24岁。我的工作是: 程序员,我的爱好是: 看美女。

let obj1 = {
name: "lihua",
age: 30
}
obj.sayHello.bind(obj1, '设计师', '画画'); // 无输出结果
obj.sayHello.bind(obj1, '设计师', '画画')(); // 我叫lihua,今年30岁。我的工作是: 设计师,我的爱好是: 画画。

this指向,call、apply、bind详解
https://tian-1-2.github.io/typblog/2022/10/02/this指向,call、apply、bind详解/
作者
田云鹏
发布于
2022年10月2日
许可协议