Точка перелому при зміні власності


147

Firebug для Firefox має приємну функцію під назвою "Перерва на зміну властивості", де я можу позначити будь-яку властивість будь-якого об'єкта, і він зупинить виконання JavaScript безпосередньо перед зміною.

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


1
Якщо ви хочете зробити це з елементами HTML, перегляньте stackoverflow.com/a/32686203/308851
chx

Відповіді:


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:

хром виклику асинхронізації


Оригінальна відповідь (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 -> дублікат випуску 5221
jakub.g

1
@ColeReed ми повинні зберігати значення десь, щоб отримати його в getter; його не можна зберігати 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застаріла і незабаром буде видалена: див.: Chromeestatus.com/features/6147094632988672
Амір Гоннен

79

Для цього є бібліотека: BreakOn ()

Якщо ви додасте його до інструментів розробки Chrome як фрагмент (джерела -> фрагменти -> клацніть правою кнопкою миші -> нове -> вставте це ) , ви можете використовувати його будь-коли.


Щоб скористатися ним, відкрийте розроблені інструменти та запустіть фрагмент. Потім, щоб перерватися при myObject.myPropertyзміні, викликайте це з консолі dev:

breakOn(myObject, 'myProperty');

Ви також можете додати бібліотеку до налагодження проекту, щоб не потрібно дзвонити breakOnповторно кожного разу, коли ви оновлюєте сторінку.


5

Це також можна зробити за допомогою нового проксі об'єктом , метою якого є саме таке: перехоплення читання та запису в об’єкт, який обгорнуто проксі. Ви просто загортаєте об'єкт, який ви хотіли б спостерігати, у проксі-сервер і використовуєте новий обгорнутий об'єкт замість оригінального.

Приклад:

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

Вирішив написати власну версію цього рішення, збережіть його в фрагменті в DevTools Chrome і загорнув його в IIFE, який повинен підтримувати і Вузол, і Браузери. Також поміняв спостерігача на використання змінної області, а не властивості об'єкта, таким чином, що немає можливості зіткнення імен, і будь-який код, який перераховує ключі, не "побачить" новий "приватний ключ", який створюється:

(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.