this是javascript中非常基础的一个知识点,也是一个令很多初学者迷惑的知识点。
Ecmascript中对其描述如下:
There is a this value associated with every active execution context. The this value depends on the caller and the type of code being executed and is determined when control enters the execution context. The this value associated with an execution context is immutable.
this的取值跟executable code的类型直接相关,execute code有三种Global code,Function code 和Eval code。跟this取值有关的是前两种。
this在global code中的取值
这种情况下问题比较简单,this
的值就是global对象本身
this in global code
1 2 3 4 5 6 7 8 9 10 11 12 |
|
demo中的b没有通过var 声明,它实际上不是一个variable,而是window(global)的一个property。变量和属性的一个显著区别就是变量具有{ DontDelete },而后者没有。因此经常说的
不用var能直接声明全局变量
的说法是错误的,事实上它是为windows添加了一个属性。
variable and property
1 2 3 4 |
|
this在function code中的取值
在function code中this
的值不像在global code中那么简单:
- this的值不是与对应的函数绑定的,也就是说不是在函数创建时决定。
- this的值在进入上下文时决定,并且在执行过程中不可变。ie.不能像变量赋值一样为this赋值。
this
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
那么是哪些因素影响this的取值呢?在有些文章和书中有这样的说法:
this的取值由函数的定义方式决定,如果函数被定义成全局函数,则对应的this是global;如果函数是一个对象的方法(method),则this的值是对应的对象。
这种说法是错误的。来看一个例子:
Is the definition of function determing this
1 2 3 4 5 6 7 8 9 10 |
|
baz虽然和foo.bar的值一样,也就是指向同一个函数。但是调用结果不一样,因此this的取值不是由函数的定义决定的。
事实上,
this
的值由调用者提供(例如调用函数的父上下文),由函数的调用方式决定。
为什么this
的取值由函数的调用方式决定?
要回答这个问题需要先看下ECMA中的一个内部类型(internal type) – Reference
type
Reference type
Refference
是一个内部类型,没有任何函数能返回Reference
类型的值,无论是built-in函数还是用户定义的函数。
规范中关于Reference
的描述如下:
The internal Reference type is not a language data type. It is defined by this specification purely for expository purposes. However, a value of type Reference is used only as an intermediate result of expression evaluation and cannot be stored as the value of a variable or property.
The Reference type is used to explain the behaviour of such operators as delete, typeof, and the assignment operators.
other use of the Reference type is to explain the determination of the this value for a function call.
A Reference is a reference to a property of an object. A Reference consists of two components, the base object and the property name.
The following abstract operations are used in this specification to access the components of references:
- GetBase(V). Returns the base object component of the reference V.
- GetPropertyName(V). Returns the property name component of the reference V.
Reference
的结构可以描述如下:
Reference的结构
1 2 3 4 |
|
根据规范,仅在以下两种情况时会返回Reference
类型的值:
- 标识符解析(identifiers resolution),标识符包括变量名,函数名,函数参数名,还有就是上面提到的不用var声明,直接赋值的变量。
- 属性访问(Property access),属性访问有两种方式,通过
[]
或者.
,如foo.bar
,foo['bar']
Reference
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
Reference
是一个中间值,要获取real value,需要用到GetValue
方法(参见),其伪代码如下:
GetValue 伪代码
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
[[Get]]
函数会返回对象属性的值,该函数同时会考虑原型链上的继承属性。
综上,关于this
的取值:
- 函数中
this
的取值由调用者提供,由函数的当前调用方式决定 - 在函数调用括号
()
的左边如果是一个Reference
类型的值,那么this
的值将被设置为该值的base属性的值 - 所有其它情况(比如
()
左边的值不是Reference
类型),this
都将被设置为null
,因为null
没有任何意义,它们将会被隐式的转换成global object
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
|
在上面的demo里
- eg1中调用
foo()
时,根据上面提到的原则,foo是一个变量,经过标识符解析会返回一个Reference
type的值fooReference,this被设置成fooReference对象的base属性的值–global
- 在eg2中调用
foo.bar()
时,bar作为foo的一个属性,经过属性读取(property access)会返回一个Reference
类型的值fooBarReference,this被设置成base属性的值–foo
- 在eg3中我们把foo.bar赋值给了test,test作为标识符,在调用test的时候产生了testReference,因此返回的就是global
现在理解了同一个函数,使用不同的方式调用,为什么返回值不同了。
来看两个问题,考虑下执行结果将是怎样的:
思考
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
Function call and non-Reference type
上面提到过,当函数调用()
的左侧不是Reference类型值的时候,this将被设置为null,继而被隐式的转换成global。
non-Reference call
1 2 3 |
|
上面例子中()
的左侧是一个函数对象,既不是标识符也不是属性访问,因此不会返回Reference值,this将被设置为null,继而被转为global。
再来看几个复杂点的例子:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
相关推荐
this 的定义 表示当前执行代码的环境对象 因此可将 this 的剖析分为“全局环境” 和 “函数环境” 两种类型的环境对象 全局环境 ...javascript 引擎会先在内存中生成 { name: ‘Tom’ } 对象,接着再
本文实例讲述了JavaScript中的this基本问题.分享给大家供大家参考,具体如下: 在函数中 this 到底取何值,是在函数真正被调用执行的时候确定下来的,函数定义的时候确定不了。 执行上下文环境 : **定义**:执行...
在Javascript中,this的取值取决于调用的模式。调用模式一共有四种:方法调用模式、函数调用模式、构造器调用模式和apply调用模式。 调用模式方法调用模式当一个函数被保存为对象的一个属性时,我们称它为一个方法。...
箭头函数极大地简化了this的取值规则。 普通函数与箭头函数 普通函数指的是用function定义的函数: var hello = function () { console.log("Hello, Fundebug!"); } 箭头函数指的是用=>定义的函数: var hello...
在JavaScript中,call、apply和bind 是Function对象自带的三个方法,这三个方法的主要作用是改变函数中的this指向,从而可以达到`接花移木`的效果。本文将对这三个方法进行详细的讲解,并列出几个经典应用场景。 ...
在JavaScript中,并没有显示的声明私有成员的关键字等。所以要想实现封装/信息隐藏就需要从另外的思路出发。我们可以使用闭包的概念来创建只允许从对象内部访问的方法和属性,来达到封装的要求。 基本方式 一般来说...
对象 JavaScript操作都是关于对象的。数组(Array)是对象,函数(Function)是对象。Object(类型)是对象。那么什么是对象呢?对象就是“名称-值”对(name-value)。名称是字符串,值可以是字符串、数值、布尔值或对象...
Javascript小技巧一箩筐 事件源对象 event.srcElement.tagName event.srcElement.type 捕获释放 event.srcElement.setCapture(); event.srcElement.releaseCapture(); 事件按键 event.keyCode ...
-修正extjs最新版本(v3.2.2)中的一个bug,如果下拉列表中存在两个相同的Text,则SelectedValue返回值永远是第一个Text的值(feedback:ben.zhou)。 -应用补丁#6593, #6621(feedback:vbelyaev)。 +修正IE7下Grid分页...
if (this.globals.options.backButtonEnabled) this.backbuttonclick_()}; DragZoomControl.prototype.initButton_ = function(buttonContainerDiv) { var G = this.globals; var buttonDiv = ...
ExtAspNet v2.2.1 ExtAspNet是一组专业的Asp.net控件库,拥有原生的AJAX支持和丰富的UI效果, 目标是创建没有JavaScript,没有... -在Page_Load中设置了哪些需要在AJAX中更新的Asp.net控件会在回发时保持状态,可以...
Chrome Frame 会把最新版的Chrome Webkit 内核和JavaScript 引擎注入到IE中, IE浏览器将获得Chrome的性能和功能 目录 摘要 I ABSTRACT II 专业名词清单 III 第一章 绪论 1 1.1 研究背景与意义 1 1.2国内外相关...
使用import指令导入库到模板中,Freemarker会为导入的库创建新的名字空间,并可以通过import指令中指定的散列变量访问库中的变量: ${my.mail} ${mail} 输出结果: <p>Copyright (C) 1999-2002 ...