首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >ES6:对象之间的回调函数

ES6:对象之间的回调函数
EN

Stack Overflow用户
提问于 2016-07-07 21:03:49
回答 1查看 3.6K关注 0票数 2

我正在构建一个页面,它将有不同的用户将通过的“部分”,每个部分都有自己的逻辑。例如:

  1. 装载部分:装载闪光灯。完成后,继续:
  2. Splash部分:介绍一些需要交互才能继续的UI元素。(让我们简单地说,有一个“幻灯片可以解锁”)
  3. 视频首映式:使用Youtube的定制播放器嵌入Javascript。

由于其中一些部分有很多特定的逻辑,所以我将它们分离成组件。几乎所有这些逻辑都是组件内部的,但偶尔,我想从组件B调用组件A中的函数。参见main.jsSplash.js的后一行。

main.js

代码语言:javascript
运行
复制
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:

代码语言:javascript
运行
复制
const Loading = {
    init() {
        // watches for events, controls loading UI, etc.
    },
    // ... other functions for this section
}
export default Loading;

Splash.js

代码语言:javascript
运行
复制
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

代码语言:javascript
运行
复制
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()是可行的,但我觉得我仍然没有抓住重点。

出什么事啦?

(可能是相关的子问题:将这些组件定义为类,即使它们只使用一次,我会从中受益吗?)

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-07-07 21:42:47

因此,要回答您的问题,为什么没有定义视频,是因为您试图访问已经更改了上下文的this

代码语言:javascript
运行
复制
Premiere.playVideo.bind(Premiere)

绑定将确保在调用playVideo时,它是在premiere的上下文中调用的,而不是在splash的上下文中调用的。这意味着premiere的上下文具有this.video

我用来验证的代码:

代码语言:javascript
运行
复制
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();

然而,这种特殊的“架构”对我来说有点难闻。你可以使用责任链模式。这是当前对象在完成工作后知道下一步调用什么的地方。

代码语言:javascript
运行
复制
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() ,链就会在那里结束。,我有它,因为它更容易看到链停止的位置。

从最后四行可以看出,责任链很容易看出:

代码语言:javascript
运行
复制
var finish = new FinishComponentHandler();
var premiere = new PremiereComponentHandler(finish);
var splash = new SplashComponentHandler(premiere);
var loading = new LoadingComponentHandler(splash);

加载组件将在doWork对象上调用splash对象,然后调用premiere对象上的doWork,以此类推。

这种模式依赖于DoWork的继承性,它是处理程序的接口类型。

这可能不是最好的实现,但您可以看到如何不必担心最后调用的东西,或者如何特别调用下一个。您只需将希望进入构造函数的对象传入,并确保在操作结束时进行调用。

我注意到你

代码语言:javascript
运行
复制
// watches for events, controls unlocking UX, etc.

doWork()函数可以执行绑定,将它们委托给处理绑定的适当组件。因此,像SplashComponentHandler一样,可以委托给SplashComponent。保持这些关注点的分离是很好的做法。

这是如何解决你的问题

代码语言:javascript
运行
复制
Splash.onUnlock = Premiere.playVideo.bind(Premiere);

首先,Splash.onUnlock没有实现,除非您给它一个实现。其次,您必须将一个上下文绑定到您的函数,因为它是在不同的上下文下执行的,这听起来不太好。

所以你可以在SplashComponentHandler.doWork()里想象

代码语言:javascript
运行
复制
doWork() {
    var component = new SplashComponent();

    component.initialise(); // when this is finished we want to execute the premiere
 
    this.nextWorkItem.doWork();
}

PremiereComponentHandler.doWork()

代码语言:javascript
运行
复制
doWork() {
    var component = new PremiereComponent();

    component.bind(); // makes sure there is a this.video.

    component.playVideo();
}

现在看到SplashComponentHandler现在不知道下一个处理程序,它只知道当它完成它的工作时,它需要调用下一个处理程序。

不存在this绑定,因为doWork()是在PremiereComponentHandler上下文中执行的,或者曾经传递给SplashComponentHandler的处理程序。

此外,

从技术上讲,您并不局限于一个接一个地执行处理程序。您可以创建一个执行许多其他处理程序的处理程序。每个被执行的处理程序都知道下一个处理程序,直到停止调用。

另一个问题是,首映式完成后,又怎会有其他事情发生呢?简单,所以从前面的解耦场景来看,这是SplashComponentHandler.doWork()

代码语言:javascript
运行
复制
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(),然后初始化步骤如下所示:

代码语言:javascript
运行
复制
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()的承诺。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/38255231

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档