js
的 this
是个比较令人头疼的东西,尤其是在面试的时候,深受面试官的喜爱。我们今天来谈谈 js 中的 this
this 是什么,为什么要用 this
它是一个在每个函数作用域中自动定义的特殊标识符关键字, this
机制提供了更优雅的方式来隐含地“传递”一个对象引用,导致更加干净的 API 设计和更容易的复用。当我们的代码和使用环境约复杂,我们就越能感受到 this
的重要性
先看一段代码
let x = {
num: 1,
sum: function(data) {
return this.num + data
}
}
let y = {
num: 1,
sum: function(data) {
return this.num + data
}
}
let y_sum = y.sum
console.log(x.sum(1)) // 2
console.log(y_sum(1)) // NaN
单从上面的代码来看,两者似乎没有区别,但是运行后的结果确是不一样的
在看一段代码
let x = {
num: 1,
sum: function(data) {
return this.num + data
}
}
let y = {
num: 1,
sum: data => {
return this.num + data
}
}
console.log(x.sum(1)) // 2
console.log(y.sum(1)) // NaN
也是一样,看着俩段代码差别不大,但是结果却完全不一样
var num = 2
var obj = {
num: 1,
say: function () {
setTimeout(function(){
console.log(this.num) // 2
}, 0)
}
}
obj.say()
一般函数的this
指向是指执行该函数的运行对象
let x = {
num: 1,
sum: function(data) {
return this.num + data
// 这里的this是指运行了sum函数的x。所以this.num = 1
}
}
let y = {
num: 1,
sum: function(data) {
return this.num + data
// 由于这里运行sum函数的不再是y而是window。所以这里相当于window.num = undefined
}
}
let y_sum = y.sum
console.log(x.sum(1)) // 2
console.log(y_sum(1)) // NaN
箭头函数的指向是指向运行该函数的父执行上下文的this
,箭头函数中的this
是在定义函数的时候绑定,而不是在执行函数的时候绑定。
let x = {
num: 1,
sum: function(data) {
return this.num + data
// 这里的this指的是x
}
}
let y = {
num: 1,
sum: data => {
return this.num + data
}
// 这里因为是箭头函数,所以这里的this在sum函数定义的时候就开始绑定了,这里的this是window
}
console.log(x.sum(1)) // 2
console.log(y.sum(1)) // NaN
var obj = {
say: function () {
let _say = () => {
console.log(this);
// 这里的this就是say,因为在定义_say时,_say会去拿父执行上下文的this
}
return _say()
}
}
obj.say()
对于匿名函数来说,this的指向是window
var num = 2
var obj = {
num: 1,
say: function () {
setTimeout(function(){
console.log(this.num) // 2
// 这里的this是window
}, 0)
}
}
obj.say()
// 这里如果是箭头函数,结果又不一样了
var obj2 = {
num: 1,
say: function () {
setTimeout(() =>{
console.log(this.num) // 1
}, 0)
}
}
obj2.say()
虽然有的时候,函数会在window下调用,this会指向window对象,但是this 不会以任何方式指向函数的 词法作用域
function foo() {
var a = 2;
this.bar();
}
function bar() {
console.log( this.a );
}
foo(); //undefined
开发者试图用 this 在 foo() 和 bar() 的词法作用域间建立一座桥,使得bar() 可以访问 foo()内部作用域的变量 a。这样的桥是不可能的。 你不能使用 this 引用在词法作用域中查找东西。这是不可能的。
更多练习
function foo() {
console.log( this.a );
}
var obj2 = {
a: 42,
foo: foo
};
var obj1 = {
a: 2,
obj2: obj2
};
obj1.obj2.foo(); // 42
// 这里最终调用foo函数的依然是obj2
function foo() {
console.log( this.a );
}
function doFoo(fn) {
// `fn` 只不过 `foo` 的另一个引用
// 在这里fn() != obj.foo()
fn();
}
var obj = {
a: 2,
foo: foo
};
var a = "oops, global";
doFoo( obj.foo ); // "oops, global"
var obj = {
say: function () {
function _say() {
// this 是什么?想想为什么?
console.log(this)
}
return _say.bind(obj)
}()
}
obj.say()
// 这里_say通过bind将this指向了obj,但是打印的结果却还是window,为什么呢?
// 这里可以看作: 因为 = 赋值语句是由右向左运行的。所以这里的obj应该是个undefined,所以this指向了window
换种写法就会完全不一样了
var obj = {}
obj.say: function () {
function _say() {
console.log(this)
}
return _say.bind(obj)
}()
obj.say()
// 这里this指向了obj