财产变更的断点


147

Firefox的Firebug具有一个不错的功能,称为“属性更改中断”,在这里我可以标记任何对象的任何属性,并且它将在更改之前停止JavaScript执行。

我正在尝试在Google Chrome浏览器中实现相同功能,但在Chrome调试器中找不到该功能。如何在Google Chrome浏览器中执行此操作?


1
如果要使用HTML元素执行此操作,请参见stackoverflow.com/a/32686203/308851
chx 2015年

Answers:


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,不知道。但是,您可以使用它制作一个方便的功能并使用like console.watch(obj, 'someProp')
katspaugh 2012年

5
这对于内置属性(例如window.location出于安全原因)不起作用。
qJake 2014年

1
要调试DOM元素的设置器,应略微修改此模式。见mnaoumov.wordpress.com/2015/11/29/...更多细节
mnaoumov

@katspaugh我能问你为什么要这么做吗obj._someProp = obj.someProp;,似乎与你要归档的内容无关(可能是因为我想念一些东西)
Victor

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

要查看调用堆栈,您需要在开发工具中启用“异步调用堆栈”选项:

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可以工作。

编辑:

请注意console.watch,由于Firefox的非标准特性,在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我们必须将值存储在某个地方才能在getter中检索它;不能将其存储在中oObj[sProp],因为getter将进入无限递归。在Chrome中尝试,您将获得RangeError: Maximum call stack size exceeded
jakub.g 2013年

1
我想添加此内容,因为async使用此方法的复选框非常金黄: html5rocks.com/en/tutorials/developertools/async-call-stack
cnp

1
@PhiLho可以看到堆栈,并带有async@cnp所写的复选框,请参阅我的更新
jakub.g 2016年

1
应该更新此答案:Object.observe已弃用,不久将被删除:请参阅:chromestatus.com/features/6147094632988672
Amir Gonnen

79

有一个用于此的库: BreakOn()

如果您将其作为摘要添加到Chrome开发工具中(源->摘要->右键单击->新建->粘贴,则可以随时使用。


要使用它,请打开dev-tools并运行代码段。然后要中断myObject.myProperty更改时间,请从dev-console调用它:

breakOn(myObject, 'myProperty');

您还可以将库添加到项目的调试版本中,这样就不必在breakOn每次刷新页面时都再次调用。


5

这也可以通过使用新的Proxy对象来完成,该对象的确切目的是:拦截对Proxy所包装的对象的读写操作。您只需将要观察的对象包装到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中的一段代码中,并将其包装在同时支持Node和Browsers的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对象)属性更改,而不是DOM属性值更改
Z. Khullah,

1
@Ivica这是一个很好的技术,但这是放错位置的地方。可以将其用作评论,但不能作为答案。
bnieland
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.