JavaScript中this终极理解(1)

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


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

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

2.9k 声望
255 粉丝
0 条评论
推荐阅读
前端路由解析以及实现(学习)
1. 什么是前端路由路由的概念来源于服务端,在服务端中路由描述的是URL与处理函数之间的映射关系。在web前端单页面应用中,路由描述的是 URL 和 UI 之间的映射关系,这种映射关系是单向的,即 URL 变化引起 UI 更...

fsrookie阅读 1.6k

从零搭建 Node.js 企业级 Web 服务器(零):静态服务
过去 5 年,我前后在菜鸟网络和蚂蚁金服做开发工作,一方面支撑业务团队开发各类业务系统,另一方面在自己的技术团队做基础技术建设。期间借着 Node.js 的锋芒做了不少 Web 系统,有的至今生气蓬勃、有的早已夭折...

乌柏木148阅读 12.2k评论 10

JavaScript有用的代码片段和trick
平时工作过程中可以用到的实用代码集棉。判断对象否为空 {代码...} 浮点数取整 {代码...} 注意:前三种方法只适用于32个位整数,对于负数的处理上和Math.floor是不同的。 {代码...} 生成6位数字验证码 {代码...} ...

jenemy46阅读 5.9k评论 12

从零搭建 Node.js 企业级 Web 服务器(十五):总结与展望
总结截止到本章 “从零搭建 Node.js 企业级 Web 服务器” 主题共计 16 章内容就更新完毕了,回顾第零章曾写道:搭建一个 Node.js 企业级 Web 服务器并非难事,只是必须做好几个关键事项这几件必须做好的关键事项就...

乌柏木66阅读 6.1k评论 16

再也不学AJAX了!(二)使用AJAX ① XMLHttpRequest
「再也不学 AJAX 了」是一个以 AJAX 为主题的系列文章,希望读者通过阅读本系列文章,能够对 AJAX 技术有更加深入的认识和理解,从此能够再也不用专门学习 AJAX。本篇文章为该系列的第二篇,最近更新于 2023 年 1...

libinfs39阅读 6.3k评论 12

封面图
从零搭建 Node.js 企业级 Web 服务器(一):接口与分层
分层规范从本章起,正式进入企业级 Web 服务器核心内容。通常,一块完整的业务逻辑是由视图层、控制层、服务层、模型层共同定义与实现的,如下图:从上至下,抽象层次逐渐加深。从下至上,业务细节逐渐清晰。视图...

乌柏木43阅读 7.3k评论 6

从零搭建 Node.js 企业级 Web 服务器(二):校验
校验就是对输入条件的约束,避免无效的输入引起异常。Web 系统的用户输入主要为编辑与提交各类表单,一方面校验要做在编辑表单字段与提交的时候,另一方面接收表单的接口也要做足校验行为,通过前后端共同控制输...

乌柏木33阅读 6.2k评论 9

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

2.9k 声望
255 粉丝
宣传栏