this到底指向什么?
对于我们前端开发攻城狮来说,this是个经常会遇到的问题,也经常因为它而出现bug的问题,主要都是因为this在不同情况下指向也是不尽相同,今天我们来讨论一下this的指向都有哪些情况?如何去区分它?
this关键词是javaScript中最复杂的机制之一,一般有两个误区: 1.this指向函数自身; 2.this指向函数的作用域;
this是指向调用位置,调用位置就是函数在代码中被调用的位置(而不是声明的位置),this的绑定规则主要有4条,我们在使用时能判断它属于哪种规则即可;
1.默认绑定
首先介绍最常用的函数调用类型:独立函数调用,可以理解为这是一条在无法应用其他规则的默认规则,非严格模式下,this指向全局对象,即window
var a = 2;
function foo(){
var a = 1;
console.log(this.a)
}
foo(); // 2
很明显这里的this指向window对象,但严格模式下呢?
var a = 1;
function foo(){
"use strict"
console.log(this.a)
}
foo(); // TypeError:this is undefined
严格模式下函数中的this指向undefined
"use strict";
console.log(this === window); // false? // true?
this.a = 9804;
console.log(window.a); // undefined? // 9804?
// true
// 9804
但全局中的this指向window
2.隐式绑定
先看一段代码
function foo(){
console.log(this.a)
}
var a = 1;
var obj = {
a:2,
foo:foo
}
obj.foo(); // ?
// 2
当函数引用有上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象,可以记住一点:谁调用绑到谁身上。
再看一段代码:
function foo(){
console.log(this.a)
}
var obj2 = {
a:42,
foo:foo
}
var obj1 = {
a:2,
obj2:obj2
}
obj1.obj2.foo(); // ?
// 42
对象属性引用链中只有上一层或者最后一层才起作用。
"use strict";
var o = new Object();
o.a = 'obj.a';
o.f5 = function(){
return this.a;
}
console.log(o.f5());// ?
// obj.a
在严格模式下,对象的函数中的this指向调用函数的对象实例
3.显式绑定
apply,call,bind
function foo(){
console.log(this.a)
}
var a = 1;
var obj = {
a:2
}
foo.call(obj);// ?
// 2
通过foo.call()可以把foo的this绑定到obj上。
var obj1 = {
a: 1,
b: 2,
caculate: function(c,d){
return this.a + this.b + c + d;
}
};
var obj2 = {
a: 10,
b: 20,
};
console.log( obj1.caculate(3,4) ); // ?
console.log( obj1.caculate.call( obj2, 3, 4 ) ); // ?
console.log( obj1.caculate.apply( obj2, [30, 40] ) ); // ?
// 10
// 37
// 100
call 和 apply 的用法唯一的不同就是传参的方式不同,参见上面两种调用方式
(1)硬绑定 看下面代码:
var a = 1;
function foo(){
console.log(this.a)
}
var obj = {
a:2
}
var bar = function(){
foo.call(obj)
}
bar();// ?
setTimeout(bar,100);// ?
//硬绑定的bar不可能再修改它的this
bar.call(window); // ?
// 2
// 2
// 2
无论何时调用bar函数,都会手动obj上调用foo,这种就是一种显示的强制绑定,即硬绑定。硬绑定的典型场景就是创建一个包裹函数 由于硬绑定是一种常用的模式,所以ES5提供的内置的方法Function.prototype.bind,它的用法如下:
function foo(something){
console.log(this.a ,something);
return this.a + something
}
var obj = {
a:2
}
var bar = foo.bind(obj);
var b = bar(3);
console.log(b);
// 2 3
// 5
bind(..)会返回一个硬编码的新函数,它会把你指定的参数设置为this的上下文并调用原始函数,但是Ta是永久绑定,一旦绑定不会在改变:
4.new绑定
当用 new 运算符调用函数时,该函数总会返回一个对象,通常情况下,如果构造器不显式地返回任何数据,或者是返回一个非对象类型的数据,构造器里的 this 就指向返回的这个对象,
var MyClass = function(){
this.name = 'sven';
return 'anne'; // 返回 string 类型
};
var obj = new MyClass();
console.log(obj) // ?
console.log(obj.name); // ?
// {name: 'sven'}
// 输出: sven
var MyClass = function(){
this.name = 'sven';
return { // 显式地返回一个对象
name: 'anne'
}
};
var obj = new MyClass();
console.log( obj); // ?
// 输出: {name: 'anne'}
"use strict";
console.log("严格模式");
console.log("构造函数中的this");
function constru(){
this.a = 'constru-a';
this.f2 = function(){
console.log(this.b);
return this.a;
}
}
var o2 = new constru(); // ?
o2.b = 'o2-b';
console.log(o2.f2()); // ?
// o2-b
// constru-a
在严格模式下,构造函数中的this指向构造函数创建的对象实例
四种绑定规则的优先级
new绑定>显式绑定>隐式绑定>默认绑定
被忽略的this
如果你把null或者undefined作为this的绑定对象传入call、apply或者bind,这些值在调用时会被忽略,实际应用的是默认绑定规则:
function foo(){
console.log(this.a)
}
var a = 2;
foo.call(null); // ?
// 2
间接引用
另一个需要注意的是,你有可能无意的创建一个函数的“间接引用”,这种情况下,调用函数会应用默认规则。间接引用最容易在赋值时发生:
function foo() {
console.log(this.a);
}
var a = 2;
var o = { a: 3, foo: foo};
var p = { a: 4 };
o.foo();
(p.foo = o.foo)(); // 返回目标函数的引用
//3
//2
this词法
之前介绍的4种绑定规则可以包含所有正常的函数,但是ES6中介绍了一种无法使用这些规则的特殊函数类型:箭头函数。 箭头函数this的定义:箭头函数中的this是在定义函数的时候绑定,而不是在执行函数的时候绑定。
function foo(){
//返回一个箭头函数
return (a) => {
//this继承自foo()
console.log(this.a)
}
}
var obj1 = {
a:2
}
var obj2 = {
a:3
}
var bar = foo.call(obj1);
bar.call(obj2); // ?
// 2, 不是3
foo()内部创建的箭头函数会捕获调用时foo()的this,由于foo()的this绑定到obj1,bar(引用箭头函数)的this也会绑定到obj1,箭头函数的绑定无法被修改
小结
1.由new调用?绑定到新创建的对象。 2.由call或者apply(或者bind)调用?绑定到指定的对象。 3.由上下文对象调用?绑定到那个上下文对象。 4.默认:在严格模式下绑定到undefined,否则绑定到全局对象。 ES6中的箭头函数并不会用这4种绑定规则,而是根据词法作用域来决定this,具体来说,箭头函数会继承外层函数调用的this绑定。
以上就是本文的内容,有不对的地方欢迎小伙伴批评与指正,如果觉得还不错的话,欢迎留下你们大大的赞👍!!!