這個(gè)算是 Chrome only 其他的我沒(méi)測(cè)試肢执,也不想測(cè)試留搔。因?yàn)槲业目刂婆_(tái)腳本僅僅在 Chrome 下加載。
如果你需要全平臺(tái)砂吞,那么這肯定不是你需要的結(jié)果署恍。
需求
其實(shí)我很早就想折騰這個(gè)了,但是蜻直,盯质,因?yàn)閼校狭撕芫酶哦钡街苣┖粝铮铱吹椒?wù)器上統(tǒng)計(jì),發(fā)現(xiàn)流量翻了一倍赎瑰,結(jié)果訪問(wèn)量還是一樣的時(shí)候王悍,我才下決心折騰。
知之為知之不知谷歌之
一開(kāi)始乡范,谷歌一番配名,發(fā)現(xiàn)有兩種思路。
第一個(gè)是 sindresorhus 大神寫(xiě)的 devtools-detect晋辆,算是全平臺(tái)兼容(除IE)渠脉,但獨(dú)立窗口打開(kāi)的時(shí)候是檢測(cè)不到的。
另一個(gè)是咱們國(guó)人 zswang 寫(xiě)的 jdetects瓶佳,目測(cè)也是 Chrome only芋膘,當(dāng)然我的靈感也來(lái)至于他。
雖然有兩個(gè)現(xiàn)成的霸饲,但這都不是我滿意的模式为朋,于是乎就有了本次 Chrome 控制臺(tái)環(huán)境探索之旅。
分析控制臺(tái)環(huán)境
根據(jù) zswang 的 jdetects 得知厚脉,控制臺(tái)會(huì)解析節(jié)點(diǎn)元素的 id 屬性习寸。
那么為什么會(huì)解析呢?或者他還做了什么處理呢傻工?
打開(kāi)瀏覽器霞溪,按 F12 打開(kāi) console 后輸入 debugger 按回車,然后按兩次 F11中捆,OK 完成鸯匹。
如果你的 Chrome 50 的話,映入眼簾的是密密麻麻的一大串壓縮的字符泄伪,好在他們沒(méi) uglify殴蓬,否則我就默默關(guān)了,也不會(huì)有這篇文章了蟋滴。
點(diǎn)左下角 {}
格式化代碼后染厅,變的非常漂亮痘绎,但是沒(méi)有注釋了,我記得之前版本都是有注釋的肖粮,更容易閱讀简逮。
大致預(yù)覽下代碼,最終定位到 660 行的 _describe
方法處尿赚,其他都不管我也不知道干嘛的,分析需要的代碼即可蕉堰。
// 用易懂的形式凌净,描述各種對(duì)象方法,如正則屋讶,日期冰寻,node,數(shù)組皿渗,函數(shù) 等斩芭。
_describe: function(obj) {
if (this.isPrimitiveValue(obj)) // 如果是原始值不描述
return null;
// 獲取類型名包括 ArrayLike,但不是 Object.prototype.toString乐疆,有興趣可以單獨(dú)查看源碼
var subtype = this._subtype(obj);
if (subtype === "regexp") // 正則和日期輸出 toString 后的結(jié)果划乖。
return toString(obj);
if (subtype === "date")
return toString(obj);
if (subtype === "node") { // dom 節(jié)點(diǎn)處理,這里是重點(diǎn)
// 獲取節(jié)點(diǎn)名挤土,text comment 等只有 nodeName
var description = obj.nodeName.toLowerCase();
switch (obj.nodeType) { // 節(jié)點(diǎn)類型
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;
}
// 獲取內(nèi)部構(gòu)造函數(shù)名琴庵,可能類似 Object.prototype.toString
var className = InjectedScriptHost.internalConstructorName(obj);
// 類似數(shù)組的就輸出 對(duì)象名[長(zhǎng)度] 比如 Array[3], jQuery.fn.jQuery.init[2] 之類的
if (subtype === "array") {
if (typeof obj.length === "number")
className += "[" + obj.length + "]";
return className;
}
if (typeof obj === "function") // 函數(shù) toString
return toString(obj);
if (isSymbol(obj)) { // Symbol 處理
try {
return (InjectedScriptHost.callFunction(Symbol.prototype.toString, obj)) || "Symbol";
} catch (e) {
return "Symbol";
}
}
// 錯(cuò)誤類型處理
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,代碼挺簡(jiǎn)單的仰美,看完基本就知道為什么 jdetects 可以檢測(cè)控制臺(tái)是否被打開(kāi)了迷殿。
那么現(xiàn)在我們知道,其實(shí)完全可以借助 正則,日期,函數(shù)
的 toString
實(shí)現(xiàn)咖杂,而且更簡(jiǎn)單庆寺,例如:
var re = /x/;
var i = 0;
console.log(re);
re.toString = function () {
return '第 ' + (++i) + ' 次打開(kāi)控制臺(tái)';
};
簡(jiǎn)單又好用,也不需要定時(shí)器或 resize
事件監(jiān)視诉字,性能更是好到不用說(shuō)懦尝。需要注意的是這里的 re.toString
必須在 console.log
之后定義,否則沒(méi)打開(kāi)控制臺(tái) toString
也會(huì)執(zhí)行奏窑。
如果只是監(jiān)聽(tīng)控制臺(tái)打開(kāi)导披,這個(gè)幾行代碼足以,但是我還沒(méi)想到監(jiān)聽(tīng)控制臺(tái)關(guān)閉方法埃唯。
這么簡(jiǎn)單的代碼撩匕,我就不寫(xiě)成插件裝逼了,需要的時(shí)候直接用即可墨叛。在 runjs 上寫(xiě)了個(gè) demo止毕,打開(kāi)預(yù)覽下效果吧模蜡!
預(yù)覽: http://sandbox.runjs.cn/show/vjtgjbzg
后序
控制臺(tái)環(huán)境下有很多功能都很方便很好用,多讀讀會(huì)發(fā)現(xiàn)很多神奇的技巧扁凛。
本文同步至我的個(gè)人博客:http://www.52cik.com/2016/04/27/chrome-console-open.html