JavaScript中this终极理解(1)

fsrookie

this关键字是JavaScript中一个复杂的机制,它被自动定义在所有的函数作用域中。

1. 为什么要用this

function identify() {
    return this.name.toUpperCase()
}

function speak() {
    var greeting = 'hello, i'am' + identify.call(this)
    console.log(greeting)
}

var me = {
    name: 'Kyle'
}
var you = {
    name: 'Reader'
}

identify.call(me); //KYLE
identify.call(you) //READER

speak.call(me) //Hello, 我是KYLE
speak.call(you) //Hello, 我是READER

上面这部分代码在不同的上下文对象中重复使用identify()和speak(),不用针对每个对象编写不同版本的函数。
如果不使用this,那就需要给identify()和speak()显示传入一个上下文对象

function identify(context) {
    return context.name.toUpperCase();
}
function speak(context) {
    var greeting = 'Hello i'am' + identify(context)
    console.log(greeting)
}
identify(you) //READER
speak(me) //hello, 我是KYLE

然而,this提供了一种更优雅的方式来隐式传递一个对象引用,因此可以将API设计的更加简洁且易于复用。
当你的代码越来越复杂的时候,显示的传递上下文对象会变得很混乱。

2. 关于this的误解之

误解之this是指向自身

通常会将this理解成指向函数自身。平常我们会在函数内部调用自身(例如递归)。在JavaScript中函数也是一个对象,那么我们可以在调用函数的时候存储状态(属性的值)。
我们看下以下代码,会发现this并没有指向函数本身:

//记录foo的调用次数
function foo(num) {
    console.log("foo: "+ num)
    this.count ++ 
}
foo.count = 0;
var i;
for(i=0; i<10; i++) {
    if(i>5) {
        foo(i)
    }
}

//foo: 6
//foo: 7
//foo: 8
//foo: 9
//foo 被调用了多少次?
console.log(foo.count) //0

可以看到foo()执行了4次,但是foo.count仍然是0,所以从字面上理解this指向的是当前函数自身就是错误的!
在执行foo.count=0的时候,确实向函数对象foo添加了一个count属性,但是函数内部的this.count的this并不是指向那个函数对象(其实是window对象)。
那么增加的是哪个count?这是创建在全局变量的一个count,值为NaN.

如果要从函数对象内部引用它自身,那只使用this是不够的。一般你需要通过一个指向函数对象的词法标识符来引用。

function foo() {
    foo.count = 4    //foo指向它自身
}
setTimeout(function() {
    //匿名函数无法指向自身
}, 10)

第一个函数被称为具名函数,在它内部可以使用foo来引用自身。但是在第二个例子中,传入setTimeout(..)的回调函数没有名称标识符,因此无法从函数内部引用自身。

还有一种方法是通过强制this指向foo函数对象

function foo(num) {
    console.log("foo:" + num)
    this.count ++
}
foo.count = 0
var i;
for(i=0; i<10; i++) {
    if(i>5) {
        foo.call(foo, i)
    }
}

如上,我们强制this指向了foo,这样就可以获得我们想要的答案了。

误解之它的作用域

第二种错误的理解是this指向函数的作用域。这个问题有些复杂,因为在某种情况下它是正确的。
需要明确的是,this在任何情况下都不指向函数的词法作用域。在JavaScript内部,作用域确实很像对象,可见的标识符都是它的属性。但是作用域对象是无法通过JavaScript代码进行访问,它是在JavaScript引擎内部。

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

function bar() {
    console.log(this.a)
}

foo(); // a is not defined

以上代码运行是不会得到你理想的结果的,因为你不能使用this来引用一个词法作用域内部的东西。

3. this到底是什么

this是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。this的绑定和函数声明的位置没有任何关系,取决于函数的调用方式

当一个函数调用时,会创建一个活动记录(有时候也称为执行上下文)。这个记录 会包含函数在哪里被调用(调用栈),函数的调用方法,传入的参数信息。this就是记录的其中一个属性,会在函数执行的过程中用到。

总结

这里我们要明白this既不是指向函数自身,也不是指向函数的词法作用域。这是理解this的前提。
this实际上时在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用。

阅读 1.1k

不想当架构的前端不是一个好厨子
好的前端是慢慢积累出来

目前很多文章都是摘抄记录其他教程。见谅。

2.8k 声望
250 粉丝
0 条评论
你知道吗?

目前很多文章都是摘抄记录其他教程。见谅。

2.8k 声望
250 粉丝
文章目录
宣传栏