this这个概念在很多流行语言里都有,大家的定义都很一致,也就是运行时的实例本身,但是在JavaScript这里就不一样了。this是函数里的当前引用(全部代码你可以看成是在宿主对象window下的一个大匿名函数里,就像require的实现那样),有一种说法是JavaScript里的this是调用者对象,但这个并不贴切。比如下面的代码:
var
o
=
{
}
;
o
.
f
=
function
(
)
{
var
ff
=
function
(
)
{
console
.
log
(
this
)
;
}
;
ff
(
)
;
console
.
log
(
this
)
;
}
;
o
.
f
(
)
;
// window 对象 & o 对象
首先, 在JavaScript的运行时里,一切存在单位皆是对象(实例)。这些对象不是孤立存在的,它们之间有两种类型的关系。一类是对象能力的回溯关系(原型链);一类是对象引用的组织关系(对象属性),也就是摆放在哪儿。而this指向的正是其摆放位置对象。比如上例中的f在对象o里,里面的this就是o。o和ff没有明确指定摆放位置,在标准模式下它们会被摆放在宿主对象window里,this自然就是window了。那如果我们执行如下代码会怎么样呢:
var
of
=
o
.
f
;
of
(
)
;
// window & window
of和o.f是同一个函数的两个引用,这里可以看出this即是引用位置对象。
继续代码:
o
.
oo
=
{
g
:
function
(
)
{
console
.
log
(
this
)
;
}
}
;
o
.
oo
.
g
(
)
;
// o.oo 对象
不用解释了吧。继续代码:
var
f1
=
function
(
)
{
console
.
log
(
this
)
;
}
;
var
o1
=
new
f1
(
)
;
// f1 对象
这个怎么理解呢?我们把引擎的动作用如下的伪代码描述:
// 伪代码
var
f1
=
function
(
)
{
console
.
log
(
this
)
;
}
;
var
o1
=
{
}
;
o1
.
__proto__
=
{
constructor
:
f1
}
;
o1
.
constructor
(
)
;
不用解释了吧。继续代码:
o
.
f2
=
function
(
)
{
var
ff
=
(
)
=>
{
console
.
log
(
this
)
;
}
;
ff
(
)
;
console
.
log
(
this
)
;
}
;
o
.
f2
(
)
;
// 两个 都是 对象 o
Why? 这个o.f2和之前的o.f的区别就是把函数的书写形式变了一下,箭头函数里输出的为什么不是window呢?“箭头函数”的学名叫lambda表达式(lambda expression),它像函数一样拥有一个独立的作用域,但是并不具备函数的各种特性。所以箭头函数里的this也就等同于此“函数”所在位置的this。OK,继续code:
o
.
oo
.
g1
=
function
(
)
{
setTimeout
(
this
.
g
,
1000
)
;
// call o.oo.g()
}
;
o
.
oo
.
g1
(
)
;
// window
这里不会像直接执行o.oo.g()那样打印o.oo对象了,因为setTimeout向宿主环境(window)注册了一个执行体函数,window会把这个执行体保存在自己的一个匿名属性里,将来由宿主对象执行时打印的就是window。但是下面这个定时器又不一样了:
o
.
oo
.
g2
=
function
(
)
{
console
.
log
(
this
)
;
setTimeout
((
)
=>
{
console
.
log
(
this
)
;
}
,
1000
)
;
}
;
o
.
oo
.
g2
(
)
;
// 两个o.oo 对象
这个定时器执行的时候为什么
不是打印window呢?前书说过,lambda表达式不具备函数特性,也不存在自己的this,这里这个this其实是因为闭包特性顺带过来的。如有疑问可以换一个普通变量看看即知。
诸如call、apply、bind这些都是人工指定this,当然您指啥就是啥。如果指定对象是undefine或者null之类,则仍然遵守前述规则指向宿主对象window(正常模式)。
总结:
一、以上论述纯属推断,并未参考源代码的实际实现。因此如有雷同,纯属巧合
。我们从正向和反向看待同一个事物的时候,有时能看到不同的东西。
二、以上推断出于技术研究的动机,若出于实用目的,我认为可以用更加常规的代码组织方式,并不必要把这些东西剥得这么清楚。就如同我们想写一封信,并不需要会写四种“回”字。
诸如call、apply、bind这些都是人工指定this,当然您指啥就是啥。如果指定对象是undefine或者null之类,则仍然遵守前述规则指向宿主对象window(正常模式)。
总结:
一、以上论述纯属推断,并未参考源代码的实际实现。因此如有雷同,纯属巧合

二、以上推断出于技术研究的动机,若出于实用目的,我认为可以用更加常规的代码组织方式,并不必要把这些东西剥得这么清楚。就如同我们想写一封信,并不需要会写四种“回”字。