为什么Promise.then
在使用类方法作为回调时传递undefined
的执行上下文,在使用"正常函数时传递window
"?
类方法是否与其拥有的对象/类分离?为什么undefined
而不是window
?
function normal() {
console.log('normal function', this);
}
const arrow = () => {
console.log('arrow function', this);
}
function strictFunction() {
'use strict';
console.log('strict function', this);
}
class Foo {
test() {
this.method(); // Foo
Promise.resolve().then(() => console.log('inline arrow function', this)); // Foo
Promise.resolve().then(normal); // window
Promise.resolve().then(arrow); // window
Promise.resolve().then(strictFunction); // undefined
Promise.resolve().then(this.method); // undefined <-- why?
}
method() {
console.log('method', this);
}
}
const F = new Foo();
F.test();
(jsFiddle)
我希望this.method
的上下文丢失,但无法理解为什么this.method
和&#34;正常&#34;之间的不同行为?和箭头功能。
这种行为有规格吗?我发现的唯一参考是Promises A +引用&#34;在严格模式下this
内部将undefined
;在草率模式下,它将是global object
。&#34; 。
答案 0 :(得分:7)
你在那里的引用告诉你原因:
在严格模式下
this
将在内部未定义;在草率模式下,它将成为全局对象。
ES6 spec说:
ClassDeclaration或ClassExpression的所有部分都是严格模式代码
因此,由于严格模式,未绑定的类方法中的this
将为undefined
。
class A {
method() {
console.log(this);
}
}
const a = new A();
a.method(); // A
const unboundMethod = a.method;
unboundMethod(); // undefined
如果您使用严格模式传递普通函数,则会产生相同的行为,因为this
绑定在严格模式下默认为undefined
,而不是设置为全局对象。
normal
和arrow
this
为window
的原因是因为它们不在类中,因此不会以严格模式包装。
就promises和then
方法而言,它只会将undefined
作为this
传递,但不会覆盖已绑定的this
。
如果你看一下PromiseReactionJob规范:
具有参数reaction和argument的作业PromiseReactionJob将适当的处理程序应用于传入值,并使用处理程序的返回值来解析或拒绝与该句柄关联的派生promise。
...
let handlerResult be Call(handler, undefined, «argument»).
Call的第二个参数是this
值,设置为undefined
。
答案 1 :(得分:4)
这与Promises无关,而是调用this
的上下文。
案例1:
this.method(); // Foo
此处method
是Foo
类中定义的函数,因此this
被评估为触发函数的对象,this
在this.method
中}。因此 - 显示Foo
。
案例2:
Promise.resolve().then(() => console.log('inline arrow function', this)); // Foo
箭头函数是ES6的一个特性,其唯一属性是定义它的封闭上下文中的this
上下文。该函数在this === Foo
的上下文中调用,这就是显示的内容。
案例3:
Promise.resolve().then(normal); // window
Promise.resolve().then(arrow); // window
箭头函数将其上下文保留为窗口,因为它是一个箭头函数,并且在没有上下文的情况下评估正常函数,其中this
在不在strict mode
时被评估为窗口。
案例4:
Promise.resolve().then(strictFunction); // undefined
由于在窗口中声明的此函数体内请求strict mode
,this
被评估为未定义。
案例5:
Promise.resolve().then(this.method); // undefined <-- why?
在这个spec中,定义所有类代码都是严格的代码:
ClassDeclaration或ClassExpression的所有部分都是严格模式代码。
答案 2 :(得分:1)
在我的案例中,它帮助定义了“自我”的简单解决方案。
<强> app.component.ts 强>
export class AppComponent implements OnInit {
public cards: Card[] = [];
public events: any[] = [];
constructor(private fbService: FacebookService) {
this.fbService.loadSdk();
}
ngOnInit() {
const self = this;
this.fbService.getEvents().then((json: any) => {
for (const event of json.data)
{
self.cards.push(
new Card({
imageUrl: 'assets/ny.jpg',
id: event.id,
name: event.name
}),
);
}
});
}
}
<强> fb.service.ts 强>
import { BehaviorSubject } from 'rxjs/Rx';
import { Injectable, NgZone } from '@angular/core';
import { Http } from '@angular/http';
declare var window: any;
declare var FB: any;
@Injectable()
export class FacebookService {
events: any[];
public ready = new BehaviorSubject<boolean>(false);
constructor(private zone: NgZone) {
}
public loadSdk() {
this.loadAsync(() => { });
}
public loadAsync(callback: () => void) {
window.fbAsyncInit = () => this.zone.run(callback);
// Load the Facebook SDK asynchronously
const s = 'script';
const id = 'facebook-jssdk';
const fjs = document.getElementsByTagName(s)[0];
// tslint:disable-next-line:curly
if (document.getElementById(id)) return;
const js = document.createElement(s);
js.id = id;
js.src = 'http://connect.facebook.net/en_US/all.js';
fjs.parentNode.insertBefore(js, fjs);
}
public getEvents(): Promise<any> {
return new Promise((resolve, reject) => {
FB.init({
appId: 'app_id',
xfbml: true,
status: true,
cookie: true,
version: 'v2.10'
});
FB.api(
'/salsaparty.bg/events',
'GET',
{
access_token: 'acess_token'
},
function (response) {
resolve(response);
}
);
});
}
}
答案 3 :(得分:0)
this.method
未定义的原因是因为当你像这样使用它时,你实际上只是将没有上下文的函数作为回调。所以,当它运行时,它并不知道这一点。
如果您想维护上下文,请使用bind
函数。
Promise.resolve().then(this.method.bind(this))
绑定将上下文绑定到方法。它基本上等同于:
Promise.resolve().then(((self) => () => self.method())(this))
是将上下文映射到范围中的变量的包装器。
使用类方法,当您将其作为变量获取时,它与包含对函数的引用的变量基本上没有区别。
例如:
const a = () => {};
class Foo {
a() {}
}
const foo = new Foo();
console.log(a); // just a function
console.log(foo.a) // just a function
console.log(foo.a()) // a function called with a context of foo
当您在某个对象上调用方法时,例如foo.a()
它与执行foo.a.call(foo)
基本相同,您可以将a
的上下文设置为foo。当您只需foo.a
并将其与foo
分开时,它与执行foo.a.call(window)
(或节点中的global
)相同。
以下是一些说明差异的代码。您还可以查看如果bind
如何,它将保持上下文。
class Foo {
constructor() {
this.b = this.b.bind(this);
}
a () {
return this;
}
b () {
return this;
}
}
const foo = new Foo();
const a = foo.a;
const b = foo.b;
const bound = foo.a.bind(foo);
console.log('A', foo.a().constructor.name);
console.log('A', a());
console.log('A', a.apply(foo).constructor.name);
console.log('A', bound().constructor.name);
console.log('B', foo.b().constructor.name);
console.log('B', b().constructor.name);
&#13;