我總是經常碰到需要檢查某個function是否是原生代碼的情況 —— 這是功能測試中一個很重要的內容: 函數是浏覽器內置支持的,還是通過第三方類庫模擬的。要檢測這一點,最簡單的辦法當然是判斷函數的 toString 方法返回的值啦。
JavaScript代碼
判斷函數是否是原生方法其實相當簡單:
1// 判斷是否原生函數
2function isNative(fn) {
3// 示例:
4// alert.toString()
5// "function alert() { [native code] }"
6// '' + fn 利用了js的隱式類型轉換.
7return (/{s*[native code]s*}/).test('' + fn);
8}
將函數轉換為字符串表示的形式,並且執行正則匹配,這就是實現的原理。
升級版,Update!
01;(function() {
02
03// 取得Object的toString方法,用於處理傳入參數value的內部(internal) `[[Class]]`
04var toString = Object.prototype.toString;
05
06// 取得原始的Function的toString方法,用於處理functions的反編譯代碼
07var fnToString = Function.prototype.toString;
08
09// 用於檢測 宿主對象構造器(host constructors),
10// (Safari > 4; 真的輸出特定的數組,really typed array specific)
11var reHostCtor = /^[object .+?Constructor]$/;
12
13// 使用RegExp將常用的native方法編譯為正則模板.
14// 使用 `Object#toString` 是因為一般他不會被污染
15var reNative = RegExp('^' +
16// 將 `Object#toString` 強轉為字符串
17String(toString)
18// 對所有正則表達式相關的特殊字符進行轉義
19.replace(/[.*+?^${}()|[]/]/g, '$&')
20// 為了保持模板的通用性,將 `toString` 替換為 `.*?`
21// 將`for ...`之類的字符替換,兼容Rhino等環境,因為他們會有額外的信息,如方法的參數數量.
22.replace(/toString|(function).*?(?=()| for .+?(?=])/g, '$1.*?')
23// 結束符
24+ '$'
25);
26
27function isNative(value) {
28// 判斷 typeof
29var type = typeof value;
30return type == 'function'
31// 使用 `Function#toString`原生方法來調用,
32// 而不是 value 自己的 `toString` 方法,
33// 以免被偽造所欺騙.
34? reNative.test(fnToString.call(value))
35// 如果type 不是'function',
36// 則需要檢查宿主對象(host object)的情形,
37// 因為某些(浏覽器)環境會將 typed arrays 之類的東西當作DOM方法
38// 此時可能不匹配標准的Native正則模式
39: (value && type == 'object' && reHostCtor.test(toString.call(value))) || false;
40};
41
42// 可以將 isNative 賦值給你想要的變量/對象
43window.isNative = isNative;
44}());
測試代碼:
1isNative(isNative) //false
2isNative(alert) //true
3window.isNative(window.isNative) //false
4window.isNative(window.alert) //true
5window.isNative(String.toString) //true