JavaScript - Владелец этого

Я следовал учебнику по созданию секундомера JavaScript и пытаюсь расширить его для работы. с несколькими секундомерами (несколько экземпляров класса). У меня проблема в том, что когда я пытаюсь отобразить текущее значение, пока часы тикают, мне нужно жестко закодировать экземпляр класса, потому что использование «этого» не работает (в строке, где я использую console.log). Я сократил код до минимума, чтобы попытаться понять этот аспект, и вставил то, что у меня есть ниже:

function Timer(){
    var time1 = null;
    var time2 = null;
    var timeLoop = null;

    function getTime(){
        var day = new Date();
        return day.getTime();
    }

    this.start = function(){
        time1 = getTime();

        timeLoop = setInterval(function(){
            time2 = getTime();
            console.log(_Timer.duration());
            //console.log(this.duration());
        },500);
    }

    this.duration = function(){
        return (time1 - time2) / 1000;
    }

}       

Я думаю, что ссылка ниже описывает мою проблему, но я недостаточно понимаю ее, чтобы применить ее здесь. Является ли проблема связана с тем, что владельцем является this.start, а не только это, и как я могу изменить код, чтобы он работал с любым экземпляром Timer?

http://www.quirksmode.org/js/this.html

Я включил как строку жестко закодированного значения, так и строку «эта», которая не работает.

Спасибо,

Герайнт


person Geraint Anderson    schedule 02.07.2014    source источник
comment
скопируйте это в то. не используйте self или другие предопределенные имена переменных, которые усложняют отладку.   -  person dandavis    schedule 02.07.2014
comment
@dandavis - Да, поэтому все используют self, плагины, jQuery, даже Google, потому что это плохо. И при использовании вне глобальной области видимости никак не конфликтует с window.self   -  person adeneo    schedule 02.07.2014
comment
то, что что-то обычное, не означает, что оно не может быть лучше. Я постоянно вижу кучу плохих js, и я бы не назвал использование себя плохим. в случае неправильного выполнения ReferenceError обычно легче найти, чем TypeError, потому что он включает имя отсутствующей переменной в сообщении об ошибке. зачем давать замыканиям невидимый путь отказа, если есть что-то сопоставимое, что выходит из строя громче и без недостатков?   -  person dandavis    schedule 02.07.2014
comment
Я увлекался веб-воркерами до Node, где self подобен окну, но роза под любым другим именем пахла бы так же сладко...   -  person dandavis    schedule 03.07.2014


Ответы (6)


Если вы хотите, чтобы свойство this было непротиворечивым, вы должны связать вызываемые функции.

Например,

setInterval(function() { /* code here */ }.bind(this), 500)

Таким образом, this внутренней функции будет таким же, как и у внешней функции.

person Strikeskids    schedule 02.07.2014

Всякий раз, когда вы видите function, вы можете предположить, что значение this изменяется, поэтому внутри функции обратного вызова для интервала this на самом деле находится window, а не объект.

Простое решение — просто сохранить this в переменной.

function Timer(){
    var time1 = null;
    var time2 = null;
    var timeLoop = null;

    function getTime(){
        var day = new Date();
        return day.getTime();
    }

    this.start = function(){

        var self = this;

        time1 = getTime();

        timeLoop = setInterval(function(){
            time2 = getTime();
            console.log(self.duration());
        },500);
    }

    this.duration = function(){
        return (time1 - time2) / 1000;
    }

}    
person adeneo    schedule 02.07.2014

this не является локальной переменной, поэтому она не сохраняется в замыканиях. Вам нужно назначить локальную переменную:

this.start = function(){
    var self = this;
    time1 = getTime();

    timeLoop = setInterval(function(){
        time2 = getTime();
        console.log(self.duration());
    },500);
}
person Barmar    schedule 02.07.2014

Пытаться:

function Timer(){
    var time1 = null;
    var time2 = null;
    var timeLoop = null;
    var _this = this;

    function getTime(){
        var day = new Date();
        return day.getTime();
    }

    this.start = function(){
        time1 = getTime();

        timeLoop = setInterval(function(){
            time2 = getTime();
            console.log(_this.duration());
        },500);
    }

    this.duration = function(){
        return (time1 - time2) / 1000;
    }

}       
person cespon    schedule 02.07.2014

Перво-наперво. Javascript не поддерживает ООП на основе классов. Это ООП вместе с его наследованием является прототипом.

Ниже приведен пример того, как реализовать прототипы функций ООП с вашим примером таймера:

function Timer(){
  var time1 = null;
  var time2 = null;
  var timeLoop = null;
}

Timer.prototype.getTime = function(){
    var day = new Date();
    return day.getTime();
}

Timer.prototype.start = function(){
    time1 = this.getTime();
    timeLoop = this.setInterval(function(){
        time2 = this.getTime();
        console.log(this.duration());
    }.bind(this),500);
}

Timer.prototype.duration = function(){
    return (time1 - time2) / 1000;
}

Посмотрите раздел «Пользовательские объекты» в Воспроизведение Javscript от MDN.

Нет ничего плохого в том, как это показано в вашем руководстве. Просто это более чистый способ, и вызов bind нужен только для оператора console.log, который в противном случае связывал бы this с window. Если вы избавитесь от него, то сможете избавиться и от bind.

person aa333    schedule 02.07.2014

Использование bind(this) работает отлично.

var timer = {
  start: function() {
    setInterval(function() {console.log(this.duration());}.bind(this), 1000);
  }
}

timer.start();

Но это может быть ненужным, если вы используете const с функцией стрелки:

const timer = {
  start() {
    setInterval(() => {console.log(this.duration());}, 1000);
  }
}

timer.start();
person Chetabahana    schedule 28.11.2019