一、问题的由来

学懂 JavaScript 语言,一个标志就是理解下面两种写法,可能有不一样的结果。


var obj = {foo: function () {}
};var foo = obj.foo;// 写法一
obj.foo()// 写法二
foo()

上面代码中,虽然obj.foofoo指向同一个函数,但是执行结果可能不一样。请看下面的例子。


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

这种差异的原因,就在于函数体内部使用了this关键字。很多教科书会告诉你,this指的是函数运行时所在的环境。对于obj.foo()来说,foo运行在obj环境,所以this指向obj;对于foo()来说,foo运行在全局环境,所以this指向全局环境。所以,两者的运行结果不一样。

这种解释没错,但是教科书往往不告诉你,为什么会这样?也就是说,函数的运行环境到底是怎么决定的?举例来说,为什么obj.foo()就是在obj环境执行,而一旦var foo = obj.foofoo()就变成在全局环境执行?

本文就来解释 JavaScript 这样处理的原理。理解了这一点,你就会彻底理解this的作用。

二、内存的数据结构

JavaScript 语言之所以有this的设计,跟内存里面的数据结构有关系。


var obj = { foo:  5 };

上面的代码将一个对象赋值给变量obj。JavaScript 引擎会先在内存里面,生成一个对象{ foo: 5 },然后把这个对象的内存地址赋值给变量obj

也就是说,变量obj是一个地址(reference)。后面如果要读取obj.foo,引擎先从obj拿到内存地址,然后再从该地址读出原始的对象,返回它的foo属性。

原始的对象以字典结构保存,每一个属性名都对应一个属性描述对象。举例来说,上面例子的foo属性,实际上是以下面的形式保存的。


{foo: {[[value]]: 5[[writable]]: true[[enumerable]]: true[[configurable]]: true}
}

注意,foo属性的值保存在属性描述对象的value属性里面。

三、函数

这样的结构是很清晰的,问题在于属性的值可能是一个函数。


var obj = { foo: function () {} };

这时,引擎会将函数单独保存在内存中,然后再将函数的地址赋值给foo属性的value属性。


{foo: {[[value]]: 函数的地址...}
}

由于函数是一个单独的值,所以它可以在不同的环境(上下文)执行。


var f = function () {};
var obj = { f: f };// 单独执行
f()// obj 环境执行
obj.f()

四、环境变量

JavaScript 允许在函数体内部,引用当前环境的其他变量。


var f = function () {console.log(x);
};

上面代码中,函数体里面使用了变量x。该变量由运行环境提供。

现在问题就来了,由于函数可以在不同的运行环境执行,所以需要有一种机制,能够在函数体内部获得当前的运行环境(context)。所以,this就出现了,它的设计目的就是在函数体内部,指代函数当前的运行环境。


var f = function () {console.log(this.x);
}

上面代码中,函数体里面的this.x就是指当前运行环境的x


var f = function () {console.log(this.x);
}var x = 1;
var obj = {f: f,x: 2,
};// 单独执行
f() // 1// obj 环境执行
obj.f() // 2

上面代码中,函数f在全局环境执行,this.x指向全局环境的x

obj环境执行,this.x指向obj.x

回到本文开头提出的问题,obj.foo()是通过obj找到foo,所以就是在obj环境执行。一旦var foo = obj.foo,变量foo就直接指向函数本身,所以foo()就变成在全局环境执行。

总结:

this是用于在函数体内部,指代函数当前的运行环境。

阮一峰的JavaScript 的 this 原理相关推荐

  1. 阮一峰的Javascript模块化编程(三):require.js的用法

    写的很好 链接地址 :http://www.ruanyifeng.com/blog/2012/11/require_js.html 转载于:https://www.cnblogs.com/susan- ...

  2. react 阮一峰_React 最简单的入门应用项目

    学习一项工具,最快的方式就是边学便用.在学习 GUI 时我会边看API边写一个记事本,学习 web 框架时我会边学边写一个留言板. 学习和工作上一直是在写后端代码,有一点前端基础,但没有用过JS框架, ...

  3. 下面属于javascript内部对象的有_JavaScript quot;thisquot;原理-转载阮一峰

    作者: 阮一峰 JavaScript 的 this 原理​www.ruanyifeng.com 一.问题的由来 学懂 JavaScript 语言,一个标志就是理解下面两种写法,可能有不一样的结果. v ...

  4. Javascript 基础教程 阮一峰

    Javascript 基础教程 阮一峰 2021-11-16 阮一峰推荐的入门教程,重在查漏补缺 https://wangdoc.com/javascript/index.html 第一章 入门 JS ...

  5. 读阮一峰对《javascript语言精粹》的笔记,我有疑问。

    <javascript语言精粹>是一本很棒的书籍,其中作者在附录列出了12种他所认为的javascript语言中的糟粕. 我最近开始跟读前端前辈的博客,其中读到了阮一峰的<12种不宜 ...

  6. 学习阮一峰Javascript模块化编程,requireJS使用

    使用背景NOW: 网站功能逐渐丰富,网页中的js也变得越来越复杂和臃肿,原有通过script标签来导入一个个的js文件这种方式已经不能满足现在互联网开发模式. 项目需要一个团队分工协作.进度管理.单元 ...

  7. 我看朴灵评注阮一峰的《JavaScript 运行机制详解:再谈Event Loop》

    阮一峰和朴灵对我来说都是大牛,他们俩的书我都买过,阮老师的译作<软件随想录>和朴灵的<深入浅出node.js>.这个事情已经过了4个月了,所以我拿来讲应该也没啥问题. 这件事情 ...

  8. Javascript 面向对象编程(一):封装 ——转自阮一峰博客

    Javascript 面向对象编程(一):封装 作者: 阮一峰 日期: 2010年5月17日 学习Javascript,最难的地方是什么? 我觉得,Object(对象)最难.因为Javascript的 ...

  9. 前端知乎:关于阮一峰博客《学习Javascript闭包》章节中最后两个思考题

    阮一峰博客:<学习Javascript闭包>章节中最后有个思考题: 如果你能理解下面两段代码的运行结果,应该就算理解闭包的运行机制了. 代码片段一 var name = "The ...

最新文章

  1. 使用read write 读写socket
  2. springboot 中的 这种策略模式运用实战
  3. UA PHYS515A 电磁理论III 静磁学问题3 静磁学问题的边界条件与标量势方法的应用
  4. Java必会的基础知识(3)
  5. Drop Table对MySQL的性能影响分析
  6. Acrobat Reader DC 2020 for mac(最好用的免费PDF阅读器) 2020新增功能
  7. JDK/Java 14 可能带来什么新特性?
  8. Ubuntu Linux下使用锐捷认证的方法
  9. c语言log_C语言最大难点揭秘:编程的祸根!
  10. 关于ReactNative0.56版本Flatlist列表内容跳动的问题
  11. oracle12c dba或者sys身份的账户和密码,怎么一次性安装好oracle 12c依赖包
  12. javascript及css实现居中效果
  13. Web大学生网页作业成品~美食餐饮网站设计与实现(HTML+CSS+JavaScript)
  14. 2.7 矩阵分块及矩阵乘法的四种方式
  15. JAVA中成员变量和局部变量区别
  16. onkeyup+onafterpaste 只能输入数字和小数点--转载
  17. 数值分析常见算法C++实现
  18. android 第三方SDK接入一般流程
  19. Revit综合软件【加强过滤】的使用方法
  20. win10-SW2016工程图关联零件属性链接操作

热门文章

  1. python整数类型没有取值范围限制_详解Python中6种数据类型
  2. Python | Lambda函数与示例
  3. oracle创建用户名了,oracle创建用户名
  4. java中map的遍历方法_Java中Map的三种遍历方式
  5. 链接服务器访问接口返回了消息没有活动事务,因为链接服务器 SQLEHR 的 OLE DB 访问接口 SQLNCLI10 无法启动分布式事务。...
  6. Java SimpleTimeZone setStartRule()方法与示例
  7. Java SecurityManager checkAwtEventQueueAccess()方法与示例
  8. 终极解密输入网址按回车到底发生了什么?
  9. Spring中的重试功能!嗯,有点东西
  10. 一文学搞懂阿里开源的微服务新贵Nacos!