|
我有注册事件处理程序的构造函数:! x; s" [" c/ q. w
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);
3 F; c; T$ c/ A$ `/ p Run code snippet& w+ P! R. Y' w
Expand snippet
+ B k8 c0 f/ k0 U. b但是,我无法访问data回调中创建的对象的属性。this它不是指创建的对象,而是指另一个对象。) V0 \" e% m6 v, Y2 b3 `6 O, e
我还试图使用对象而不是匿名函数:
% E' r$ w" T; [8 {% T: }function MyConstructor(data,transport) this.data = data; transport.on('data',this.alert);}MyConstructor.prototype.alert = function() alert(this.name);};
1 ]0 n& s! b& U( r! p 但它也表现出同样的问题。
: a2 {3 Q; f+ X; j如何访问正确的对象?
o: c+ h* A+ i
6 {7 p1 ]1 K4 i( h7 z 解决方案:
1 q( O& ]" G- o' {# Z/ y: X: P ,你应该知道的thisthis(又称上下文)是每个功能中的特殊关键字,其值仅取决于如何调用函数,而不是如何/何时/何时被定义。它不像其他变量那样受词法作用域的影响(箭头函数除外,见下文)。这里有一些例子:
- _- ^ \. V+ w( y) J R: Ffunction 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`
+ E6 v+ p; _* |$ Y 了解更多信息this,请查看MDN 文档。
( f" O9 u$ c3 ^5 {9 ]如何正确引用 this使用箭头函数ECMAScript 6 引入了箭头函数,可视为 lambda 函数。他们没有自己的this绑定。this在范围内搜索就像普通变量一样。这意味着你不必调用它.bind. 这不是他们唯一的特殊行为。请参考 MDN 文档获取更多信息。* h# ]7 k. X. r: X: O5 t
function MyConstructor(data,transport) this.data = data; transport.on('data',() => alert(this.data));}1 X2 W) w- `$ B( F
不要使用 this你实际上不想this特别访问,而是它所指的对象。这就是为什么一个简单的解决方案只是创建一个引用对象的新变量。变量可以有任何名称,但常见的是self和that。1 V1 y1 o) F3 g
: U; x3 @# G2 |- function MyConstructor(data,transport) this.data = data; var self = this; transport.on('data',function() alert(self.data);}code]由于self它是一个普通的变量,遵守词法作用域规则,可以在回调中访问。这还有另一个好处,你可以访问它this回调本身的价值。7 b" L/ p9 O7 m( {; U% I" T
- 显式设置this回调 - 第一 部分你似乎无法控制 值,this因为它的值是自动设置的,但事实并非如此。
. U) i+ q" l$ V- p5 S+ r H - 每个函数都有方法[.bind *docs]*,它返回一个this绑定到一个值的新函数。该函数与您调用的函数完全相同.bind,只是this由您设置。无论何时调用此函数,this将始终引用传输值。2 N2 e# U, \9 L/ j% N+ h2 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。* ]: u$ O* }) ?- ^5 i
- 注意:当是 jQuery 绑定上下文时,请改用[jQuery.proxy *docs]*。这样做的原因是,在取消绑定事件回调时,您不需要存储引用函数。jQuery 内部处理。
1 w0 n, `+ y* M: Z+ n - 设置this回调-第2部分一些接受回调的函数/方法也接受回调this应引用的值。这与您自己的绑定基本相同,但函数/方法将为您完成。[Array#map *docs]*这是一种方法。它的签名是:[code]array.map(callback[,thisArg]) m. g4 ?5 P% \; l$ t' @; S9 y1 ~
第一个参数是回调,第二个参数是回调this值应引用。这是一个人为的例子:
0 h% ]. ~ G& U6 C* d& |
R5 v% [3 j' u4 F6 _9 Z( p- 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:
% u" O2 c! P5 x5 q- W) D - 对象将成为所有和 Ajax 相关回调的上下文。9 x6 r* Q5 m! `
- 常见问题:使用对象方法作为回调/事件处理程序这个问题的另一个常见表现是将对象方法用作回调/事件处理程序。函数是 JavaScript 中的一流公民,术语方法只是作为对象属性值函数的流行术语。但该函数并没有指向其包含对象的特定链接。! w3 }2 X0 I% O) F: b
- 考虑以下示例:[code]function Foo() this.data = 42, document.body.onclick = this.method;}Foo.prototype.method = function() console.log(this.data);};1 s/ W! L! b8 {; i7 W- Y1 i! Z
该函数this.method被分配到单击事件处理程序,但如果document.body单击 ,记录的值将为undefined,因为在事件处理程序内,this指的是document.body,而不是 的例子Foo。- s5 I2 {5 o" _, B5 n0 S Y* v
正如开头提到的,this什么取决于函数是如何工作的调用的,而不是它的样子定义的。
/ u) c% W$ ]2 [ M: i( G# z2 H如下码如下所示,则该函数没有对象的隐藏引用可能更为明显:
3 v' m- c: |& J9 t& C& bfunction method() console.log(this.data);}function Foo() this.data = 42, document.body.onclick = this.method;}Foo.prototype.method = method;
2 F2 J5 x6 ~6 s3 A% b# P3 ` 解决该方法与上述方法相同:如果可用,则使用.bind显式绑定this到特定值6 }7 p; e; A6 m; l& e S
document.body.onclick = this.method.bind(this);, V+ Y7 W. E" z9 q6 e
或使用匿名函数作为回调/事件处理程序,对象 ( )this)分配给另一个变量,显式调用函数作为对象的方法:
, L* {6 `) l" c9 v8 v0 s" V4 Y V' R! e- ~! U
- var self = this;document.body.onclick = function() { self.method();code]或使用箭头函数:[code]document.body.onclick = () => this.method();
* E, m1 a H8 q- N) } |
|