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();
|
2.作为普通函数调用时
当函数不作为对象的属性被调用,而是以普通函数的方式,this总是指向全局对象(在浏览器中,通常是Window对象)
1 2 3 4 5 6 7 8
| window.name = 'test';
var getName = function(){ console.log(this.name); };
getName();
|
这里有一个迷惑性的代码
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;
|
但是,如果显式的返回了一个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);
|
只要构造器不显示的返回任何数据,或者返回非对象类型的数据,就不会造成上述问题。
这里就涉及到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, };
console.log(obj1.getName()); console.log(obj1.getName.call(obj2, 2)); console.log(obj1.getName.apply(obj2, [2]));
|
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();
|
以上五种是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岁。我的工作是: 设计师,我的爱好是: 画画。
|