有关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 ...
随机推荐
- Java 类加载
类的生命周期 类的生命周期是从class文件到类加载 开始.类卸载结束. https://gitbook.cn/books/5cb844a55f75cc777e4c2320/index.html
- Flink简介
Flink简介 Flink的核心是一个流式的数据流执行引擎,其针对数据流的分布式计算提供了数据分布,数据通信以及容错机制等功能.基于流执行引擎,Flink提供了诸多更高抽象层的API以方便用户编写分布 ...
- <JavaScript> 关于闭包和this对象
1.this指向windows是如何得出的 var name = "The Window"; var object = { name : "My Object" ...
- Android:高通平台性能调试
1.GPU / CPU 信息打印脚本 gpu_cpu_info.bat @echo off echo "==================GPU====================== ...
- java+断点续传
在Web应用系统开发中,文件上传和下载功能是非常常用的功能,今天来讲一下JavaWeb中的文件上传和下载功能的实现. 先说下要求: PC端全平台支持,要求支持Windows,Mac,Linux 支持所 ...
- android之Framework问题总结:
移动开发知识体系总章(Java基础.Android.Flutter) Android Handler消息机制 . Android中为什么主线程不会因为Looper.loop里的无限循环ANR? 1.1 ...
- python进阶--多线程多进程
一.线程和进程 进程是拥有独立内存,能够独立运行的最小单位,也是程序执行的最小单位,线程是程序运行过程中,一个单一的顺序控制流程,是程序执行流的最小单位,一个进程至少包含一个线程,多线程共享进程的内存 ...
- Allegro静态铜皮避让问题
使用Allegro的人都知道,Allegro的铜分为静态和动态,我的设计习惯是需要满足载流地方一般使用静态铜皮,避免设计过程中因为打孔把铜皮割裂,这是静态铜皮的一个特性,不会自动避让,强制打孔或者走线 ...
- Unity 实现橡皮擦效果
我所实现的橡皮擦效果是设置图片某点的像素的透明度为0,来简单实现擦除效果的: 下面是效果 首先需要注意两点:1:设置 Main Camera 的 projection 属性为Orthographic ...
- python+opencv实现车牌定位
写在前面 HIT大三上学期视听觉信号处理课程中视觉部分的实验三,经过和学长们实验的对比发现每一级实验要求都不一样,因此这里标明了是2019年秋季学期的视觉实验三. 由于时间紧张,代码没有进行任何优化, ...