资讯专栏INFORMATION COLUMN

JavaScript中this终极理解(1)

YacaToy / 2453人阅读

摘要:关键字是中一个复杂的机制,它被自动定义在所有的函数作用域中。指向它自身匿名函数无法指向自身第一个函数被称为具名函数,在它内部可以使用来引用自身。的绑定和函数声明的位置没有任何关系,取决于函数的调用方式。这是理解的前提。

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实际上时在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/89948.html

相关文章

  • JavaScriptthis终极理解(2)

    摘要:找到函数的调用位置最重要的是要分析调用栈就是为了到达当前执行位置所调用的所有函数。显示绑定我们可以使用函数的和方法,通过这两个方法可以在某个对象上强制调用函数。 在上一篇我们了解过每个函数的this是在调用的时候绑定的,完全却决于函数的调用位置(也就是函数的调用方法)。 1. 调用位置 在理解this的绑定过程之前,首先要理解调用位置:调用位置就是函数在代码中被调用的位置,而不是声明的...

    liujs 评论0 收藏0
  • 笔试题之Event Loop终极

    摘要:下面开始分析开头的代码第一轮事件循环流程整体作为第一个宏任务进入主线程,遇到,输出遇到函数声明,声明暂时不用管遇到,其回调函数被分发到微任务中。我们记为遇到,其回调函数被分发到宏任务中。 先上一道常见的笔试题 console.log(1); async function async1() { console.log(2); await async2(); con...

    niceforbear 评论0 收藏0
  • 面试题之Event Loop终极

    摘要:下面开始分析开头的代码第一轮事件循环流程整体作为第一个宏任务进入主线程,遇到,输出遇到函数声明,声明暂时不用管遇到,其回调函数被分发到微任务中。我们记为遇到,其回调函数被分发到宏任务中。 先上一道常见的笔试题 console.log(1); async function async1() { console.log(2); await async2(); con...

    233jl 评论0 收藏0
  • jsthis的“终极三问”

    摘要:是什么本质是一个绑定,在函数被调用时建立。它的指向是完全由函数被调用的调用点来决定的。因为函数的调用点在全局作用域,所以指向全局变量这里就是函数的调用点存在的意义在函数体内部指代函数当前的运行环境。从而实现干净的设计和更容易的复用。 this是什么? this 本质是一个绑定, 在函数被调用时建立。它的指向是完全由函数被调用的调用点来决定的。 function baz() { ...

    silvertheo 评论0 收藏0
  • 终极蛇皮上帝视角之微信小程序之告别 setData

    摘要:而小程序官方的是在中调用方法来改变数据,从而改变界面。为了写测试让咱们来重构一把,利用学习过的函数式编程中的高阶函数把依赖注入。也就是说当中的某个数据更新的时候,我们并不知道它会影响哪个中的属性,特别的还有依赖于的情况。 众所周知 Vue 是借助 ES5 的 Object.defineProperty 方法设置 getter、setter 达到数据驱动界面,当然其中还有模板编译等等其他...

    wuyumin 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<