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


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

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) удалено .


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повторно вызывать каждый раз, когда вы обновляете страницу.


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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.