Chrome 監聽 console 打開


這個算是 Chrome only 其他的我沒測試,也不想測試。因為我的控制台腳本僅僅在 Chrome 下加載。
如果你需要全平台,那么這肯定不是你需要的結果。

需求

其實我很早就想折騰這個了,但是,,因為懶,拖了很久,直到周末,我看到服務器上統計,發現流量翻了一倍,結果訪問量還是一樣的時候,我才下決心折騰。

知之為知之不知谷歌之

一開始,谷歌一番,發現有兩種思路。

第一個是 sindresorhus 大神寫的 devtools-detect,算是全平台兼容(除IE),但獨立窗口打開的時候是檢測不到的。
另一個是咱們國人 zswang 寫的 jdetects,目測也是 Chrome only,當然我的靈感也來至於他。

雖然有兩個現成的,但這都不是我滿意的模式,於是乎就有了本次 Chrome 控制台環境探索之旅。

分析控制台環境

根據 zswangjdetects 得知,控制台會解析節點元素的 id 屬性。
那么為什么會解析呢?或者他還做了什么處理呢?

打開瀏覽器,按 F12 打開 console 后輸入 debugger 按回車,然后按兩次 F11,OK 完成。
如果你的 Chrome 50 的話,映入眼簾的是密密麻麻的一大串壓縮的字符,好在他們沒 uglify,否則我就默默關了,也不會有這篇文章了。

點左下角 {} 格式化代碼后,變的非常漂亮,但是沒有注釋了,我記得之前版本都是有注釋的,更容易閱讀。
大致預覽下代碼,最終定位到 660 行的 _describe 方法處,其他都不管我也不知道干嘛的,分析需要的代碼即可。

// 用易懂的形式,描述各種對象方法,如正則,日期,node,數組,函數 等。
_describe: function(obj) {
  if (this.isPrimitiveValue(obj)) // 如果是原始值不描述
    return null;

  // 獲取類型名包括 ArrayLike,但不是 Object.prototype.toString,有興趣可以單獨查看源碼
  var subtype = this._subtype(obj);

  if (subtype === "regexp") // 正則和日期輸出 toString 后的結果。
    return toString(obj);
  if (subtype === "date")
    return toString(obj);

  if (subtype === "node") { // dom 節點處理,這里是重點
    // 獲取節點名,text comment 等只有 nodeName
    var description = obj.nodeName.toLowerCase();
    switch (obj.nodeType) { // 節點類型
      case 1: // Element 類型
        description += obj.id ? "#" + obj.id : ""; // 獲取元素 id
        var className = obj.className; // 獲取元素 class
        description += (className && typeof className === "string") ? "." + className.trim().replace(/\s+/g, ".") : "";
        break;
      case 10: // DocumentType 類型
        description = "<!DOCTYPE " + description + ">";
        break;
    }
    return description;
  }

  // 獲取內部構造函數名,可能類似 Object.prototype.toString
  var className = InjectedScriptHost.internalConstructorName(obj);

  // 類似數組的就輸出 對象名[長度] 比如 Array[3], jQuery.fn.jQuery.init[2] 之類的
  if (subtype === "array") {
    if (typeof obj.length === "number")
      className += "[" + obj.length + "]";
    return className;
  }

  if (typeof obj === "function") // 函數 toString 
    return toString(obj);

  if (isSymbol(obj)) { // Symbol 處理
    try {
      return (InjectedScriptHost.callFunction(Symbol.prototype.toString, obj)) || "Symbol";
    } catch (e) {
      return "Symbol";
    }
  }

  // 錯誤類型處理
  if (InjectedScriptHost.subtype(obj) === "error") {
    try {
      var stack = obj.stack;
      var message = obj.message && obj.message.length ? ": " + obj.message : "";
      var firstCallFrame = /^\s+at\s/m.exec(stack);
      var stackMessageEnd = firstCallFrame ? firstCallFrame.index : -1;
      if (stackMessageEnd !== -1) {
        var stackTrace = stack.substr(stackMessageEnd);
        return className + message + "\n" + stackTrace;
      }
      return className + message;
    } catch (e) {}
  }
  return className;
}

OK,代碼挺簡單的,看完基本就知道為什么 jdetects 可以檢測控制台是否被打開了。
那么現在我們知道,其實完全可以借助 正則,日期,函數toString 實現,而且更簡單,例如:

var re = /x/;
var i = 0;
console.log(re);

re.toString = function () {
  return '第 ' + (++i) + ' 次打開控制台';
};

簡單又好用,也不需要定時器或 resize 事件監視,性能更是好到不用說。需要注意的是這里的 re.toString 必須在 console.log 之后定義,否則沒打開控制台 toString 也會執行。
如果只是監聽控制台打開,這個幾行代碼足以,但是我還沒想到監聽控制台關閉方法。

這么簡單的代碼,我就不寫成插件裝逼了,需要的時候直接用即可。在 runjs 上寫了個 demo,打開預覽下效果吧!
預覽: http://sandbox.runjs.cn/show/vjtgjbzg

后序

控制台環境下有很多功能都很方便很好用,多讀讀會發現很多神奇的技巧。

本文同步至我的個人博客:http://www.52cik.com/2016/04/27/chrome-console-open.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM