“兄弟我a塔了。。。“你懊悔的说到。
就在刚才,你和你的打野准备越塔击杀盖伦,只剩最后一下平a的时候,你却把平a给了防御塔,最后导致了”上野联动,一死一送“的情况。
随后你的打野好兄弟想要与你恩断义绝,”xx上单,这都会a到塔?不玩了15点了。“
此时你不敢多说,毕竟是自己的低级失误才导致了被0换2的局面。
就在你懊悔的时候,中单亚索说到
”上单可以按一下~号,这是锁定英雄的快捷键,以后越塔可以先这样按,就不会再a到防御塔了“
你震惊了,还有这种操作,于是你默默的记下了这个细节
打野刷完了野区之后发现只有上路有机会,还是决定再相信你一次,这次依然是越塔,你没有再犯上次的错误,稳稳地拿下了人头,这把也走向了胜利。
都是越塔操作,为什么亚索不会失误而你就会呢? 这就有点类似我们的this指向和this绑定问题了,这篇文章我们就来讲讲面试高频考点,this指向问题。
注意:部分引用《你不知道的JavaScript》。
什么是this?
就像《你不知道的JavaScript》中所说:
this 关键字是JavaScript 中最复杂的机制之一。它是一个很特别的关键字,被自动定义在 所有函数的作用域中。但是即使是非常有经验的JavaScript开发者也很难说清它到底指向什么,在缺乏清晰认识的情况下,this对你来说完全就是一种魔法。
由此可见,this是一个十分抽象的概念。
当我们对内存(栈内存,堆内存) 看的透, 就可以理解this。
不同指向
全局
在全局node环境中,this指向全局对象global,在浏览器环境中,this则指向window
默认绑定
什么是默认绑定?
当一个函数不是作为对象的方法、构造函数或使用 call
/apply
调用时,它被称为“独立函数调用”或“直接调用”。在这种情况下
- 非严格模式:
this
绑定到全局对象。在浏览器环境中,this
指向window
对象;在 Node.js 环境中,指向global
对象。
// 'use strict'
function foo() {
console.log(this);
}
foo(); // 非严格模式下输出global, 严格模式下输出: undefined
- 严格模式 (
'use strict';
):this
绑定为undefined
。
隐式绑定
而在函数作为对象的一个属性被调用时,this
会隐式绑定到该上下文对象。这也叫隐式绑定
const obj = {
method: function() {
console.log(this);
}
};
obj.method();
还有个叫做隐式丢失的绑定问题,隐式丢失(也称为 this
上下文丢失)是指当一个方法被从对象中取出并作为普通函数调用时,原本隐式绑定到该对象的 this
值会丢失。这种情况下,this
的值会根据默认绑定规则重新确定,通常会导致意外的行为。
简单来说,就是给函数起一个小名,思考下面的代码:
const obj = {
value: 42,
printValue: function() {
console.log(this.value);
}
};
const print = obj.printValue;
print(); // 如果不在严格模式下,this 指向全局对象,输出 undefined;在严格模式下,this 依然是 undefined
即使print引用的是函数本身,但此时的 print()其实是一个不带任何修饰的函数调用,因此应用了默认绑定。
显示绑定
简单来说,显示绑定就是利用别的方法绑定函数,例如 call
、apply
或 bind
方法来显式地指定 this
的值。
function foo() {
console.log( this.a );
}
var obj = {
a:2
};
foo.call( obj ); // 2
通过foo.call(..),我们可以在调用foo时强制把它的this绑定到obj上。
但是这依然无法解决我们的丢失绑定问题。又出现一个新的方式,称为硬绑定,可以解决绑定丢失问题:
function foo() {
console.log( this.a );
}
var obj = {
a:2
};
var bar = function() {
foo.call( obj );
};
bar(); // 2
setTimeout( bar, 100 ); // 2
// 硬绑定的bar不可能再修改它的this
bar.call( window ); // 2
我们来看看这个变种到底是怎样工作的。我们创建了函数bar(),并在它的内部手动调用 了foo.call(obj),因此强制把foo的this绑定到了obj。无论之后如何调用函数bar,它 总会手动在obj上调用foo。这种绑定是一种显式的强制绑定,因此我们称之为硬绑定。
new 绑定
我们都知道,new是用于构造函数的方法,但在《你不知道的JavaScript》却这样说道:
JavaScript 也有一个 new 操作符,使用方法看起来也和那些面向类的语言一样,但JavaScript中new的机制实际上和面向类的语言完全不同。 首先我们重新定义一下JavaScript中的“构造函数”。在JavaScript中,构造函数只是一些 使用new操作符时被调用的函数。它们并不会属于某个类,也不会实例化一个类。实际上, 它们甚至都不能说是一种特殊的函数类型,它们只是被new操作符调用的普通函数而已。
function foo(a) {
this.a = a;
}
var bar = new foo(2);
console.log( bar.a ); // 2
使用new来调用foo(..)时,我们会构造一个新对象并把它绑定到foo(..)调用中的this 上。new是最后一种可以影响函数调用时this绑定行为的方法,我们称之为new绑定。
箭头函数
我们只需要记住重点,箭头函数没有自己的 this
绑定,而是继承自外层作用域的 this
。
优先级
绑定之间也有优先级。
显示绑定,new绑定,隐式绑定,默认绑定,箭头函数绑定。
优先级依次下降。
关于this指向问题就讲到这,如果你还想了解更多,请去看《你不知道的JavaScript》这本书,相信会给你带来更大的收获。