Точка останова при изменении свойства

147

Firebug для Firefox имеет замечательную функцию, называемую «Break on change property», где я могу пометить любое свойство любого объекта, и он остановит выполнение JavaScript прямо перед изменением.

Я пытаюсь добиться того же в Google Chrome, и я не могу найти функцию в отладчике Chrome. Как мне это сделать в Google Chrome?

Арсен Захрай
источник
1
Если вы хотите сделать это с помощью элементов HTML см stackoverflow.com/a/32686203/308851
СНХ

Ответы:

106

Если вы не возражаете возиться с источником, вы можете переопределить свойство с помощью метода доступа.

// original object
var obj = {
    someProp: 10
};

// save in another property
obj._someProp = obj.someProp;

// overwrite with accessor
Object.defineProperty(obj, 'someProp', {
    get: function () {
        return obj._someProp;
    },

    set: function (value) {
        debugger; // sets breakpoint
        obj._someProp = value;
    }
});
katspaugh
источник
2
есть ли плагин, который бы сделал это для меня?
Арсен Захрай
3
@ArsenZahray, не знаю. Тем не менее, вы можете сделать из него удобную функцию и использовать как console.watch(obj, 'someProp').
katspaugh
5
Это не работает для встроенных свойств, таких как window.locationпо соображениям безопасности.
qJake
1
Для отладки сеттеров для элементов DOM этот шаблон должен быть слегка изменен. См. Mnaoumov.wordpress.com/2015/11/29/… для более подробной информации
mnaoumov
@katspaugh Могу я спросить, зачем тебе это obj._someProp = obj.someProp;, кажется, это не имеет отношения к тому, что ты пытаешься архивировать (вероятно, потому, что мне не хватает чего-то)
Виктор
109

Изменить 2016.03: Object.observeустарело и удалено в Chrome 50

Редактировать 2014.05: Object.observeдобавлено в Chrome 36

Chrome 36 поставляется с собственной Object.observeреализацией, которую можно использовать здесь:

myObj = {a: 1, b: 2};
Object.observe(myObj, function (changes){
    console.log("Changes:");
    console.log(changes);
    debugger;
})
myObj.a = 42;

Если вы хотите это только временно, вы должны хранить обратный вызов в переменной и вызывать, Object.unobserveкогда закончите:

myObj = {a: 1, b: 2};
func = function() {debugger;}
Object.observe(myObj, func);
myObj.a = 42;
Object.unobserve(myObj, func);
myObj.a = 84;

Обратите внимание, что при использовании Object.observeвы не будете уведомлены, когда назначение ничего не изменило, например, если вы написали myObj.a = 1.

Чтобы увидеть стек вызовов, вам нужно включить опцию «асинхронный стек вызовов» в Dev Tools:

стек асинхронных вызовов Chrome


Оригинальный ответ (2012.07):

console.watchЭскиз как предложено @katspaugh:

var console = console || {}; // just in case
console.watch = function(oObj, sProp) {
   var sPrivateProp = "$_"+sProp+"_$"; // to minimize the name clash risk
   oObj[sPrivateProp] = oObj[sProp];

   // overwrite with accessor
   Object.defineProperty(oObj, sProp, {
       get: function () {
           return oObj[sPrivateProp];
       },

       set: function (value) {
           //console.log("setting " + sProp + " to " + value); 
           debugger; // sets breakpoint
           oObj[sPrivateProp] = value;
       }
   });
}

Призвание:

console.watch(obj, "someProp");

Совместимость:

  • В Chrome 20 вы можете вставить его прямо в Dev Tools во время выполнения!
  • Для полноты: в Firebug 1.10 (Firefox 14) вы должны добавить его на свой веб-сайт (например, через Fiddler, если вы не можете редактировать источник вручную); к сожалению, функции, определенные из Firebug, похоже, не работают debugger(или это вопрос конфигурации? пожалуйста, исправьте меня тогда), но console.logработают.

Редактировать:

Обратите внимание, что в Firefox он console.watchуже существует из-за нестандартности Firefox Object.watch. Следовательно, в Firefox вы можете наблюдать за изменениями:

>>> var obj = { foo: 42 }
>>> obj.watch('foo', function() { console.log('changed') })
>>> obj.foo = 69
changed
69

Однако, это будет скоро (конец 2017) удалено .

jakub.g
источник
1
Кстати, кажется, что неспособность поразить отладчик в пользовательском коде - это регрессия между Firebug 1.8 и 1.9: проблема 5757 ->
копия
1
@ColeReed, мы должны где-то хранить значение, чтобы получить его в получателе; это не может быть сохранено в oObj[sProp], потому что получатель вошел бы в бесконечную рекурсию. Попробуйте в Chrome, вы получите RangeError: Maximum call stack size exceeded.
jakub.g
1
Я хотел бы добавить это, поскольку asyncфлажок такой золотой с таким подходом: html5rocks.com/en/tutorials/developertools/async-call-stack
cnp
1
@PhiLho можно увидеть стек, с asyncфлажком, как написал @cnp, см. Мое обновление
jakub.g
1
Следует обновить этот ответ: Object.observeустарела и скоро будет удалена: см. Chromestatus.com/features/6147094632988672
Амир Гоннен
79

Для этого есть библиотека: BreakOn ()

Если вы добавите его в инструменты Chrome dev как фрагмент (исходники -> фрагменты -> щелчок правой кнопкой мыши -> новый -> вставить это ) , вы можете использовать его в любое время.


Чтобы использовать его, откройте dev-tools и запустите сниппет. Чтобы прервать myObject.myPropertyизменение, вызовите его из консоли dev:

breakOn(myObject, 'myProperty');

Вы также можете добавить библиотеку в отладочную сборку вашего проекта, чтобы вам не нужно было breakOnповторно вызывать каждый раз, когда вы обновляете страницу.

BlueRaja - Дэнни Пфлугхофт
источник
5

Это также можно сделать с помощью нового объекта Proxy , цель которого именно в этом: перехватывать чтение и запись в объект, который упакован прокси. Вы просто оборачиваете объект, который вы хотели бы наблюдать, в прокси-сервер и используете новый обернутый объект вместо исходного.

Пример:

const originalObject = {property: 'XXX', propertyToWatch: 'YYY'};
const watchedProp = 'propertyToWatch';
const handler = {
  set(target, key, value) {
    if (key === watchedProp) {
      debugger;
    }
    target[key] = value;
  }
};
const wrappedObject = new Proxy(originalObject, handler);

Теперь используйте wrappedObject, где вы бы вместо этого указали originalObject и проверяли стек вызовов на разрыв.

Дима Сливин
источник
Прокси setдолжны вернуться, trueчтобы не потерпеть неудачу, за исключением отслеживаемых случаев.
keaukraine
4
function debugProperty(obj, propertyName) {
  // save in another property
  obj['_' + propertyName] = obj[propertyName];

  // overwrite with accessor
  Object.defineProperty(obj, propertyName, {
    get: function() {
      return obj['_' + propertyName];
    },

    set: function(value) {
      debugger; // sets breakpoint
      obj['_' + propertyName] = value;
    }
  });
}
Роланд Соос
источник
1

Решил написать свою собственную версию этого решения, сохранить ее во фрагменте кода в Chrome DevTools и обернуть его в IIFE, который должен поддерживать как Node, так и Browser. Также изменил наблюдатель, чтобы использовать переменную области видимости, а не свойство объекта, чтобы исключить возможность конфликта имен, и любой код, который перечисляет ключи, не будет «видеть» новый «закрытый ключ», который создается:

(function (global) {
  global.observeObject = (obj, prop) => {
    let value

    Object.defineProperty(obj, prop, {
      get: function () {
        return value
      },

      set: function (newValue) {
        debugger
        value = newValue
      },
    })
  }
})(typeof process !== 'undefined' ? process : window)
Александрос Катехис
источник
-2

Chrome имеет эту функцию, встроенную в последние версии https://developers.google.com/web/updates/2015/05/view-and-change-your-dom-breakpoints .

Поэтому больше нет необходимости в пользовательских библиотеках и решениях, просто щелкните правой кнопкой мыши на элементе DOM в инспекторе и выберите «Разбить» -> «Изменение атрибутов» и все.

Ивица Пулич
источник
10
Он попросил изменить (js object) свойство, а не изменение значения атрибута DOM
Z. Khullah
1
@ Ivica Это хорошая техника, но это не то место, где ее можно поставить. Это было бы хорошо, как комментарий, но не как ответ.
bnieland