Breakpoint tentang perubahan properti

147

Firebug untuk Firefox memiliki fitur yang bagus, yang disebut "Break on property change", di mana saya dapat menandai properti apa pun dari objek apa pun, dan itu akan menghentikan eksekusi JavaScript tepat sebelum perubahan.

Saya mencoba mencapai hal yang sama di Google Chrome, dan saya tidak dapat menemukan fungsinya di debugger Chrome. Bagaimana saya melakukan ini di Google Chrome?

Arsen Zahray
sumber
1
Jika Anda ingin melakukan ini dengan elemen HTML, lihat stackoverflow.com/a/32686203/308851
chx

Jawaban:

106

Jika Anda tidak keberatan bermain-main dengan sumbernya, Anda dapat mendefinisikan ulang properti dengan pengakses.

// 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
sumber
2
apakah ada plug yang akan melakukan itu untuk saya?
Arsen Zahray
3
@ArsenZahray, tidak tahu. Namun, Anda dapat membuat fungsi praktis darinya dan menggunakan like console.watch(obj, 'someProp').
katspaugh
5
Ini tidak berfungsi untuk properti bawaan seperti window.locationuntuk alasan keamanan.
qJake
1
Untuk men-debug setter untuk elemen DOM, pola ini harus sedikit dimodifikasi. Lihat mnaoumov.wordpress.com/2015/11/29/... untuk lebih jelasnya
mnaoumov
@katspaugh dapatkah saya bertanya mengapa Anda perlu melakukan ini obj._someProp = obj.someProp;, sepertinya tidak berhubungan dengan apa yang Anda coba arsipkan (mungkin karena saya kehilangan sesuatu)
Victor
109

Edit 2016.03: Object.observesudah usang dan dihapus di Chrome 50

Edit 2014.05: Object.observetelah ditambahkan di Chrome 36

Chrome 36 dikirimkan dengan Object.observeimplementasi asli yang dapat dimanfaatkan di sini:

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

Jika Anda menginginkannya hanya sementara, Anda harus menyimpan panggilan balik dalam suatu variabel dan menelepon Object.unobserveketika selesai:

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

Perhatikan bahwa saat menggunakan Object.observe, Anda tidak akan diberitahu ketika tugas tidak mengubah apa pun, misalnya jika Anda telah menulis myObj.a = 1.

Untuk melihat tumpukan panggilan, Anda harus mengaktifkan opsi "tumpukan panggilan async" di Dev Tools:

tumpukan panggilan chrome async


Jawaban asli (2012.07):

Sebuah console.watchsketsa seperti yang disarankan oleh @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;
       }
   });
}

Doa:

console.watch(obj, "someProp");

Kesesuaian:

  • Di Chrome 20, Anda dapat menempelkannya langsung di Dev Tools saat runtime!
  • Untuk kelengkapan: di Firebug 1.10 (Firefox 14), Anda harus menyuntikkannya di situs web Anda (misalnya melalui Fiddler jika Anda tidak dapat mengedit sumber secara manual); sayangnya, fungsi yang didefinisikan dari Firebug tampaknya tidak rusak debugger(atau apakah ini masalah konfigurasi? tolong perbaiki saya), tetapi console.logberfungsi.

Edit:

Perhatikan bahwa di Firefox, console.watchsudah ada, karena Firefox tidak standar Object.watch. Karenanya di Firefox, Anda dapat melihat perubahan secara asli:

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

Namun, ini akan segera (akhir 2017) dihapus .

jakub.g
sumber
1
Ngomong-ngomong, tampaknya tidak bisa mengenai debugger dalam kode khusus adalah regresi antara Firebug 1.8 dan 1.9: issue 5757 -> duplikat masalah
5221
1
@ColeReed kita harus menyimpan nilai di suatu tempat untuk mengambilnya di pengambil; itu tidak dapat disimpan di oObj[sProp], karena rajin akan memasuki rekursi yang tak terbatas. Cobalah di Chrome, Anda akan mendapatkannya RangeError: Maximum call stack size exceeded.
jakub.g
1
Saya ingin menambahkan ini, karena asynckotak centangnya sangat emas dengan pendekatan ini: html5rocks.com/en/tutorials/developertools/async-call-stack
cnp
1
@PhiLho dimungkinkan untuk melihat tumpukan, dengan asynckotak centang seperti @cnp menulis, lihat pembaruan saya
jakub.g
1
Sebaiknya perbarui jawaban ini: Object.observesudah usang dan akan segera dihapus: lihat: chromestatus.com/features/6147094632988672
Amir Gonnen
79

Ada perpustakaan untuk ini: BreakOn ()

Jika Anda menambahkannya ke alat dev Chrome sebagai cuplikan (sumber -> cuplikan -> klik kanan -> baru -> rekatkan ini ) , Anda dapat menggunakannya kapan saja.


Untuk menggunakannya, buka dev-tools dan jalankan snippet. Kemudian untuk memecah ketika myObject.myPropertydiubah, panggil ini dari dev-console:

breakOn(myObject, 'myProperty');

Anda juga bisa menambahkan pustaka ke debug-build proyek Anda sehingga Anda tidak perlu menelepon breakOnlagi setiap kali Anda menyegarkan halaman.

BlueRaja - Danny Pflughoeft
sumber
5

Ini juga dapat dilakukan dengan menggunakan objek Proxy baru yang tujuannya persis seperti itu: mencegat membaca dan menulis ke objek yang dibungkus oleh Proxy. Anda cukup membungkus objek yang ingin Anda amati menjadi Proxy dan menggunakan objek yang dibungkus baru, bukan yang asli.

Contoh:

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

Sekarang gunakan wrapObject di mana Anda akan menyediakan originalObject sebagai gantinya dan memeriksa tumpukan panggilan saat istirahat.

Dima Slivin
sumber
Proxy setharus kembali trueagar tidak gagal untuk kasus selain yang terlacak.
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;
    }
  });
}
Roland Soós
sumber
1

Memutuskan untuk menulis versi saya sendiri dari solusi ini, simpan dalam potongan di DevTools Chrome, dan bungkus dalam IIFE yang seharusnya mendukung Node dan Browser. Juga mengubah pengamat untuk menggunakan variabel lingkup daripada properti pada objek, sehingga tidak ada kemungkinan bentrokan nama, dan kode apa pun yang menyebutkan kunci tidak akan "melihat" "kunci pribadi" baru yang dibuat:

(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)
Alexandros Katechis
sumber
-2

Chrome memiliki fitur bawaan versi terbaru https://developers.google.com/web/updates/2015/05/05/view-and-change-your-dom-breakpoints .

Jadi tidak ada lagi kebutuhan untuk pustaka dan solusi kustom, cukup klik kanan pada elemen DOM di inspektur dan pilih 'Hancurkan' -> 'modifikasi atribut' dan hanya itu.

Ivica Puljic
sumber
10
Dia meminta perubahan properti (objek js), bukan perubahan nilai atribut DOM
Z. Khullah
1
@Ivica Ini adalah teknik yang bagus, tapi ini adalah tempat yang salah untuk mengatakannya. Itu akan baik-baik saja sebagai komentar, tetapi bukan sebagai jawaban.
bnieland