JavaScript - владелец "этого"

Я следовал руководству по созданию секундомера JavaScript и пытаюсь расширить его для работы с несколькими секундомерами (несколькими экземплярами класса). Проблема, с которой я сталкиваюсь, заключается в том, что когда я пытаюсь отобразить текущее значение, когда часы тикают, мне нужно жестко закодировать экземпляр класса, потому что использование "this" не работает (в строке, где я использую 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

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

Спасибо,

Geraint

5 ответов

Решение

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

Например,

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

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

Всякий раз, когда вы видите 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;
    }

}    

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

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

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

Пытаться:

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;
    }

}       

С помощью 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();

Обо всем по порядку. 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;
}

Посмотрите на раздел "Пользовательские объекты" в JNscript Reintroduction MDN

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

Другие вопросы по тегам