有关this指针指向问题
在下面两个写法中
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指针指向问题的更多相关文章
- 指针的指针&指向指针数组的指针
一.指针的指针 指针的指针看上去有些令人费解.它们的声明有两个星号.例如: char ** cp; 如果有三个星号,那就是指针的指针的指针,四个星号就是指针的指针的指针的指针 ...
- OC3-父类指针指向子类对象
// // Cat.h // OC3-父类指针指向子类对象 // // Created by qianfeng on 15/6/17. // Copyright (c) 2015年 qianfeng. ...
- 关于C++的子类指针指向父类
基类指针引用派生类对象 用基类指针引用一个派生类对象,由于派生类对象也是基类的对象,所以这种引用是安全的; 但是只能引用基类成员. 若试图通过基类指针引用那些只在派生类中才有的成员,编译器会报告语法错 ...
- restrict关键字(暗示编译器,某个指针指向的空间,只能从该指针访问)
我们希望某个对象(内存空间)不被修改的通常做法是什么?声明该空间的const类型,但是这样真的可以吗?是不是的,由于const空间对象的指针是可以付给一个非const值指针的.所以这仍然无法不让该空间 ...
- c++ 动态判断基类指针指向的子类类型(typeid)
我们在程序中定义了一个基类,该基类有n个子类,为了方便,我们经常定义一个基类的指针数组,数组中的每一项指向都指向一个子类,那么在程序中我们如何判断这些基类指针是指向哪个子类呢? 本文提供了两种方法 ( ...
- C++析构函数的自动调用(用于父类指针指向子类对象,内存泄漏问题)
class A {public:A() { printf("A \n"); }~A() { printf(" ~A \n"); } // 这里不管写不写virt ...
- 把x指针指向的4个字节次序颠倒过来
举例:x指向的内存地址,其字节内容从低到高依次分别为c1,c2,c3,c4(Delphi读取一个integer的时候,结果是c4c3c2c1,其排列规则是"高高低低"),那么结果是 ...
- go语言笔记——切片底层本质是共享数组内存!!!绝对不要用指针指向 slice切片本身已经是一个引用类型就是指针
切片 切片(slice)是对数组一个连续片段的引用(该数组我们称之为相关数组,通常是匿名的),所以切片是一个引用类型(因此更类似于 C/C++ 中的数组类型,或者 Python 中的 list 类型) ...
- 父类指针指向子类内存,为什么当父类的成员函数不加virtual时,访问的还是父类的成员函数,而不是子类同名的成员函数
我认为是这样,类的成员函数都在代码区,不同的类的成员函数在代码区有自己的类名称空间限制,类的虚函数在虚函数表中,程序执行的时候,是先在虚函数表中找该成员函数,如果没有找到,就去该类在代码区的成员函数中 ...
- foreach() 中用指针指向数组元素,循环结束后最好销毁指针
之前发过一次微博,今天又遇到这个问题,并且再次犯错,于是决定再加深一下. 就举php.net里的一个例子吧 $a = array('abe','ben','cam'); foreach ($a as ...
随机推荐
- zookeeper源码 — 五、处理写请求过程
目录 处理写请求总体过程 客户端发起写请求 follower和leader交互过程 follower发送请求给客户端 处理写请求总体过程 zk为了保证分布式数据一致性,使用ZAB协议,在客户端发起一次 ...
- apt如何列出所有已经安装的软件包
apt如何列出所有已经安装的软件包 转 https://www.helplib.com/ubuntu/article_155294 问题: 我想将所有已安装软件包的列表输出到文本文件中,以便我可以查看 ...
- python实现并发服务器实现方式(多线程/多进程/select/epoll)
python实现并发服务器实现方式(多线程/多进程/select/epoll) 并发服务器开发 并发服务器开发,使得一个服务器可以近乎同一时刻为多个客户端提供服务.实现并发的方式有多种,下面以多进 ...
- SQL语法介绍
一.Select 查询 语法: mysql> help selectName: 'SELECT'Description:Syntax:SELECT [ALL | DISTINCT | DISTI ...
- windows和Linux下定时启动或关闭服务
http://blog.csdn.net/clare504/article/details/17410643 1.Windows下的定时启动程序可以采用系统的计划和任务,定时启动服务可以在计划任务中添 ...
- js 匿名函数 js-函数定义方法
1.任何函数都是有返回值的,没有返回值的,在某些语言里称之为过程例如PL/SQL 2.js中的函数如果没有return 关键字指明给出的返回值,那么当调用完函数后,会返回“undefined" ...
- 1-18-2 LVM管理和ssm存储管理器使用&磁盘配额 (二)
LVM管理和ssm存储管理器使用&磁盘配额 (二) 内容如下: ü LVM快照 ü ssm存储管理器的使用 ü 磁盘配额 第1章 LVM快照 lvm快照:为了保持系统的一致性,我们先做 ...
- XGBoost原理详解
原文:https://blog.csdn.net/qq_22238533/article/details/79477547
- celery监控工具flower
特性 用Celery事件实时监控 任务进程和历史 能够显示任务的详细信息(arguments, start time, runtime等) 图形化和统计 远程控制 查看worker状态和统计 关闭和重 ...
- RESTful架构(Representational State Transfer资源表现层状态转换)
1. 什么是REST REST全称是Representational State Transfer,中文意思是表述(编者注:通常译为表征)性状态转移. 它首次出现在2000年Roy Fielding的 ...