这篇笔记主要是在讨论:Chrome DevTools 和 VSCode + Node.js 在执行“条件断点”时,对多语句条件表达式的取值方式不一样

通过 console.loghong.push 这两类测试可以看出:

  • Chrome 条件断点更像是执行完整段代码后,取最后一个表达式的结果来判断是否断住;
  • VSCode + Node.js 条件断点更像是在用户输入的条件前面加了 return,所以会取第一个表达式的结果,后面的语句可能不会继续执行;
  • 这个差异会影响带副作用的条件表达式,比如 hong.push(1233) 是否真的执行、是否记录数据、以及最终是否触发断点。

后面的插桩内容,是为了从 VSCode 的 js-debug 传给调试器的 condition 字段中,验证这个现象背后的实现原因。

  • 测试方案
// 条件断点

// 谷歌浏览器中
console.log(11111111111111); // 不会断住 会打印
console.log(11111111111111);true; // 会断住 会打印
true;console.log(11111111111111); // 不会断住 会打印

hong.push(1233); // 会断住 会记录
hong.push(1233);false; // 不会断住 会记录
false;hong.push(1233); // 会断住 会记录



// Nodejs + vscode
console.log(11111111111111); // 不会断住 会打印
console.log(11111111111111);true; // 不会断住 会打印
true;console.log(11111111111111); // 会断住 会打印

hong.push(1233); // 会断住 会记录
hong.push(1233);false; // 会断住 会记录
false;hong.push(1233); // 不会断住 不会记录



  • 得出结果
// 第一个 console.log 和 hong.push
// 两个语句
console.log // 返回是 undefined
hong.push // 返回的是数组长度,转成条件判断时是真


// Chrome:用“最后一个表达式”的值当结果
// VSCode Node:用“第一个表达式”的值当结果

也就是说,同样写多个语句时,两个地方对“条件表达式结果”的取值方式不一样:

// Chrome 条件断点
console.log(111); true;
// 先执行 console.log,再取最后的 true,所以会断住

true; console.log(111);
// 最后一个表达式是 console.log 的返回值 undefined,所以不会断住
// VSCode + Node 条件断点
console.log(111); true;
// 取第一个表达式 console.log 的返回值 undefined,所以不会断住

true; console.log(111);
// 取第一个表达式 true,所以会断住

hong.push(1233) 这个例子容易看出来,是因为 push 本身有副作用,会修改数组;并且它的返回值是新数组长度,比如 12,在条件判断里会被当成真。

所以:

hong.push(1233); false;
  • 在 Chrome 里会执行 push,但是最后结果是 false,所以不会断住;
  • 在 VSCode + Node 里会执行 push,并且第一个表达式 hong.push(1233) 的返回值为真,所以会断住。

再看:

false; hong.push(1233);
  • 在 Chrome 里会执行到 push,最后结果为真,所以会断住;

  • 在 VSCode + Node 里第一个表达式就是 false,后面的 hong.push(1233) 不会继续作为条件主体执行,所以不会断住,也不会记录。

  • vscode+nodejs插桩

// Ctrl+Shift+P → 输入 “Developer: Toggle Developer Tools”
// Microsoft VS Code\resources\app\extensions\ms-vscode.js-debug\src\extension.js
async _setAny(e,t){if(!this.isEnabled)return;let n={state:0,args:t,deadletter:!1};console.log('breakpoint', JSON.stringify(t));n.done=(async()=>{let i=RO(t)?await e.cdp().Debugger.setBreakpoint(t)


// console.log('breakpoint', JSON.stringify(t));
// 加入了这个代码,但是这个代码是用 vscode中的开发者工具才可以显示的

// {"urlRegex":"[fF][iI][lL][eE]:\\/\\/\\/[cC]:\\/[uU][sS][eE][rR][sS]\\/[rR][aA][nN][hH][oO][nN][gG][fF][eE][iI][xX][uU][eE]\\/[dD][eE][sS][kK][tT][oO][pP]\\/[wW][oO][rR][kK]0407\\/[wW][oO][rR][kK]\\/[nN][eE][wW][aA][sS][tT]\\/[sS][tT][uU][dD][yY]\\/[cC][aA][sS][eE]1\\/[cC][oO][dD][eE]\\.[jJ][sS]($|\\?)|[cC]:\\\\[uU][sS][eE][rR][sS]\\\\[rR][aA][nN][hH][oO][nN][gG][fF][eE][iI][xX][uU][eE]\\\\[dD][eE][sS][kK][tT][oO][pP]\\\\[wW][oO][rR][kK]0407\\\\[wW][oO][rR][kK]\\\\[nN][eE][wW][aA][sS][tT]\\\\[sS][tT][uU][dD][yY]\\\\[cC][aA][sS][eE]1\\\\[cC][oO][dD][eE]\\.[jJ][sS]($|\\?)","condition":"(()=>{try{return hong.push(123);false;}catch(e){console.error(`Breakpoint condition error: ${e.message||e}`);return false}})()","lineNumber":17,"columnNumber":0}

// urlRegex -> [fF][iI][lL][eE]:\\/\\/\\/[cC]:\\/[uU][sS][eE][rR][sS]\\/[rR][aA][nN][hH][oO][nN][gG][fF][eE][iI][xX][uU][eE]\\/[dD][eE][sS][kK][tT][oO][pP]\\/[wW][oO][rR][kK]0407\\/[wW][oO][rR][kK]\\/[nN][eE][wW][aA][sS][tT]\\/[sS][tT][uU][dD][yY]\\/[cC][aA][sS][eE]1\\/[cC][oO][dD][eE]\\.[jJ][sS]($|\\?)|[cC]:\\\\[uU][sS][eE][rR][sS]\\\\[rR][aA][nN][hH][oO][nN][gG][fF][eE][iI][xX][uU][eE]\\\\[dD][eE][sS][kK][tT][oO][pP]\\\\[wW][oO][rR][kK]0407\\\\[wW][oO][rR][kK]\\\\[nN][eE][wW][aA][sS][tT]\\\\[sS][tT][uU][dD][yY]\\\\[cC][aA][sS][eE]1\\\\[cC][oO][dD][eE]\\.[jJ][sS]($|\\?)
// 转移后 file:///C:/Users/ranhongfeixue/Desktop/work0407/work/newast/study/case1/code.js


这里最关键的是 condition 字段:

condition: "(()=>{try{return hong.push(123);false;}catch(e){...;return false}})()"

VSCode 的 js-debug 会把条件断点包装成一个立即执行函数,并且在用户输入的条件前面加上 return

return hong.push(123); false;

这样 JS 执行到第一个表达式后就直接 return 了,后面的 false 不会再执行。这就解释了为什么 VSCode + Node 更像是取“第一个表达式”的结果。

对应到前面的测试:

// 用户输入
false; hong.push(1233);

// VSCode 传给调试器的大概效果
return false; hong.push(1233);

因为 return false 已经结束函数,所以 hong.push(1233) 不会执行,也就不会记录。

// 用户输入
hong.push(1233); false;

// VSCode 传给调试器的大概效果
return hong.push(1233); false;

因为 hong.push(1233) 先执行并返回数组长度,所以会记录,而且条件结果为真,会断住。

在这里插入图片描述

更多推荐