ES6:对象

时间:2016-07-07 21:03:49

标签: javascript ecmascript-6

我正在构建一个包含各种各个部分的网页。用户将通过,每个都有自己的逻辑。例如:

  1. 装载部分:装载闪光灯。完成后,继续:
  2. 启动部分:介绍一些需要与之交互的UI元素才能继续。 (我们只是说那是一个解锁的幻灯片)
  3. 视频首映:使用Youtube嵌入Javascript API的自定义播放器。
  4. 由于其中一些部分有很多特定的逻辑,我将它们分成几个部分。几乎所有这些逻辑都是组件的内部逻辑,但偶尔,我想从组件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()有效,但我觉得我仍然忽略了这一点。

    什么了?

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

1 个答案:

答案 0 :(得分:4)

因此,要回答您的问题,为什么未定义视频是因为您尝试访问已更改上下文的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(),链就会在那里结束。我有它,因为它更容易看到< em>其中链条停止。

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

var finish = new FinishComponentHandler();
var premiere = new PremiereComponentHandler(finish);
var splash = new SplashComponentHandler(premiere);
var loading = new LoadingComponentHandler(splash);

加载组件会调用doWork对象上的splashdoWork对象将依次调用premiere对象上的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()的最后一点,您可以使用promises使整个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()

让它全部使用response的诀窍是确保始终从console.log(body)返回承诺。