|
我有注册事件处理程序的构造函数:/ o4 k% d+ k2 C( b7 o
function MyConstructor(data,transport) this.data = data; transport.on('data',function () alert(this.data); }Mock transport objectvar transport = on: function(event,callback) setTimeout(callback, }called asvar obj = new MyConstructor('foo',transport);
& A- B! J) i6 \+ n0 {$ s5 H Run code snippet
+ V3 v0 z2 t9 kExpand snippet; }9 V4 _5 S+ D
但是,我无法访问data回调中创建的对象的属性。this它不是指创建的对象,而是指另一个对象。( \3 R" C! y- z; C4 T
我还试图使用对象而不是匿名函数:
& Z% e) a8 z4 J7 n5 Lfunction MyConstructor(data,transport) this.data = data; transport.on('data',this.alert);}MyConstructor.prototype.alert = function() alert(this.name);};, j) w: H7 R6 v" T
但它也表现出同样的问题。; |) C- r# F8 c- j& [! |/ X' Z
如何访问正确的对象?
9 C0 _4 f4 N8 m d& ~ ) |) [8 D" \4 P I7 `& u! b2 N& g
解决方案: : {& G: g. L2 F0 B' p1 X0 i
,你应该知道的thisthis(又称上下文)是每个功能中的特殊关键字,其值仅取决于如何调用函数,而不是如何/何时/何时被定义。它不像其他变量那样受词法作用域的影响(箭头函数除外,见下文)。这里有一些例子:+ i' s8 H0 P7 z* W7 T, i+ ?7 _
function foo() console.log(this);}// normal function callfoo(); // `this` will refer to `window`// as object methodvar obj = {bar: foo};obj.bar(); // `this` will refer to `obj`// as constructor functionnew foo(); // `this` will refer to an object that inherits from `foo.prototype`8 v5 Y$ s- R! J
了解更多信息this,请查看MDN 文档。
% c3 q0 y' b; V5 J如何正确引用 this使用箭头函数ECMAScript 6 引入了箭头函数,可视为 lambda 函数。他们没有自己的this绑定。this在范围内搜索就像普通变量一样。这意味着你不必调用它.bind. 这不是他们唯一的特殊行为。请参考 MDN 文档获取更多信息。
0 K+ l2 b' F y0 w5 G5 ufunction MyConstructor(data,transport) this.data = data; transport.on('data',() => alert(this.data));}. P' ]. Q5 `$ }% q. L
不要使用 this你实际上不想this特别访问,而是它所指的对象。这就是为什么一个简单的解决方案只是创建一个引用对象的新变量。变量可以有任何名称,但常见的是self和that。
5 g5 J8 g k F! D. u* P. H1 n/ |8 o' G( B6 Y9 V
- function MyConstructor(data,transport) this.data = data; var self = this; transport.on('data',function() alert(self.data);}code]由于self它是一个普通的变量,遵守词法作用域规则,可以在回调中访问。这还有另一个好处,你可以访问它this回调本身的价值。! F9 U; S' w5 a/ F1 c/ [# R
- 显式设置this回调 - 第一 部分你似乎无法控制 值,this因为它的值是自动设置的,但事实并非如此。5 \# ]; |" M4 R' J$ g8 P4 j
- 每个函数都有方法[.bind *docs]*,它返回一个this绑定到一个值的新函数。该函数与您调用的函数完全相同.bind,只是this由您设置。无论何时调用此函数,this将始终引用传输值。
7 _1 W- o+ G) }, I8 i - [code]function MyConstructor(data,transport) this.data = data; var boundFunction = (function() { / parenthesis are not necessary alert(this.data); but might improve readability .bind(this); // 在这种情况下,我们绑定了回调this到MyConstructor‘s的值this。
" Z% u; p7 ^5 ]! Y' @/ @ - 注意:当是 jQuery 绑定上下文时,请改用[jQuery.proxy *docs]*。这样做的原因是,在取消绑定事件回调时,您不需要存储引用函数。jQuery 内部处理。
) r" C% G; t8 K& M: R - 设置this回调-第2部分一些接受回调的函数/方法也接受回调this应引用的值。这与您自己的绑定基本相同,但函数/方法将为您完成。[Array#map *docs]*这是一种方法。它的签名是:[code]array.map(callback[,thisArg])
+ T" U4 z9 u- W t 第一个参数是回调,第二个参数是回调this值应引用。这是一个人为的例子:' A' E: G, A8 N1 a0 \
' h0 V5 U9 l, x" c1 B- var arr = [1,2,3];var obj = {multiplier: 42};var new_arr = arr.map(function(v) return v * this.multiplier;},obj); // 注意:this函数/方法的文档通常提到您是否可以传递值。[jQuery 的$.ajax方法*docs]*描述了一个叫 的选项context:+ ^% \ }+ @9 {9 P' d
- 对象将成为所有和 Ajax 相关回调的上下文。( k. P6 Z, N% ^6 l) G
- 常见问题:使用对象方法作为回调/事件处理程序这个问题的另一个常见表现是将对象方法用作回调/事件处理程序。函数是 JavaScript 中的一流公民,术语方法只是作为对象属性值函数的流行术语。但该函数并没有指向其包含对象的特定链接。
h5 V( W! W. V" h' d: e- E1 I - 考虑以下示例:[code]function Foo() this.data = 42, document.body.onclick = this.method;}Foo.prototype.method = function() console.log(this.data);};$ L) P5 e8 j; U) \1 s" K, N; E
该函数this.method被分配到单击事件处理程序,但如果document.body单击 ,记录的值将为undefined,因为在事件处理程序内,this指的是document.body,而不是 的例子Foo。8 c. T1 H; g0 S/ T3 {' \* ?
正如开头提到的,this什么取决于函数是如何工作的调用的,而不是它的样子定义的。- j. L7 g# `+ u
如下码如下所示,则该函数没有对象的隐藏引用可能更为明显:
r$ `& v$ l- \( ?function method() console.log(this.data);}function Foo() this.data = 42, document.body.onclick = this.method;}Foo.prototype.method = method;
) f7 o7 Y4 w$ f$ E7 y1 P: v0 M' _0 d 解决该方法与上述方法相同:如果可用,则使用.bind显式绑定this到特定值
. ]& E5 E7 O7 K+ R0 Hdocument.body.onclick = this.method.bind(this);2 g2 o. Z$ A+ g' I; W
或使用匿名函数作为回调/事件处理程序,对象 ( )this)分配给另一个变量,显式调用函数作为对象的方法:
9 r4 m o3 r8 j: K% k" @$ j, M0 E$ E r5 h
- var self = this;document.body.onclick = function() { self.method();code]或使用箭头函数:[code]document.body.onclick = () => this.method();, X2 V C0 I$ c& s) }4 i
|
|