日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

類型判斷在 web 開發中有非常廣泛的應用,簡單的有判斷數字還是字符串,進階一點的有判斷數組還是對象,再進階一點的有判斷日期、正則、錯誤類型,再再進階一點還有比如判斷 plainObject、空對象、Window 對象等等。

typeof

我們最最常用的莫過于 typeof,注意,盡管我們會看到諸如:

console.log(typeof('yayu')) // string

的寫法,但是 typeof 可是一個正宗的運算符,就跟加減乘除一樣!這就能解釋為什么下面這種寫法也是可行的:

console.log(typeof 'yayu') // string

typeof 是一元操作符,放在其單個操作數的前面,操作數可以是任意類型。返回值為表示操作數類型的一個字符串。

那我們都知道,在 ES6 前,JAVAScript 共六種數據類型,分別是:

Undefined、Null、Boolean、Number、String、Object

然而當我們使用 typeof 對這些數據類型的值進行操作的時候,返回的結果卻不是一一對應,分別是:

undefined、object、boolean、number、string、object

注意以上都是小寫的字符串。Null 和 Object 類型都返回了 object 字符串。

盡管不能一一對應,但是 typeof 卻能檢測出函數類型:

function a() {}
console.log(typeof a); // function

所以 typeof 能檢測出六種類型的值,但是,除此之外 Object 下還有很多細分的類型吶,如 Array、Function、Date、RegExp、Error 等。

如果用 typeof 去檢測這些類型,舉個例子:

var date = new Date();
var error = new Error();
var symbol = new Symbol('a');
console.log(typeof date); // object
console.log(typeof error); // object
console.log(typeof a); // symbol

返回的都是 object 吶,這可怎么區分~ 所以有沒有更好的方法呢?

Object.prototype.toString

是的,當然有!這就是 Object.prototype.toString!

那 Object.protototype.toString 究竟是一個什么樣的方法呢?上英文版:

When the toString method is called, the following steps are taken:

If the this value is undefined, return "[object Undefined]".

If the this value is null, return "[object Null]".

Let O be the result of calling ToObject passing the this value as the argument.

Let class be the value of the [[Class]] internal property of O.

Return the String value that is the result of concatenating the three Strings "[object ", class, and "]".

如果沒有看懂,就不妨看看我理解的:

當 toString 方法被調用的時候,下面的步驟會被執行:

  1. 如果 this 值是 undefined,就返回 [object Undefined]
  2. 如果 this 的值是 null,就返回 [object Null]
  3. 讓 O 成為 ToObject(this) 的結果
  4. 讓 class 成為 O 的內部屬性 [[Class]] 的值
  5. 最后返回由 "[object " 和 class 和 "]" 三個部分組成的字符串

通過規范,我們至少知道了調用 Object.prototype.toString 會返回一個由 "[object " 和 class 和 "]" 組成的字符串,而 class 是要判斷的對象的內部屬性。

console.log(Object.prototype.toString.call(undefined)) // [object Undefined]
console.log(Object.prototype.toString.call(null)) // [object Null]
var date = new Date();
console.log(Object.prototype.toString.call(date)) // [object Date]
var a = Symbol('a');
console.log(Object.prototype.toString.call(Symbol)) // [object Symbol]

正是因為這種特性,我們可以用 Object.prototype.toString 方法識別出更多類型!

那到底能識別多少種類型呢?

至少 12 種!

// 以下是11種:
var number = 1; // [object Number]
var string = '123'; // [object String]
var boolean = true; // [object Boolean]
var und = undefined; // [object Undefined]
var nul = null; // [object Null]
var obj = {a: 1} // [object Object]
var array = [1, 2, 3]; // [object Array]
var date = new Date(); // [object Date]
var error = new Error(); // [object Error]
var reg = /a/g; // [object RegExp]
var func = function a(){}; // [object Function]
function checkType() {
 for (var i = 0; i < arguments.length; i++) {
 console.log(Object.prototype.toString.call(arguments[i]))
 }
}

checkType(number, string, boolean, und, nul, obj, array, date, error, reg, func)

除了以上 11 種之外,還有:

console.log(Object.prototype.toString.call(Math)); // [object Math]
console.log(Object.prototype.toString.call(JSON)); // [object JSON]
function a() {
 console.log(Object.prototype.toString.call(arguments)); // [object Arguments]
}
a();

type API

既然有了 Object.prototype.toString 這個神器!那就讓我們寫個 type 函數幫助我們以后識別各種類型的值吧!

我的設想:

寫一個 type 函數能檢測各種類型的值,如果是基本類型,就使用 typeof,引用類型就使用 toString。此外鑒于 typeof 的結果是小寫,我也希望所有的結果都是小寫。

考慮到實際情況下并不會檢測 Math 和 JSON,所以去掉這兩個類型的檢測。

我們來寫一版代碼

// 第一版
var class2type = {};
// 生成class2type映射
"Boolean Number String Function Array Date RegExp Object Error Null Undefined".split(" ").map(function(item, index) {
 class2type["[object " + item + "]"] = item.toLowerCase();
})
function type(obj) {
 return typeof obj === "object" || typeof obj === "function" ?
 class2type[Object.prototype.toString.call(obj)] || "object" :
 typeof obj;
}

嗯,看起來很完美的樣子~~ 但是注意,在 IE6 中,null 和 undefined 會被 Object.prototype.toString 識別成 [object Object]!

我去,竟然還有這個兼容性!有什么簡單的方法可以解決嗎?那我們再改寫一版,絕對讓你驚艷!

第二版

var class2type = {};
// 生成class2type映射
"Boolean Number String Function Array Date RegExp Object Error".split(" ").map(function(item, index) {
 class2type["[object " + item + "]"] = item.toLowerCase();
})
function type(obj) {
 // 一箭雙雕
 if (obj == null) {
 return obj + "";
 }
 return typeof obj === "object" || typeof obj === "function" ?
 class2type[Object.prototype.toString.call(obj)] || "object" :
 typeof obj;
}

isFunction

有了 type 函數后,我們可以對常用的判斷直接封裝,比如 isFunction:

function isFunction(obj) {
 return type(obj) === "function";
}

數組

jQuery 判斷數組類型,舊版本是通過判斷 Array.isArray 方法是否存在,如果存在就使用該方法,不存在就使用 type 函數。

var isArray = Array.isArray || function( obj ) {
 return type(obj) === "array";
}

但是在 jQuery v3.0 中已經完全采用了 Array.isArray。

在上篇中,我們抄襲 jQuery 寫了一個 type 函數,可以檢測出常見的數據類型,然而在開發中還有更加復雜的判斷,比如 plainObject、空對象、Window 對象等,這一篇就讓我們接著抄襲 jQuery 去看一下這些類型的判斷。

plainObject

plainObject 來自于 jQuery,可以翻譯成純粹的對象,所謂"純粹的對象",就是該對象是通過 "{}" 或 "new Object" 創建的,該對象含有零個或者多個鍵值對。

之所以要判斷是不是 plainObject,是為了跟其他的 JavaScript對象如 null,數組,宿主對象(documents)等作區分,因為這些用 typeof 都會返回object。

jQuery提供了 isPlainObject 方法進行判斷,先讓我們看看使用的效果:

function Person(name) {
 this.name = name;
}
console.log($.isPlainObject({})) // true
console.log($.isPlainObject(new Object)) // true
console.log($.isPlainObject(Object.create(null))); // true
console.log($.isPlainObject(Object.assign({a: 1}, {b: 2}))); // true
console.log($.isPlainObject(new Person('yayu'))); // false
console.log($.isPlainObject(Object.create({}))); // false

由此我們可以看到,除了 {} 和 new Object 創建的之外,jQuery 認為一個沒有原型的對象也是一個純粹的對象。

實際上隨著 jQuery 版本的提升,isPlainObject 的實現也在變化,我們今天講的是 3.0 版本下的 isPlainObject,我們直接看源碼:

// 上節中寫 type 函數時,用來存放 toString 映射結果的對象
var class2type = {};
// 相當于 Object.prototype.toString
var toString = class2type.toString;
// 相當于 Object.prototype.hasOwnProperty
var hasOwn = class2type.hasOwnProperty;
function isPlainObject(obj) {
 var proto, Ctor;
 // 排除掉明顯不是obj的以及一些宿主對象如Window
 if (!obj || toString.call(obj) !== "[object Object]") {
 return false;
 }
 /**
 * getPrototypeOf es5 方法,獲取 obj 的原型
 * 以 new Object 創建的對象為例的話
 * obj.__proto__ === Object.prototype
 */
 proto = Object.getPrototypeOf(obj);
 // 沒有原型的對象是純粹的,Object.create(null) 就在這里返回 true
 if (!proto) {
 return true;
 }
 /**
 * 以下判斷通過 new Object 方式創建的對象
 * 判斷 proto 是否有 constructor 屬性,如果有就讓 Ctor 的值為 proto.constructor
 * 如果是 Object 函數創建的對象,Ctor 在這里就等于 Object 構造函數
 */
 Ctor = hasOwn.call(proto, "constructor") && proto.constructor;
 // 在這里判斷 Ctor 構造函數是不是 Object 構造函數,用于區分自定義構造函數和 Object 構造函數
 return typeof Ctor === "function" && hasOwn.toString.call(Ctor) === hasOwn.toString.call(Object);
}

注意:我們判斷 Ctor 構造函數是不是 Object 構造函數,用的是 hasOwn.toString.call(Ctor),這個方法可不是 Object.prototype.toString,不信我們在函數里加上下面這兩句話:

console.log(hasOwn.toString.call(Ctor)); // function Object() { [native code] }
console.log(Object.prototype.toString.call(Ctor)); // [object Function]

發現返回的值并不一樣,這是因為 hasOwn.toString 調用的其實是 Function.prototype.toString,畢竟 hasOwnProperty 可是一個函數!

而且 Function 對象覆蓋了從 Object 繼承來的 Object.prototype.toString 方法。函數的 toString 方法會返回一個表示函數源代碼的字符串。具體來說,包括 function關鍵字,形參列表,大括號,以及函數體中的內容

EmptyObject

jQuery提供了 isEmptyObject 方法來判斷是否是空對象,代碼簡單,我們直接看源碼:

function isEmptyObject(obj) {
 var name;
 for (name in obj) {
 return false;
 }
 return true;
}

其實所謂的 isEmptyObject 就是判斷是否有屬性,for 循環一旦執行,就說明有屬性,有屬性就會返回 false。

但是根據這個源碼我們可以看出isEmptyObject實際上判斷的并不僅僅是空對象。

舉個栗子:

console.log(isEmptyObject({})); // true
console.log(isEmptyObject([])); // true
console.log(isEmptyObject(null)); // true
console.log(isEmptyObject(undefined)); // true
console.log(isEmptyObject(1)); // true
console.log(isEmptyObject('')); // true
console.log(isEmptyObject(true)); // true

以上都會返回 true。

但是既然 jQuery 是這樣寫,可能是因為考慮到實際開發中 isEmptyObject 用來判斷 {} 和 {a: 1} 是足夠的吧。如果真的是只判斷 {},完全可以結合上篇寫的 type 函數篩選掉不適合的情況

Window對象

Window 對象作為客戶端 JavaScript 的全局對象,它有一個 window 屬性指向自身,這點在《JavaScript深入之變量對象》中講到過。我們可以利用這個特性判斷是否是 Window 對象。

function isWindow( obj ) {
 return obj != null && obj === obj.window;
}

isArrayLike

isArrayLike,看名字可能會讓我們覺得這是判斷類數組對象的,其實不僅僅是這樣,jQuery 實現的 isArrayLike,數組和類數組都會返回 true。

因為源碼比較簡單,我們直接看源碼:

function isArrayLike(obj) {
 // obj 必須有 length屬性
 var length = !!obj && "length" in obj && obj.length;
 var typeRes = type(obj);
 // 排除掉函數和 Window 對象
 if (typeRes === "function" || isWindow(obj)) {
 return false;
 }
 return typeRes === "array" || length === 0 ||
 typeof length === "number" && length > 0 && (length - 1) in obj;
}

重點分析 return 這一行,使用了或語句,只要一個為 true,結果就返回 true。

所以如果 isArrayLike 返回true,至少要滿足三個條件之一:

  1. 是數組
  2. 長度為 0
  3. lengths 屬性是大于 0 的數字類型,并且obj[length - 1]必須存在

第一個就不說了,看第二個,為什么長度為 0 就可以直接判斷為 true 呢?

那我們寫個對象:

var obj = {a: 1, b: 2, length: 0}

isArrayLike 函數就會返回 true,那這個合理嗎?

回答合不合理之前,我們先看一個例子:

function a(){
 console.log(isArrayLike(arguments))
}
a();

如果我們去掉length === 0 這個判斷,就會打印 false,然而我們都知道 arguments 是一個類數組對象,這里是應該返回 true 的。

所以是不是為了放過空的 arguments 時也放過了一些存在爭議的對象呢?

第三個條件:length 是數字,并且 length > 0 且最后一個元素存在。

為什么僅僅要求最后一個元素存在呢?

讓我們先想下數組是不是可以這樣寫:

var arr = [,,3]
var arrLike = {
 2: 3,
 length: 3
}

也就是說當我們在數組中用逗號直接跳過的時候,我們認為該元素是不存在的,類數組對象中也就不用寫這個元素,但是最后一個元素是一定要寫的,要不然 length 的長度就不會是最后一個元素的 key 值加 1。比如數組可以這樣寫

var arr = [1,,];
console.log(arr.length) // 2

但是類數組對象就只能寫成:

var arrLike = {
 0: 1,
 length: 1
}

所以符合條件的類數組對象是一定存在最后一個元素的!

這就是滿足 isArrayLike 的三個條件,其實除了 jQuery 之外,很多庫都有對 isArrayLike 的實現,比如 underscore:

var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
var isArrayLike = function(collection) {
 var length = getLength(collection);
 return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
};
isElement
isElement 判斷是不是 DOM 元素。
isElement = function(obj) {
 return !!(obj && obj.nodeType === 1);
};

分享到:
標簽:判斷 類型 JavaScript
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定