this
说到this,须要明白三方面内容:
- this什么时候被赋值
- this被赋了什么值
- 内置函数如何运用this的
this什么时候被赋值
进入函数代码
当掌握流依据一个函数对象 F、挪用者供应的 thisArg 以及挪用者供应的 argumentList,进入函数代码的实行环境时,实行以下步骤:
- 假如函数代码是严厉形式下的代码,设 this 绑定 为 thisArg。
- 不然假如 thisArg 是 null 或 undefined ,则设 this 绑定为全局对象。
- ……
以上信息泉源:
进入函数代码
从上诉信息中能够晓得:
- this 与挪用者供应的 thisArg 密切相关
- this 在严厉形式下为 thisArg
- this 在非严厉形式下为 thisArg 或 全局对象
那末 thisArg 又是如何来的呢?下面来看下函数挪用历程:
函数挪用
- 令 ref 为诠释实行 MemberExpression 的结果。
- 令 func 为 GetValue(ref)。
- 令 argList 为诠释实行 Arguments 的结果,发生参数值们的内部列表(拜见 11.2.4)。
- 假如 Type(func) 不是 Object,抛出一个 TypeError 非常。
- 假如 IsCallable(func) 为 false,抛出一个 TypeError 非常。
假如 Type(ref) 为 Reference,那末
假如 IsPropertyReference(ref) 为 true,
- 那末令 thisValue 为 GetBase(ref)。
不然,ref 的基值是一个环境纪录项。
- 令 thisValue 为挪用 GetBase(ref) 的 ImplicitThisValue 具体要领的结果。
不然,Type(ref) 不是 Reference。
- 令 thisValue 为 undefined。
- 返回挪用 func 的 [[Call]] 内置要领的结果,传入 thisValue 作为 this 值和列表 argList 作为参数列表。
以上信息泉源:
函数挪用
从上诉信息中能够晓得:
- thisArg 即 thisValue
- thisValue 与 ref 的范例密切相关
假如 ref 的范例是 Reference(援用范例范例)
- 假如 ref 是属性援用,经由过程 GetBase(ref)(返回援用值ref的基值部份) 猎取 thisValue
- 不然,经由过程 ImplicitThisValue 要领猎取 thisValue
- 不然,thisValue 为 undefined
那末,相识到这里可能有很多新的疑问,比方:
- Reference 是如何的范例
- ref 是如何来的
- ref 什么时候是 Reference,什么时候不是。
- GetBase(ref) 和 ImplicitThisValue 是如何发生结果的
Reference 是如何的范例
起首先诠释下 Reference。
实在ES中的范例分为ECMAScript言语范例和范例范例
ECMAScript言语范例对应的是程序员运用 ECMAScript 言语直接操纵的值,如 Undefined、Null、Boolean、String、Number、Object等。
范例范例可用来形貌 ECMAScript 表达式运算的中心结果,但如许的值不能储存为对象的属性或 ECMAScript 言语的变量值。援用尤雨溪的诠释:
这里的 Reference 是一个 Specification Type,也就是 “只存在于范例里的笼统范例”。它们是为了更好地形貌言语的底层行动逻辑才存在的,但并不存在于现实的 js 代码中。
ref 是如何来的
从上诉函数挪用中能够晓得,ref 是诠释实行 MemberExpression 的结果。
下面细致看下 MemberExpression 的剖析历程:
发生式 CallExpression : MemberExpression Arguments 根据下面的历程实行 :
- 令 baseReference 为诠释实行 MemberExpression 的结果。
- 令 baseValue 为 GetValue(baseReference)。
- 令 propertyNameReference 为诠释实行 Expression 的结果。
- 令 propertyNameValue 为 GetValue(propertyNameReference)。
- 挪用 CheckObjectCoercible(baseValue)。
- 令 propertyNameString 为 ToString(propertyNameValue)。
- 假如正在实行中的语法发生式包括在严厉形式代码当中,令 strict 为 true,不然令 strict 为 false。
- 返回一个值范例的援用,其基值为 baseValue 且其援用名为 propertyNameString,严厉形式标记为 strict。
从上诉信息中剖析能够晓得:
- 诠释实行 MemberExpression 的结果是一个援用范例范例(Reference)
这个援用范例范例包括三部份信息:
- baseValue
- propertyNameString
- strict
- thisValue 的值取决于 baseValue
- baseValue 是挪用 GetValue 取得的。
- GetValue 获得的基值是 undefined、Object、Boolean、String、Number、环境纪录项中的恣意一个(详见:援用范例范例),而不是援用范例范例。
GetValue 细致历程见:
GetValue
ref 什么时候是 Reference,什么时候不是 Reference
一般来说,ref 是MemberExpression剖析的结果,都将是 Reference。
然则,假如 MemberExpression 是其函数表达式的一部份,则可能将转变终究剖析结果的范例。
而转变剖析结果范例的重要原因取决于是不是挪用了 GetValue 要领,假如挪用了 GetValue ,函数中心值 ref 将是 Object 范例。
那末哪些表达式不运用 GetValue 呢?
- 标识符援用 : 标识符实行的结果老是一个 Reference 范例的值。
- 群组表达式:本算法不在实行 Expression 后运用 GetValue。这重要的目标是让 delete 与 typeof 运算符能够作用在被括号括起来的表达式。
- 成员表达式
- 挪用表达式
更多表达式详见:
表达式
GetBase(ref) 和 ImplicitThisValue 是如何发生结果的
- GetBase:返回援用值ref的基值部份
- ImplicitThisValue : 声明式环境纪录项永久将 undefined 作为其 ImplicitThisValue 返回。
this被赋了什么值
实在在 this什么时候被赋值 部份已引见了 this被赋了什么值。下面总结三种赋值历程:
第一种this赋值历程:
var v = 1;
var obj = {
v: 2,
fn: function(){
console.log(this.v);
}
}
obj.fn(); // 2
(obj.fn)(); // 2
- 挪用表达式剖析 obj.fn ,返回援用范例范例,baseValue 为 obj。
- 函数挪用,因为 1 历程返回的为援用范例范例,且为属性援用,挪用 GetBase 将 baseValue (obj) 作为返回值,返回给 thisValue。
- 进入函数代码,thisArg 为 obj,将其赋值给 this。
假如对步骤1中,baseValue 为 obj 有疑问,详见属性接见, 发生式 MemberExpression : MemberExpression [ Expression ] 实行历程。
相当于有两个历程:
- 剖析obj,baseValue 为声明式环境纪录项。
- 剖析obj.fn,baseValue 为 obj。
第二种this赋值历程:
function foo(){
console.log(this);
}
foo(); // Window
- 挪用表达式剖析 foo, 返回援用范例范例,baseValue 为声明式环境纪录项(函数声明时绑定)。
- 函数挪用,因为 1 历程返回的为援用范例范例,且不为属性援用,挪用 ImplicitThisValue 要领,返回 undefined 。
- 进入函数代码,thisArg 为 undefined,非严厉形式下将 this 赋值为全局对象。
第三种this赋值历程:
var v = 1;
var obj = {
v: 2,
fn: function(){
console.log(this.v);
}
}
var fn2 = obj.fn;
fn2(); // 1
(obj.fn, obj.fn)(); // 1
- 挪用表达式剖析 fn2,(obj.fn, obj.fn) ,因为赋值表达式、逗号表达式都运用了 GetValue 要领,返回函数(Object 范例)。
- 函数挪用,因为 1 历程返回的不是援用范例范例,所以 thisValue 为 undefined`。
- 进入函数代码,thisArg 为 undefined,非严厉形式下将 this 赋值为全局对象。
内置函数如何运用this的
内置函数修正 this 是经由过程给 func 的 [[Call]] 内置要领通报 thisArg 来完成的。
内置要领
- Function.prototype.apply (thisArg, argArray)
- Function.prototype.call (thisArg [ , arg1 [ , arg2, … ] ] )
- Function.prototype.bind (thisArg [, arg1 [, arg2, …] ] )
- Array.prototype.every ( callbackfn [ , thisArg ] )
- Array.prototype.some ( callbackfn [ , thisArg ] )
- Array.prototype.forEach ( callbackfn [ , thisArg ] )
- Array.prototype.map ( callbackfn [ , thisArg ] )
- Array.prototype.filter ( callbackfn [ , thisArg ] )
- new 运算符 运用内置要领 [[Construct]] 完成this指定
内置要领模仿完成
运用成员表达式模仿内置要领[[Call]]的结果:
apply
Function.prototype.apply_ = function (context, arr) {
var context = Object(context) || window;
var result;
// 暂时纪录须要挪用的function
context.fn = this;
if (!arr) {
result = context.fn();
}
else {
var args = [];
for (var i = 0, len = arr.length; i < len; i++) {
args.push('arr[' + i + ']');
}
// 运用成员表达式指定context.fn实行时this为context
result = eval('context.fn(' + args + ')')
}
delete context.fn
return result;
}
call
Function.prototype.call_ = function (context) {
var context = context || window;
context.fn = this;
var args = [];
// 猎取参数列表
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
// 运用成员表达式指定context.fn实行时this为context
var result = eval('context.fn(' + args +')');
delete context.fn
return result;
}
bind
Function.prototype.bind_ = function (context) {
// 纪录bind的函数
var self = this;
var args = [];
// 猎取绑定的参数列表
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
// 建立新函数
var fbound = function () {
// 猎取未绑定的参数列表
var bindArgs = Array.prototype.slice.call(arguments);
// fbound被当作组织函数运用,this指向实例。不然,指向 context
self.apply(this instanceof self ? this : context, args.concat(bindArgs));
}
// 保护原型关联
fbound.prototype = self.prototype || new Function().prototype ;
return fbound;
}