【问题标题】:Breakpoint on property change属性更改断点
【发布时间】:2012-07-22 00:40:26
【问题描述】:

Firebug for Firefox 有一个很好的功能,称为“属性更改中断”,我可以在其中标记任何对象的任何属性,它会在更改之前停止 JavaScript 执行。

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

【问题讨论】:

标签: javascript debugging firefox google-chrome firebug


【解决方案1】:

如果您不介意弄乱源,可以使用访问器重新定义属性。

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

【讨论】:

  • 是否有插件可以为我做到这一点?
  • @ArsenZahray,不知道。但是,您可以从中创建一个方便的功能并使用 console.watch(obj, 'someProp')
  • 出于安全原因,这不适用于window.location 等内置属性。
  • 要调试 DOM 元素的设置器,应该稍微修改这个模式。更多详情请见mnaoumov.wordpress.com/2015/11/29/…
  • @katspaugh 我能问一下你为什么需要这个obj._someProp = obj.someProp;,它似乎与你想要归档的内容无关(可能是因为我错过了一些东西)
【解决方案2】:

编辑 2016.03:Object.observe 在 Chrome 50 中已弃用和删除

**编辑 2014.05:在 Chrome 36 中添加了“Object.observe”**

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

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


原答案(2012.07):

@katspaugh 建议的console.watch 草图:

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 中,您可以在运行时直接将其粘贴到开发工具中!
  • 为了完整性:在 Firebug 1.10 (Firefox 14) 中,您必须将其注入您的网站(例如,如果您无法手动编辑源代码,则通过 Fiddler);遗憾的是,从 Firebug 定义的函数似乎并没有在 debugger 上中断(或者是配置问题?请纠正我),但 console.log 有效。
请注意,在 Firefox 中,`console.watch` 已经存在,因为 Firefox 的非标准 [`Object.watch`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects /对象/手表)。因此,在 Firefox 中,您可以原生地观察变化:
>>> var obj = { foo: 42 }
>>> obj.watch('foo', function() { console.log('changed') })
>>> obj.foo = 69
changed
69
罢工>

编辑:Object.watch 在 Firefox 57 中被删除。

【讨论】:

  • 顺便说一句,似乎无法在自定义代码中命中调试器是 Firebug 1.8 和 1.9 之间的回归:issue 5757 -> issue 5221 的重复项
  • @ColeReed 我们必须将值存储在某个地方才能在 getter 中检索它;它不能存储在oObj[sProp] 中,因为getter 会进入无限递归。在 Chrome 中试试,你会得到RangeError: Maximum call stack size exceeded
  • 我想添加这个,因为 async 复选框在这种方法中非常金:html5rocks.com/en/tutorials/developertools/async-call-stack
  • @PhiLho 可以看到堆栈,@cnp 写的async 复选框,请参阅我的更新
  • 应该更新这个答案:Object.observe 已弃用,很快将被删除:请参阅:chromestatus.com/features/6147094632988672
【解决方案3】:

为此有一个库:BreakOn()

如果您将其作为 sn-p 添加到 Chrome 开发工具中 (来源 --> snippets --> 右键单击​​ --> 新建 --> 粘贴 this --> 运行),你可以随时使用。


要使用它,请打开开发工具并运行 sn-p。然后在更改myObject.myProperty 时中断,请从开发控制台调用:

breakOn(myObject, 'myProperty');

您还可以将库添加到项目的调试构建中,这样您就无需在每次刷新页面时再次调用 breakOn

【讨论】:

  • 任何尝试从开发控制台调试的人的最佳解决方案。无需额外的努力在任何网站上重复使用,太棒了!
  • 很棒的工具发现!调试复杂代码可以节省大量时间。
  • scrollLeftscrollTop 发生变化时,这并没有中断。这帮助我意识到,当用户的鼠标滚轮滚动修改这些值时,不涉及 JavaScript。 (它们是否改变受 SCSS 样式中的overflow-xoverflow-y 影响。)
【解决方案4】:

这也可以通过使用新的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 以使其不会因跟踪案例而失败。
【解决方案5】:
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;
    }
  });
}

【讨论】:

    【解决方案6】:

    决定编写我自己的解决方案版本,将其保存在 Chrome 的 DevTools 中的 sn-p 中,并将其包装在应支持 Node 和浏览器的 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)
    

    【讨论】:

      【解决方案7】:

      以 Alexandos Katechis 的出色解决方案为基础,这里是一个不影响财产原始价值的 sn-p 版本。我重命名它以更好地匹配我使用它时的想法。

      用法:

      1. 通过 Sources -> Snippets 添加 sn-p
      2. 需要时,按 Command-O 并选择运行 breakOnChange sn-p
      3. 在控制台调用breakOnChange(anyObject, 'propertyName')
      4. 采取导致改变的行动
      5. 在调试器中停止

      这对于发现诸如 jQuery 之类的全局库被第三方脚本踩踏之类的事情非常有帮助。

      (function (global) {
        global.breakOnChange = (obj, prop) => {
          let value = obj[prop]
      
          Object.defineProperty(obj, prop, {
            get: function () {
              return value
            },
      
            set: function (newValue) {
              debugger
              value = newValue
            },
          })
        }
      })(typeof process !== 'undefined' ? process : window)
      

      【讨论】:

        【解决方案8】:

        Chrome 在最新版本中内置了此功能https://developers.google.com/web/updates/2015/05/view-and-change-your-dom-breakpoints

        因此不再需要自定义库和解决方案,只需在检查器中右键单击 DOM 元素并选择“中断”->“属性修改”即可。

        【讨论】:

        • 他要求(js对象)属性更改,而不是DOM属性值更改
        • @Ivica 这是一个很好的技术,但是放错地方了。可以作为评论,但不能作为答案。
        猜你喜欢
        • 2015-11-12
        • 2012-10-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多