你不知道的JavaScript(四)this(一)

来源:互联网 发布:淘宝店铺评价好的包包 编辑:程序博客网 时间:2024/05/03 12:57

this到底是什么

this绑定的是调用位置所在的对象:并不是调用所在的作用域或者调用函数本身。

首先来看看调用位置

调用位置就是函数在代码中被调用的位置。要找到函数调用的位置,需要分析调用栈(就是为了到达当前执行位置所调用的所有函数),调用位置就在当前正在执行的函数的前一个调用中。
举个例子:

function baz() {    //当前调用栈是:baz    //因此,调用位置是全局作用域    console.log("baz");    bar();//<-- bar的调用位置}function bar() {    //当前调用栈是baz-->bar    //因此,当前调用位置在baz中    console.log("bar");    foo();//<-- foo的调用位置}function foo() {    //当前调用栈是baz-->bar-->foo    //因此当前调用为置在bar中    console.log("foo");}baz();//<--baz的调用位置

找到调用位置后,我们需要确定this的绑定对象,下面介绍this绑定的四条规则。

绑定规则

绑定规则有四种,本文介绍其中两种,剩余两种在下一篇博客中介绍。
1. 默认绑定
函数直接使用不带任何修饰的函数引用进行调用,使用默认绑定,无法应用其他规则。如果不能理解,看下面的例子:

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

上述代码中,foo()的调用位置是全局作用域,而且是直接进行函数引用的调用,this绑定到全局对象,this.a被解析成全局变量a。
如果使用严格模式(strict mode),全局对象将无法使用默认绑定,this会绑定到undefined:

function foo() {    "use strict";    console.log(this);}foo();//undefined

还有一个重要的细节需要注意,虽然this的绑定规则取决于调用位置,但是如果函数运行在严格模式下,则this的绑定与函数的调用位置无关,一律绑定到全局对象上:

function foo() {    console.log(this.a);}var a=2;function bar() {    var a=3;    foo();}bar();//2

2. 隐式绑定
如果函数的调用位置有上下文对象,或者说函数被某个对象拥有或包含,则使用隐式绑定。

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

obj对象拥有foo属性,值为foo()函数,调用位置使用obj上下文来引用函数,隐式绑定规则会把函数调用中的this绑定到这个上下文对象obj上,这时,obj.athis.a是一样的。
对象属性引用链中只有最顶层或者说最后一层会影响调用位置,举例来说:

function foo() {    console.log(this.a);}var obj2={    a:42,    foo:foo}var obj1={    a:2,    obj2:obj2}obj1.obj2.foo();//42

隐式丢失
一个最常见的this绑定问题就是被隐式绑定的函数会丢失绑定对象,也就是说它会应用默认绑定,从而把this绑定到全局对象或者undefined上(取决于是否是严格模式)。
看下面的代码:

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

虽然bar是obj.foo的一个引用,但是实际上,它引用的是foo函数本身,因此此时的bar()其实是一个不带任何修饰的函数调用,因此应用了默认绑定。
传入回调函数的情况:

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

参数传递也是一种隐式赋值,本例将obj.foo赋值给参数fn,所以结果和上例一样。