javascript - this 在模块模式中未定义

标签 javascript design-patterns revealing-module-pattern

我正在尝试将揭示模块模式与构造函数模式一起使用来创建一些漂亮的 JS!如果像这样“倒计时”,我希望能够创建多个实例:

var c1 = new Countdown();
c1.init();
var c2 = new Countdown();
c2.init();

这些应该是独立的。因此,我在原型(prototype)上使用“this”,而不是用“var”声明变量。我在函数内引用“this”,但它是未定义的。我怎样才能访问“这个”?

var Countdown = function() {};

Countdown.prototype = (function(doc) {

    return {
        init: init
    };

    function init() {
        // day
        this.d1 = doc.createElement('div');
        this.d1.setAttribute('class', 'day-1 elem');

        // ... more elements left out here
    }

    function updateView(time) {
        this.d1.textContent = getDays(secs)[0];
    }

    function getDays(secs) {
        var result = 0;
        if (secs > 0) {
            result = secs / (60 * 60 * 24);
        }

        return Math.floor(result);
    }

})(document);

--- 编辑 ---

这是我的完整代码:

"use strict";

var Countdown = function() {
    //this.self = this;
};

Countdown.prototype = (function(doc) {

    return {
        initialize: initialize
    };

    function createElements() {
        var countdown = doc.createElement('div');
        countdown.setAttribute('class', 'countdown');

        var heading = doc.createElement('h2');
        heading.textContent = 'Countdown';
        countdown.appendChild(heading);
        document.body.appendChild(countdown);



        // day
        var wrapDay = doc.createElement('div');
        wrapDay.setAttribute('class', 'wrap-day');
        countdown.appendChild(wrapDay);

        this.d1 = doc.createElement('div');
        this.d1.setAttribute('class', 'day-1 elem');
        wrapDay.appendChild(this.d1);

        this.d2 = doc.createElement('div');
        this.d2.setAttribute('class', 'day-2 elem');
        wrapDay.appendChild(this.d2);

        var lblDay = doc.createTextNode('dage');
        wrapDay.appendChild(lblDay);

        var sepDay = doc.createElement('div');
        sepDay.setAttribute('class', 'separator');
        sepDay.textContent = ':';
        countdown.appendChild(sepDay);



        // hour
        var wrapHour = doc.createElement('div');
        wrapHour.setAttribute('class', 'wrap-hour');
        countdown.appendChild(wrapHour);

        this.h1 = doc.createElement('div');
        this.h1.setAttribute('class', 'hour-1 elem');
        wrapHour.appendChild(this.h1);

        this.h2 = doc.createElement('div');
        this.h2.setAttribute('class', 'hour-2 elem');
        wrapHour.appendChild(this.h2);

        var lblHour = doc.createTextNode('timer');
        wrapHour.appendChild(lblHour);

        var sepHour = doc.createElement('div');
        sepHour.setAttribute('class', 'separator');
        sepHour.textContent = ':';
        countdown.appendChild(sepHour);



        // min
        var wrapMin = doc.createElement('div');
        wrapMin.setAttribute('class', 'wrap-min');
        countdown.appendChild(wrapMin);

        this.m1 = doc.createElement('div');
        this.m1.setAttribute('class', 'min-1 elem');
        wrapMin.appendChild(this.m1);

        this.m2 = doc.createElement('div');
        this.m2.setAttribute('class', 'min-2 elem');
        wrapMin.appendChild(this.m2);

        var lblMin = doc.createTextNode('minutter');
        wrapMin.appendChild(lblMin);

        var sepMin = doc.createElement('div');
        sepMin.setAttribute('class', 'separator');
        sepMin.textContent = ':';
        countdown.appendChild(sepMin);



        // sec
        var wrapSec = doc.createElement('div');
        wrapSec.setAttribute('class', 'wrap-sec');
        countdown.appendChild(wrapSec);

        this.s1 = doc.createElement('div');
        this.s1.setAttribute('class', 'sec-1 elem');
        wrapSec.appendChild(this.s1);

        this.s2 = doc.createElement('div');
        this.s2.setAttribute('class', 'sec-2 elem');
        wrapSec.appendChild(this.s2);

        var lblSec = doc.createTextNode('sekunder');
        wrapSec.appendChild(lblSec);
    }

    function initialize() {
        domReady(function() {

            // create DOM
            createElements();

            // get time
            var now = new Date();
            var year = now.getFullYear();
            var month = now.getMonth();
            var dateFinal = new Date(year, month + 1, 1, 0, 0, 0); // first day next month
            var seconds = getSecsLeft(dateFinal);
            var time = getTimeLeftObject(seconds);

            // update view every second
            setInterval(function() {
                seconds = getSecsLeft(dateFinal);
                time = getTimeLeftObject(seconds);
                updateView(time);
            }, 1000);

            // first time
            updateView(time);
        });
    }

    function updateView(time) {
        var days = zeroPadding(time.days);
        var hours = zeroPadding(time.hours);
        var mins = zeroPadding(time.mins);
        var secs = zeroPadding(time.secs);

        this.d1.textContent = days[0];
        this.d2.textContent = days[1];

        this.h1.textContent = hours[0];
        this.h2.textContent = hours[1];

        this.m1.textContent = mins[0];
        this.m2.textContent = mins[1];

        this.s1.textContent = secs[0];
        this.s2.textContent = secs[1];
    }

    function getDays(secs) {
        var result = 0;
        if (secs > 0) {
            result = secs / (60 * 60 * 24);
        }

        return Math.floor(result);
    }
    function getHours(secs) {
        var result = 0;
        if (secs > 0) {
            result = (secs / (60*60)) % 24;
            result = result === 24 ? 0 : result;
        }

        return Math.floor(result);
    }
    function getMins(secs) {
        var result = 0;
        if (secs > 0) {
            result = (secs / 60) % 60;
            result = result === 60 ? 0 : result;
        }

        return Math.floor(result);
    }
    function getSecs(secs) {
        var result = 0;
        if (secs > 0) {
            result = secs % 60;
            result = result === 60 ? 0 : result;
        }

        return Math.floor(result);
    }

    function zeroPadding(num) {
        var result;
        result = num < 10 ? "0" + num : num;

        return new String(result);
    }

    function getTimeLeftObject(seconds) {
        var secs = getSecs(seconds);
        var mins = getMins(seconds);
        var hours = getHours(seconds);
        var days = getDays(seconds);

        return {
            days: days,
            hours: hours,
            mins: mins,
            secs: secs
        };
    }

    function getSecsLeft(dateFinal) {
        var result = (dateFinal - new Date()) / 1000;
        return result < 0 ? 0 : result;
    }

    function domReady(callback) {
        document.readyState === "interactive" || document.readyState === "complete" ? callback() : document.addEventListener("DOMContentLoaded", callback);
    }

})(document);

最佳答案

看来您正在 setInterval 内调用 updateView

function init() {
    // day
    this.d1 = doc.createElement('div');
    this.d1.setAttribute('class', 'day-1 elem');

    var update = updateView.bind(this); // you need to bind updateView context
    setInterval(function() {
       var time = ???
       update(time); // `this` will be current instance
    }, 1000)
}

UPD 由于您是在 domReady 回调中设置间隔,因此您需要绑定(bind)回调本身或事先绑定(bind) updateView

function initialize() {
    domReady(function() {

        // create DOM
        createElements.call(this); // call with current context

        // get time
        var now = new Date();
        var year = now.getFullYear();
        var month = now.getMonth();
        var dateFinal = new Date(year, month + 1, 1, 0, 0, 0); // first day next month
        var seconds = getSecsLeft(dateFinal);
        var time = getTimeLeftObject(seconds);

        var update = updateView.bind(this); 

        // update view every second
        setInterval(function() {
            seconds = getSecsLeft(dateFinal);
            time = getTimeLeftObject(seconds);
            update(time);
        }, 1000);

        // first time
        update(time);
    }.bind(this)); //bind callback
}

或者

function initialize() {
    var update = updateView.bind(this),
        create = createElements.bind(this); // prebind functions that use this
    domReady(function() {

        // create DOM
        create(); //call prebinded function

        // get time
        var now = new Date();
        var year = now.getFullYear();
        var month = now.getMonth();
        var dateFinal = new Date(year, month + 1, 1, 0, 0, 0); // first day next month
        var seconds = getSecsLeft(dateFinal);
        var time = getTimeLeftObject(seconds);

        // update view every second
        setInterval(function() {
            seconds = getSecsLeft(dateFinal);
            time = getTimeLeftObject(seconds);
            update(time);
        }, 1000);

        // first time
        update(time);
    });
}

关于javascript - this 在模块模式中未定义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38585653/

相关文章:

javascript - 揭示模块模式java脚本无法访问变量

javascript - 使用初始实例化类的 Jasmine 测试

javascript - 滚动时动画开始

javascript - 继承的 Angular 2 组件中的输入属性

javascript - 调用函数后,函数的作用域还剩下什么?

java - 这个问题的最佳设计模式

javascript - AngularJS - 未知提供商 : AuthProvider

c# - 您使用什么规则来描述 MVP 方法和成员

java - "instanceof"运算符的这种使用是否被认为是糟糕的设计?

javascript - 使用模块模式访问 jQuery 回调中的私有(private)变量