深入理解JavaScript-this

1. this的说明

this是在运行时绑定的。在绝大多数情况下,this值的确定与函数的调用方式有关。

2. 全局执行上下文中的this

在全局执行环境中(在任何函数体外部)this 都指向全局对象。在JS引擎运行global code之前,会创建一个全局执行上下文压入执行栈的栈底,这个全局执行上文的ThisBinding绑定的是全局对象,在浏览器里指的就是window。

3. 函数执行上下文中的this

JavaScript是静态作用域,词法环境是由代码结构决定的,开发把代码写成什么样,词法环境就是怎么样,跟方法在哪里调用没有关系。但是对于函数的this刚好反过来,跟代码在哪里定义没有关系,而跟代码在哪里调用有关系。一般我们调用函数有以下四种方式:

  • 普通函数调用,比如foo()或者(functon(){})()
  • 作为对象方法调用,比如obj.foo()
  • 构造函数调用,比如new foo()
  • 使用call、apply、bind等方法调用

在介绍着几种函数调用之前,我们先来看下ECMAScript对this的规范:

ECMAScript规范: 严格模式时,函数内的this绑定严格指向传入的thisArgument。非严格模式时,若传入的thisArgument为undefined或null时,函数内的this绑定指向全局的this;不为undefined或null时,函数内的this绑定指向传入的this。

3.1 普通函数调用

普通函数的调用,包括函数调用foo()和立即调用函数表达式(functon(){})(),传到函数里的thisArgument是undefined。根据ECMAScript规范,如果在非严格模式下,普通函数里的this就是全局对象,而在严格模式下就为undefined。

var a = 2;
function foo(val) {
    console.log(this.a); //2
    console.log(val); //3
}
foo(3); 
/**
foo(3) 相当于是:foo().call(undefined,3) 的简化版本。
为了代码方便,所以有直接普通函数调用,thisArgument 默认是undefined,不用每次都敲这个undefined。
**/

改变一下代码:

var a = 2;
function foo() {
    "use strict"
    console.log(this.a)
}
foo();

这段代码会报错:Cannot read property ‘a’ of undefined

3.2 对象方法调用

作为对象方法调用,传到函数里的thisArgument是该对象。比如有如下代码:

function foo() {
	console.log( this.a );
}

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

还有一种比较看起来像对象方法调用,实际上是普通函数调用:

function foo() {
    console.log(this.a);
}
var obj = {
    a: 2,
    foo: foo
}
var bar = obj.foo; 
var a = "global variable";
bar(); // global variable

3.3 构造函数调用

new functionname()构造函数调用,this指的是构造出来的新对象。

function foo(a) {
	this.a = a;
}

var bar = new foo( 2 );
console.log( bar.a ); // 2

3.4 使用call、apply、bind等方法调用

call、apply、bind调用,可以显示传递对象给函数的thisArg,默认这几个函数的第一个形参是thisArg:

Function.prototype.apply( thisArg, argArray )
Function.prototype.call( thisArg , arg1, [ arg2, ... ] )
Function.prototype.bind( thisArg , [ arg1, [ arg2, ... ] ] )

需要注意的是当thisArg为null或者undefined,在非严格模式下,this是全局对象。

var obj = {
    a: 1
};

function print() {
    console.log(this);
}

print.call(null);//window
print.call(undefined);//window
print.call(obj);//obj

3.5 箭头函数

箭头函数在调用的时候不会绑定this,它会去词法环境链上寻找this(parent scope),所以箭头函数的this取决于它定义的位置(包裹箭头函数的第一个普通函数的this),也就是箭头函数会跟包着它的作用域共享一个词法作用域。

window.a = 10
const foo = () => {		//箭头函数在这里定义
    console.log(this.a)
}
foo.call({a: 20}) // 10

let obj = {
    a: 20,
    foo: foo
}
obj.foo() //10

function bar() {
    foo()
}
bar.call({a: 20}) //10

4. 总结

  • 非严格模式下,函数普通调用,this指向全局对象。严格模式下,函数普通调用,this为undefined。
  • 函数作为对象方法调用,this指向这个对象。
  • 函数作为构造方法使用new调用,this指向新创建的对象
  • 非严格模式下,函数通过call,apply,bind间接调用,this指向传入的第一个参数,传入的参数如果为undefined或者null,则this指向全局对象。严格模式下,通过call,apply,bind间接调用,this严格指向第一个参数。
  • 箭头函数中没有this绑定,this的值取决于其创建时所在词法环境链中最近的this绑定
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值