我正在构建一个页面,它将有不同的用户将通过的“部分”,每个部分都有自己的逻辑。例如:
由于其中一些部分有很多特定的逻辑,所以我将它们分离成组件。几乎所有这些逻辑都是组件内部的,但偶尔,我想从组件B调用组件A中的函数。参见main.js和Splash.js的后一行。
main.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;
});
Loading.js:
const Loading = {
init() {
// watches for events, controls loading UI, etc.
},
// ... other functions for this section
}
export default Loading;
Splash.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;
Premiere.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()
是可行的,但我觉得我仍然没有抓住重点。
出什么事啦?
(可能是相关的子问题:将这些组件定义为类,即使它们只使用一次,我会从中受益吗?)
发布于 2016-07-07 21:42:47
因此,要回答您的问题,为什么没有定义视频,是因为您试图访问已经更改了上下文的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
是Null对象模式的一部分,它的实现执行noop (没有操作)。这实际上结束了责任范围。当然,您不需要一个FinishComponent
**,,您可以不调用** this.nextWorkItem.doWork()
,链就会在那里结束。,我有它,因为它更容易看到链停止的位置。
从最后四行可以看出,责任链很容易看出:
var finish = new FinishComponentHandler();
var premiere = new PremiereComponentHandler(finish);
var splash = new SplashComponentHandler(premiere);
var loading = new LoadingComponentHandler(splash);
加载组件将在doWork
对象上调用splash
对象,然后调用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
的处理程序。
此外,
从技术上讲,您并不局限于一个接一个地执行处理程序。您可以创建一个执行许多其他处理程序的处理程序。每个被执行的处理程序都知道下一个处理程序,直到停止调用。
另一个问题是,首映式完成后,又怎会有其他事情发生呢?简单,所以从前面的解耦场景来看,这是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
的最后一个注意事项上,您可以使用承诺实现整个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()
的承诺。
https://stackoverflow.com/questions/38255231
复制相似问题