【问题标题】:this undefined in module pattern这个在模块模式中未定义
【发布时间】:2016-07-26 09:10:36
【问题描述】:

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

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

这些应该是独立的。因此,我没有用“var”声明变量,而是在原型上使用“this”。我在函数中引用“this”,但它是未定义的。如何访问“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);

【问题讨论】:

  • 你的意思是你想从init函数中调用updateView?如果是,请使用updateView.call(this, 100)
  • 我使用间隔计时器每秒更新一次文本(代码省略)
  • 我想你只需要在 Countdown 构造函数中调用 this.init() ?我不确定您要达到的目标...因为对我来说应该定义它,所以我对其进行了测试并且有效...
  • 我没有看到您的代码存在问题 - 在 init 中记录 d1 似乎与预期的一样。 jsfiddle.net/bxxsu6qh 我怀疑你忽略了你遇到的问题!!此外,记录 this 不是未定义的,它是对 Countdown 实例的引用。
  • 我更新了我的问题以包含所有代码,因此您可以看到我想要实现的目标。

标签: javascript design-patterns revealing-module-pattern


【解决方案1】:

您似乎在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 回调中设置间隔,因此您需要绑定回调本身或绑定 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);
    });
}

【讨论】:

  • 很好发现(嗯,理解 - 这不是问题,只是 cmets!)
  • 哦,好吧,我明白了这个问题!不错;)
  • 只是为了我自己的利益,你为什么要把this绑定到updateView函数上?
  • @LeDoc 这是 OP 中唯一使用this 的函数。
  • 如果你直接调用updateView(time)而不绑定'this','this'将不会是你在updateView函数中的计数器实例!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-09
  • 1970-01-01
  • 1970-01-01
  • 2012-02-13
  • 2020-01-28
  • 1970-01-01
相关资源
最近更新 更多