Почему такие изменения в методе Start при прототипном наследовании? [дубликат]

147
27 марта 2019, 00:10

На данный вопрос уже ответили:

  • Потеря контекста вызова 5 ответов

Вопрос такой:Почему в функциональном стиле в 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); 
};

Answer 1

Касательно первой части вопроса, почему нельзя при прототипном стиле использовать 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);
Answer 2

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

READ ALSO
Drag &amp; Drop всех элементов JS

Drag & Drop всех элементов JS

хочу реализовать перемещение всех блоков на JS, те нажал на один квадрат вместе с ним перемещаются другие

219
API Instagram не работает получение историй

API Instagram не работает получение историй

Не могу понять, почему не работает получение json ответа для историй для такой ссылки:https://iinstagram

169
MongoDB + setTimeout context

MongoDB + setTimeout context

Такой возник вопрос: хочу внутри рекурсивного setimeout обращаться к mongoDB (функция updateAdded), однако внутри settimeout получаю ошибку UnhandledPromiseRejectionWarning:...

149
Обработка ответа от api и обновление данных в массиве

Обработка ответа от api и обновление данных в массиве

Работаю с vue и apiПолучил я массив объектов

161