深入理解this原理(JavaScript)
JavaScript中this的原理
一、问题的由来
学懂 JavaScript 语言,一个标志就是理解下面两种写法,可能有不一样的结果。
> var obj = {
> foo: function () {}
> };
>
> var foo = obj.foo;
>
> // 写法一
> obj.foo()
>
> // 写法二
> foo()
上面代码中,虽然obj.foo和foo指向同一个函数,但是执行结果可能不一样。请看下面的例子。
> 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.foo,foo()就变成在全局环境执行?
本文就来解释 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)的更多相关文章
- node.js学习(三)简单的node程序&&模块简单使用&&commonJS规范&&深入理解模块原理
一.一个简单的node程序 1.新建一个txt文件 2.修改后缀 修改之后会弹出这个,点击"是" 3.运行test.js 源文件 使用node.js运行之后的. 如果该路径下没有该 ...
- 理解和使用 JavaScript 中的回调函数
理解和使用 JavaScript 中的回调函数 标签: 回调函数指针js 2014-11-25 01:20 11506人阅读 评论(4) 收藏 举报 分类: JavaScript(4) 目录( ...
- 摘抄--全面理解面向对象的 JavaScript
全面理解面向对象的 JavaScript JavaScript 函数式脚本语言特性以及其看似随意的编写风格,导致长期以来人们对这一门语言的误解,即认为 JavaScript 不是一门面向对象的语言,或 ...
- 深入理解Delete(JavaScript)
深入理解Delete(JavaScript) Delete 众所周知是删除对象中的属性. 但如果不深入了解delete的真正使用在项目中会出现非常严重的问题 (: Following 是翻译 ka ...
- 深入全面理解面向对象的 JavaScript
深入全面理解面向对象的 JavaScript (原著: 曾 滢, 软件工程师, IBM,2013 年 4 月 17 日) JavaScript 函数式脚本语言特性以及其看似随意的编写风格,导致长期以来 ...
- 深入理解 ProtoBuf 原理与工程实践(概述)
ProtoBuf 作为一种跨平台.语言无关.可扩展的序列化结构数据的方法,已广泛应用于网络数据交换及存储.随着互联网的发展,系统的异构性会愈发突出,跨语言的需求会愈加明显,同时 gRPC 也大有取代R ...
- 深入理解Faiss 原理&源码 (一) 编译
目录 深入理解Faiss 原理&源码 (一) 编译 mac下安装 安装mac xcode工具包 安装 openblas 安装swig 安装libomp 编译faiss 附录 深入理解Faiss ...
- Atitit 图像处理 深刻理解梯度原理计算.v1 qc8
Atitit 图像处理 深刻理解梯度原理计算.v1 qc8 1.1. 图像处理 梯度计算 基本梯度 内部梯度 外部梯度 方向梯度1 2. 图像梯度就是图像边缘吗?2 1.1. 图像处理 梯度计算 ...
- 深入理解PHP原理之变量作用域
26 Aug 08 深入理解PHP原理之变量作用域(Scope in PHP) 作者: Laruence( ) 本文地址: http://www.laruence.com/2008/08/26 ...
随机推荐
- python容器类型字典的操作
字典(dict):由大括号进行描述一组键值对,其键值对之间使用冒号隔开,键值对与键值对之间使用逗号隔开: 注意:字典的key可以为数字,但是不可以重复,因为key是唯一标识符: 1.声明一个字典:语法 ...
- eNSP仿真软件之利用单臂路由实现VLAN间路由
1. 实验原理 以太网中,通常会使用VLAN技术隔离二层广播域来减少广播的影响,并增强网络的安全性和可管理性.其缺点是同时也严格地隔离了不同VLAN之间的任何二层流量,使分属于不同VLAN的用户不能直 ...
- 微信小程序使用 ECharts 实现数据可视化
微信小程序使用 ECharts 显示图表 首先创建微信小程序 这里就不再赘述 下载 GitHub 上的 ecomfe/echarts-for-weixin 下载后解压,打开文件夹,里面的 ec-can ...
- PL真有意思(七):数据抽象和面向对象
前言 在之前的名字.作用域那篇提到模块类型,它使程序员可以从一个给定抽象出发,通过实例化产生多个实例:再后面是类,它使程序员可以定义一族相关的抽象. 在这一篇里,我们会来看一下面向对象程序设计及其三个 ...
- 小白学习python第一天,Pycharm破解与用法(持续更新)
目录 Pycharm安装与破解及汉化 Pycharm安装 Pycharm破解 Pycharm汉化 Pycharm使用 添加作者.时间等信息 补充 @ Pycharm安装与破解及汉化 本人最近开始找到了 ...
- linux [CTRL]+c与[CTRL]+d
[CTRL]+c:中断目前程序.用于在linux中输入了错误的命令或者参数,有的时候会在系统不停的运行,如果想让程序需停下来,可以使用[CTRL]+C [CTRL]+d:这个组合键代表着键盘输入结束( ...
- PV、UV、IP、TPS、QPS、RPS等网络度量术语
PV:(访问量)即Page View --页面浏览量或单击量.用户每一次对网站中的任何页面进行访问后,被记录为一次PV,用户对同一页面的多次刷新,访问量将进行累计: UV:(独立访客)即Unique ...
- sql 删除表数据并使ID自增重置
方法1:truncate table 你的表名//这样不但将数据全部删除,而且重新定位自增的字段 方法2:delete from 你的表名dbcc checkident(你的表名,reseed,0) ...
- Haproxy安装部署文档及多配置文件管理方案
一.部署安装 二.软件配置 三.系统服务 四.日志配置 五.小结 文章目录 最近我在负责一个统一接入层的建设项目,涉及到 Haproxy 和 ospf 的运维部署,本文分享一下我在部署 Haproxy ...
- 点击查看大图滑动预览(h5,pc通用)
点击预览大图并滑动观看,支持手机端和pc端,具体功能如下图: 一. touchTouch 的js和css 以及jquery依赖库 <link rel="stylesheet" ...