Ponto de interrupção na alteração de propriedade

147

O Firebug for Firefox possui um recurso interessante, chamado "Interromper na alteração de propriedade", onde eu posso marcar qualquer propriedade de qualquer objeto e interromperá a execução do JavaScript antes da alteração.

Estou tentando obter o mesmo no Google Chrome e não consigo encontrar a função no depurador do Chrome. Como faço isso no Google Chrome?

Arsen Zahray
fonte
1
Se você quiser fazer isso com elementos HTML, consulte stackoverflow.com/a/32686203/308851
chx

Respostas:

106

Se você não se importa de mexer com a fonte, pode redefinir a propriedade com um acessador.

// 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
fonte
2
Existe um plug que faria isso por mim?
Arsen Zahray
3
@ArsenZahray, não sei. No entanto, você pode criar uma função útil e usar como console.watch(obj, 'someProp').
Katspaugh
5
Isso não funciona para propriedades internas, como window.locationpor razões de segurança.
precisa
1
Para depurar setters para elementos DOM, esse padrão deve ser ligeiramente modificado. Veja mnaoumov.wordpress.com/2015/11/29/… para obter mais detalhes
mnaoumov
@katspaugh eu posso perguntar por que você precisa para isso obj._someProp = obj.someProp;, parece não relacionado sobre o que você está tentando archieve (provavelmente porque eu estou perdendo somethign)
Victor
109

Editar 2016.03: Object.observefoi descontinuado e removido no Chrome 50

Editar 2014.05: Object.observefoi adicionado no Chrome 36

O Chrome 36 é enviado com Object.observeimplementação nativa que pode ser aproveitada aqui:

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

Se você desejar apenas temporariamente, armazene o retorno de chamada em uma variável e ligue Object.unobservequando terminar:

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

Observe que, ao usar Object.observe, você não será notificado quando a tarefa não mudar nada, por exemplo, se você tiver escrito myObj.a = 1.

Para ver a pilha de chamadas, você precisa habilitar a opção "pilha de chamadas assíncronas" nas Ferramentas de Desenvolvimento:

pilha de chamadas assíncronas do chrome


Resposta original (2012.07):

Um console.watchesboço sugerido por @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;
       }
   });
}

Invocação:

console.watch(obj, "someProp");

Compatibilidade:

  • No Chrome 20, você pode colá-lo diretamente no Dev Tools em tempo de execução!
  • Para ser completo: no Firebug 1.10 (Firefox 14), você deve injetá-lo em seu site (por exemplo, via Fiddler, se você não pode editar a fonte manualmente); infelizmente, as funções definidas no Firebug parecem não funcionar debugger(ou é uma questão de configuração? por favor me corrija), mas console.logfunciona.

Editar:

Observe que no Firefox console.watchjá existe, devido ao não padrão do Firefox Object.watch. Portanto, no Firefox, você pode observar as alterações de forma nativa:

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

No entanto, isso será removido em breve (final de 2017) .

jakub.g
fonte
1
A propósito, parece ser incapaz de bater depurador de código personalizado é uma regressão entre Firebug 1,8 e 1,9: edição 5757 -> duplicar de emissão 5221
jakub.g
1
@ColeReed, devemos armazenar o valor em algum lugar para recuperá-lo no getter; não pode ser armazenado oObj[sProp], porque o getter entraria em uma recursão infinita. Experimente no Chrome, você receberá RangeError: Maximum call stack size exceeded.
precisa saber é
1
Gostaria de adicionar isso, pois a asynccaixa de seleção é tão dourada com esta abordagem: html5rocks.com/en/tutorials/developertools/async-call-stack
cnp:
1
@PhiLho é possível ver a pilha, com a asynccaixa de seleção como @cnp escreveu, ver meu update
jakub.g
1
Deve atualizar esta resposta: Object.observeé obsoleto e em breve será removido: ver: chromestatus.com/features/6147094632988672
Amir Gonnen
79

Existe uma biblioteca para isso: BreakOn ()

Se você adicioná-lo às ferramentas de desenvolvimento do Chrome como um snippet (fontes -> snippets -> clique com o botão direito do mouse -> novo -> cole este ) , poderá usá-lo a qualquer momento.


Para usá-lo, abra o dev-tools e execute o snippet. Para interromper quando myObject.myPropertyfor alterado, chame isso no console do desenvolvedor:

breakOn(myObject, 'myProperty');

Você também pode adicionar a biblioteca à compilação de depuração do seu projeto para não precisar ligar breakOnnovamente sempre que atualizar a página.

BlueRaja - Danny Pflughoeft
fonte
5

Isso também pode ser feito usando o novo objeto Proxy, cujo objetivo é exatamente o seguinte: interceptar as leituras e gravações no objeto envolvido pelo Proxy. Você simplesmente quebra o objeto que gostaria de observar em um Proxy e usa o novo objeto quebrado em vez do original.

Exemplo:

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

Agora use wrapObject onde você forneceria originalObject e examine a pilha de chamadas no intervalo.

Dima Slivin
fonte
Os proxy setdevem retornar truepara que não falhem em outros casos que não sejam rastreados.
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
fonte
1

Decidi escrever minha própria versão desta solução, salve-a em um trecho no DevTools do Chrome e envolva-a em um IIFE que deve oferecer suporte a nós e navegadores. Também mudou o observador para usar uma variável de escopo em vez de uma propriedade no objeto, de modo que não haja possibilidade de conflitos de nomes, e qualquer código que enumere chaves não "verá" a nova "chave privada" criada:

(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
fonte
-2

O Chrome tem esse recurso incorporado nas versões mais recentes https://developers.google.com/web/updates/2015/05/05/view-and-change-your-dom-breakpoints .

Portanto, não há mais necessidade de bibliotecas e soluções personalizadas, basta clicar com o botão direito do mouse no elemento DOM no inspetor e escolher 'Interromper' -> 'modificações de atributos' e pronto.

Ivica Puljic
fonte
10
Ele pediu (js objeto) mudança de propriedade, não DOM alteração do valor atributo
Z. Khullah
1
@ Ivica Esta é uma boa técnica, mas este é o lugar errado para colocá-lo. Seria bom como um comentário, mas não como uma resposta.
precisa saber é o seguinte