JS this 详解
为什么要用this
function foo(){
console.log(`我本身属性a是 ${this.a}`)
}
var bar ={
a:2,
foo:foo
}
var baz={
a:4,
foo:foo
}
bar.foo();//我本身属性a是 2
baz.foo()://我本身属性a是 4
foo()只定义了一次,去可以被不同的对象引用,实现了代码共享。
this的由来
javascript允许在函数体内,引用当前环境的其他变量
var fn = function(){
console.log(x)
}
上面代码使用变量x,而变量x由当前的运行环境提供。
函数可以在不同的运行环境下执行,所以需要一种机制,可以在函数内部获取当前的运行环境(context),所以this就出现了,它的作用就是在函数体内部,指代当前的运行环境。
this的几种模式
this有以下几种情况
1.方法调用模式下,this总是指向调用它所在方法的对象,this的指向与所在方法的调用位置有关,而与方法的声明位置无关(箭头函数特殊)
2.函数调用下,this指向window,调用方法没有明确对象的时候,this指向window,如setTimeout,匿名函数等
3.构造函数调用模式下,this指向被构造的对象
4.apply、call、bind调用模式下,this指向第一个参数。
5.箭头函数,在声明的时候绑定this,而非取决于调用位置,换句话说就是指向它的上一层。
6.严格模式下,如果this没有被执行环境(execution context)定义,那this是为undefined。
方法调用模式
var fn = function(){
console.log(this.x)
}
var x = "2"
var obj = {
x:"1",
fn:fn
}
//调用位置
obj.fn(); //1
//调用位置
fn(); //2
以上代码,可以看到,this指向调用它所在方法的对象,say方法在obj对象下,所以this指向obj,fn在window对象下,所以this指向window,也可以看出来this和声明位置无关,和调用位置有关。
函数调用模式
var x = "2"
//声明位置
var fn = function(){
console.log(this.x)
}
//调用位置
fn();//2
匿名函数,setTimeout:
var that = this;
(function(){
console.log(this === that) //true
})()
setTimeout(() => {
console.log(this === that) //true
}, 0);
可以看出以上所有情况,this指向window
call,apply,bind模式下
var obj = {
name: 'q1'
getName: function(){
console.log(this.name)
}
}
var otherObj = {
name:'q2'
}
var name = 'q3'
obj.getName() //q1
obj.getName().call() //q3
obj.getName().call(otherObj) //q2
obj.getName().apply() //q3
obj.getName().apply(otherObj) //q2
obj.getName().bind(this)() //q3
obj.getName().bind(otherObj)() //q2
构造函数模式下
var flag = undefined
function Fn(){
flag = this;
}
var obj = new Fn()
console.log(flag == obj)
其实,这个this指向obj,内部原理还是用apply把this指向obj的。
严格模式
"use strict"
var fn = function(){
return this
}
fn() == undefined; //true
可以看出,在严格模式下,fn是直接被调用的,并没有被执行环境所定义,也就是说不是作为对象的属性或方法调用的(如window.fn())
箭头函数
//声明位置
var fn = (()=>{
console.log(this.x)
})
var x = "2"
var obj = {
x:"1",
fn:fn
}
//调用位置
obj.fn(); //2
//调用位置
fn(); //2
以上可以看出,箭头函数在定义时就绑定了this,而非取决于调用位置了,同样的call, apply,bind都无法更改this
var globalObject = this
var foo = (() => this)
console.log(foo() === globalObject) // true
var obj = {foo: foo}
// 尝试用call来设定this
console.log(foo.call(obj) === globalObject) // true
// 尝试使用bind来设定this
foo = foo.bind(obj);
console.log(foo() === globalObject); //true