ES6:对象之间的回调函数
ES6: callback functions between objects
我正在构建一个页面,该页面将包含用户将要经过的各种"部分",每个部分都有自己的逻辑。例如:
- 加载
- 部分:加载闪光灯。完成后,继续:
- 启动部分:介绍一些需要与之交互才能继续的 UI 元素。(假设有一个"解锁幻灯片")
- 视频首映:使用 Youtube 嵌入式 Javascript API 的自定义播放器。
由于其中一些部分有很多特定的逻辑,因此我将它们分成组件。几乎所有这些逻辑都是组件内部的,但偶尔,我想从组件 B 调用组件 A 中的函数。请参阅main.js和Splash的后几行.js
主.js
import $ from 'jquery';
import Loading from './components/Loading.js';
import Splash from './components/Splash.js';
import Premiere from './components/Premiere.js';
$(() => {
Loading.init();
Splash.init();
Premiere.init();
Splash.onUnlock = Premiere.playVideo;
});
加载.js:
const Loading = {
init() {
// watches for events, controls loading UI, etc.
},
// ... other functions for this section
}
export default Loading;
飞溅.js
const Splash = {
init() {
// watches for events, controls unlocking UX, etc.
},
// ... other functions for this section
unlock() {
// called when UX is completed
this.onUnlock();
}
}
export default Splash;
首映.js
const Premiere = {
init() {
this.video = $('#premiereVideo');
// watches for events, binds API & player controls, etc.
},
// ... other functions for this section
playVideo() {
this.video.play()
},
}
export default Premiere;
现在,我希望当this.onUnlock()
在Splash
内被调用时,Premiere.playVideo()
会被触发。但是,我得到一个错误:video
没有定义 - 因为它正在寻找Splash.video
,而不是Premiere.video
.
据我了解,将对象或其属性分配给变量会创建对该属性的引用,而不是重复的实例。看来我没有正确理解这一点。
将this.video.play()
更改为Premiere.video.play()
有效,但我觉得我仍然没有抓住重点。
怎么了?
(可能相关的子问题:将这些组件定义为类是否会受益,即使它们只会使用一次?
要回答您的问题,为什么没有定义视频是因为您正在尝试访问已更改上下文this
。
Premiere.playVideo.bind(Premiere)
绑定将确保在调用playVideo
时,它是在premiere
上下文中调用的,而不是在splash
的上下文中调用的。这意味着premiere
的上下文this.video
。
我用来验证的代码:
const Splash = {
init() {
// watches for events, controls unlocking UX, etc.
},
// ... other functions for this section
unlock() {
// called when UX is completed
this.onUnlock();
}
}
const Premiere = {
init() {
this.video = {
play() {
console.log("playing");
}
};
// watches for events, binds API & player controls, etc.
},
// ... other functions for this section
playVideo() {
console.log(this);
this.video.play()
},
}
Premiere.init();
Splash.onUnlock = Premiere.playVideo.bind(Premiere);
console.log(Splash);
Splash.unlock();
然而,这种特殊的"架构"对我来说有点臭。您可以使用责任链模式。这是当前对象在完成工作后知道下一步要调用什么的地方。
class DoWork {
constructor(nextWorkItem) {
this.nextWorkItem = nextWorkItem;
}
doWork() {
}
}
class LoadingComponentHandler extends DoWork {
constructor(nextWorkItem) {
super(nextWorkItem);
}
doWork() {
// do what you need here
console.log("Doing loading work")
// at the end call
this.nextWorkItem.doWork();
}
}
class SplashComponentHandler extends DoWork {
constructor(nextWorkItem) {
super(nextWorkItem);
}
doWork() {
// do what you need here
console.log("Doing Splash Work")
// at the end call
this.nextWorkItem.doWork();
}
}
class PremiereComponentHandler extends DoWork {
constructor(nextWorkItem) {
super(nextWorkItem);
}
doWork() {
// do what you need here
console.log("Doing Premiere Work")
// at the end call
this.nextWorkItem.doWork();
}
}
class FinishComponentHandler extends DoWork {
constructor() {
super(null);
}
doWork() {
console.log("End of the line now");
}
}
var finish = new FinishComponentHandler();
var premiere = new PremiereComponentHandler(finish);
var splash = new SplashComponentHandler(premiere);
var loading = new LoadingComponentHandler(splash);
loading.doWork();
FinishComponent
是空对象模式的一部分,其实现执行 noop(无操作)。这有效地结束了责任线。当然你不需要FinishComponent
,你可以不打电话给this.nextWorkItem.doWork()
,链条就会到此结束。我把它放在那里,因为它更容易看到链条停止的地方。
从最后四行可以看出,责任链很容易看出:
var finish = new FinishComponentHandler();
var premiere = new PremiereComponentHandler(finish);
var splash = new SplashComponentHandler(premiere);
var loading = new LoadingComponentHandler(splash);
加载组件将在splash
对象上调用doWork
,而对象又会在premiere
对象上调用doWork
,依此类推,第四。
此模式依赖于 DoWork
的继承, 是处理程序的接口类型。
这可能不是最好的实现,但您可以看到如何不必担心调用的最后一个东西,或者如何专门调用下一个。您只需将您希望接下来进入的对象传入构造函数,并确保在操作结束时调用。
我注意到你有
// watches for events, controls unlocking UX, etc.
doWork()
函数可以执行绑定,将它们委托给处理此问题的适当组件。所以像SplashComponentHandler
可以委托给SplashComponent
.保持这些关注点的分离是一种很好的做法。
这如何解决您的问题
Splash.onUnlock = Premiere.playVideo.bind(Premiere);
首先,Splash.onUnlock
没有实现,直到你给它一个。其次,你必须将上下文绑定到你的函数,因为它在不同的上下文下执行,这听起来并不好。
所以你可以想象SplashComponentHandler.doWork()
:
doWork() {
var component = new SplashComponent();
component.initialise(); // when this is finished we want to execute the premiere
this.nextWorkItem.doWork();
}
在PremiereComponentHandler.doWork()
:
doWork() {
var component = new PremiereComponent();
component.bind(); // makes sure there is a this.video.
component.playVideo();
}
现在看到SplashComponentHandler
现在不知道下一个处理程序,它只知道当它完成工作时,它需要调用下一个处理程序。
没有this
绑定,因为doWork()
已在PremiereComponentHandler
或传递给SplashComponentHandler
的任何处理程序的上下文中执行。
此外
从技术上讲,您不仅限于执行一个又一个处理程序。您可以创建一个执行许多其他处理程序的处理程序。每个执行的处理程序都将知道下一个处理程序,直到您停止调用。
另一个问题是,如果首映完成后会发生什么,之后Splash如何做其他事情?很简单,所以从前面的解耦场景来看,这是SplashComponentHandler.doWork()
:
doWork() {
var component = new SplashComponent();
component.initialise(); // when this is finished we want to execute the premiere
this.nextWorkItem.doWork();
// so when we get to this execution step
// the next work item (PremiereComponentHandler)
// has finished executing. So now you can do something after that.
component.destroy(); // cleanup after the component
fetch('api/profile') // i don't know, what ever you want.
.then(profileContent => {
component.splashProfile = profileContent.toJson();;
});
}
在使用Promise
的最后一点,您可以使用 promise 使整个doWork()
异步。只需返回this.nextWorkItem.doWork()
,然后初始化步骤如下所示:
var finish = new FinishComponentHandler();
var premiere = new PremiereComponentHandler(finish);
var splash = new SplashComponentHandler(premiere);
var loading = new LoadingComponentHandler(splash);
loading
.doWork()
.then(() => {
// here we have finished do work asynchronously.
})
.catch(() => {
// there was an error executing a handler.
});
让它全部使用Promises
诀窍是确保您始终返回doWork()
的承诺。
- JavaScript回调函数
- 如何在回调函数中执行流
- 回调函数中传递参数的困难(Google Map API Markers)
- 如何正确地将参数传递给RequireJS回调函数
- 回调函数在python代码中离线
- 从AJAX回调函数中分离数据
- 赋值后的回调函数
- 如何在javascript回调函数中返回多个变量
- 函数表达式,返回回调函数
- 等待回调函数执行
- JavaScript回调函数和Google Feed API
- 如何从ajax成功回调函数中读取javascript变量
- 正在保存JavaScript内部回调函数中的值
- 有没有什么方法可以停止Jquery中的animate(也可以停止完整的回调函数)
- 如何在javascript中添加带有回调函数的按钮点击事件作为window.conf
- 在Golang回调函数中启用CORS
- 在初始函数完成之前调用回调函数
- jQuery Mobile:如何在$.Mobile.changePage之前运行回调函数
- 即使使用回调函数也无法返回值
- 为什么prototypjs观察到回调函数有绑定