這個算是 Chrome only 其他的我沒測試,也不想測試。因為我的控制台腳本僅僅在 Chrome 下載入。 如果你需要全平臺,那麼這肯定不是你需要的結果。 <! more 需求 其實我很早就想折騰這個了,但是,,因為懶,拖了很久,直到周末,我看到伺服器上統計,發現流量翻了一倍,結果訪問量還是一樣 ...
這個算是 Chrome only 其他的我沒測試,也不想測試。因為我的控制台腳本僅僅在 Chrome 下載入。
如果你需要全平臺,那麼這肯定不是你需要的結果。
需求
其實我很早就想折騰這個了,但是,,因為懶,拖了很久,直到周末,我看到伺服器上統計,發現流量翻了一倍,結果訪問量還是一樣的時候,我才下決心折騰。
知之為知之不知谷歌之
一開始,谷歌一番,發現有兩種思路。
第一個是 sindresorhus 大神寫的 devtools-detect,算是全平臺相容(除IE),但獨立視窗打開的時候是檢測不到的。
另一個是咱們國人 zswang 寫的 jdetects,目測也是 Chrome only,當然我的靈感也來至於他。
雖然有兩個現成的,但這都不是我滿意的模式,於是乎就有了本次 Chrome 控制台環境探索之旅。
分析控制台環境
根據 zswang 的 jdetects 得知,控制台會解析節點元素的 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