0

Вопрос такой:Почему в функциональном стиле в setInterval render вызывается как обычно, а в прототипном используется вызов через замыкание?

И есть ли смысл использовать метод bind вместо замыкания через новую переменную self?

Код в функциональном стиле.: function Clock(options) {

var template = options.template;
  var timer;

function render() { var date = new Date();

var hours = date.getHours();
if (hours < 10) hours = '0' + hours;

var min = date.getMinutes();
if (min < 10) min = '0' + min;

var sec = date.getSeconds();
if (sec < 10) sec = '0' + sec;

var output = template.replace('h', hours).replace('m', min).replace('s', sec);

console.log(output);

}

this.stop = function() { clearInterval(timer); };

this.start = function() { render(); timer = setInterval(render, 1000);

Тот же код в прототипном стиле:

function Clock(options) {
  this._template = options.template;
}

Clock.prototype._render = function render() { var date = new Date();

var hours = date.getHours(); if (hours < 10) hours = '0' + hours;

var min = date.getMinutes(); if (min < 10) min = '0' + min;

var sec = date.getSeconds(); if (sec < 10) sec = '0' + sec;

var output = this._template.replace('h', hours).replace('m', min).replace('s', sec);

console.log(output); };

Clock.prototype.stop = function() { clearInterval(this._timer); };

Clock.prototype.start = function() { this._render(); var self = this; this._timer = setInterval(function() { self._render(); }, 1000); };

  • Вас интересует, можно ли во втором случае использовать что-то вроде this._timer = setInterval(this._render, 1000)? Или я неправильно понял вопрос? – smellyshovel Nov 03 '18 at 11:46
  • Да,но я как понимаю кроме как использовать bind других вариантов кроме замыкания -нет.Мне не понятно почему в функциональном стиле бинд и замыкание не нужны,а прототипном нужны.Якобы всё дело в контексте – ZdraviSmisl Nov 03 '18 at 11:53
  • Посмотрите ответ в этом вопросе https://ru.stackoverflow.com/questions/535030/Потеря-контекста-вызова/536020#536020 – Nikita Umnov Nov 03 '18 at 12:09

2 Answers2

0

Касательно первой части вопроса, почему нельзя при прототипном стиле использовать this._timer = setInterval(this._render, 1000).

function A() {
  let instanceVariable = 1;

function _render() { console.log("Rendring A..."); console.log(instanceVariable); }

this.start = function() { setInterval(_render, 1000); } }

function B() { this.instanceVariable = 1; }

B.prototype._render = function() { console.log("Rendering B..."); console.log(this.instanceVariable); }

B.prototype.start = function() { setInterval(this._render, 1000); }

new A().start(); new B().start();

Причина проста: потеря контекста. Можно видеть из примера с функцией B, что _render хотя и вызывается и нормально отрабатывает, но поскольку вызвана она в контексте глобального (в данном случае) объекта, то инстансную переменную внутри этой функции не видно.

Происходит так потому, что функции setInterval мы передали в качестве первого параметра функцию, которую хотим вызывать раз в секунду, то есть, грубо говоря, сам код функции. А о контексте setInterval ничего в момент вызова знать не будет.

Именно поэтому в вашем примере используется такая конструкция:

var self = this;
this._timer = setInterval(function() {
  self._render();
}, 1000);

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

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

// var self = this; // закомментировано, явно сохранять контекст более нет необходимости
this._timer = setInterval(() => { // стрелочная функция
  this._render(); // теперь здесь используем `this`
}, 1000);
smellyshovel
  • 5,224
  • 1
    Если у вас остались какие-то вопросы касательно моего объяснения, то прошу их задать здесь, чтобы я мог обновить ответ под ваши нужды. Сейчас все выглядит довольно сумбурно, потому что я не совсем понимаю, чего именно не понимаете вы. – smellyshovel Nov 03 '18 at 12:09
  • Спасибо!Теперь всё ясно – ZdraviSmisl Nov 03 '18 at 21:22
-1

Может быть дело в том,что при прототипмном наследовании мы теряем возможность использовать локальные переменные как приватные свойства, у них больше нет общей области видимости с конструктором.По этому в функциональном наследовании мы не потеряем контекст при вызове метода render,он не ссылается на this,а в прототипном варианте render() ссылается на this._template. и из-за этого нужно привязывать контекст вызова, что мы и делаем через переменную self и метод apply. ведь при использовании setInterval контекст вызова теряется. Короче я походу бред тут сморозил)