jquery 循环数组-更具体的 JavaScript 类型确定

2023-09-29 0 2,023 百度已收录

类型判断广泛应用于Web开发中。 简单的包括判断数字或字符串。 更高级的包括判断领域或物体。 更高级的包括判断日期、规律性、错误类型。 更高级的包括判断日期、规律性和错误类型。 还有诸如确定plainObject、空对象、Window对象等。

在上一章中,我们也介绍了 JavaScript 类型判断,但只讲了几个基本方法。 此次整理的内容更广泛,实用价值更高。

类型

最常用的是 typeof。 请注意,尽管我们会看到例如:

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

的写法,但是typeof是一个纯粹的运算符,就像加减乘除一样! 这就可以解释为什么下面的写法也是可行的:

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

typeof 是一个一元运算符,位于其单个操作数之后,可以是任何类型。 返回值是一个表示操作数类型的字符串。

嗯,我们都知道,在 ES6 之前,JavaScript 有六种数据类型,分别是:

未定义、空、布尔值、数字、字符串、对象

然而,当我们使用typeof对这种数据类型的值进行操作时,返回的结果并不是一一对应的,分别是:

未定义、对象、布尔值、数字、字符串、对象

注意jquery 循环数组,上面都是大写字符串。 Null 和 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

返回的所有对象都是对象。 怎么区分呢~那么有没有更好的方法呢?

对象.prototype.toString

是的当然! 这是Object.prototype.toString!

那么Object.protototype.toString到底是什么? 英文版:

当调用toString方法时,会执行以下步骤:

如果 this 值未定义,则返回“[object Undefined]”。

如果该值为空,则返回“[object Null]”。

令 O 为调用 ToObject 并传递 this 值作为参数的结果。

令 class 为 O 的 [[Class]] 内部属性的值。

返回由三个字符串“[object”、class 和“]”连接而成的字符串值。

如果你不明白,不妨看看我的理解:

当调用toString方法时,会执行以下步骤:

如果 this 的值未定义,则返回 [object Undefined]。 如果该值为 null,则返回 [object Null]。 让 O 成为 ToObject(this) 的结果。 让 class 成为 O 的内部属性 [[Class]] 的值。 最后返回一个由三部分组成的字符串:“[object”、class和“]”

通过规范jquery 循环数组,我们至少知道调用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(数字、字符串、布尔值、und、nul、obj、数组、日期、错误、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();

类型API

现在我们有了强大的工具Object.prototype.toString! 那我们就写一个类型函数来帮助我们后面识别各种类型的值吧!

我的想法:

编写一个类型函数,可以测量各种类型的值。 如果是基本类型,则使用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;
}

是函数

有了type function,我们就可以直接封装常用的判断,比如isFunction:

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

大批

jQuery 确定链表的类型。 旧版本判断Array.isArray方法是否存在。 如果存在,请使用此技术。 如果不存在,则使用类型函数。

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

但Array.isArray在jQuery v3.0中已经被完全采用。

在下一篇文章中,我们抄袭了jQuery,写了一个可以测量常见数据类型的类型函数。 然而开发中的判断越来越复杂,比如plainObject、空对象、Window对象等等,在这篇文章中,让我们再抄袭jQuery来看看那些类型的判别。

普通对象

plainObject 来自 jQuery,可以翻译为纯对象。 所谓“纯对象”是指通过“{}”或“new Object”创建对象,并且该对象包含零个或多个键名称对。

之所以需要判断是否是plainObject,是为了与其他JavaScript对象如null、数组、宿主对象(文档)等区分开来,因为使用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方法返回一个字符串,代表该函数的源代码。具体包括函数关键字、形参列表、大括号以及函数体中的内容。

空对象

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 对象是客户端 JavaScript 的全局对象。 它有一个指向自身的窗口属性。 这在《JavaScript 深入变量对象》中提到过。 我们可以利用这个特性来判断是否是一个Window对象。

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

类似于数组

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;
}

重点分析回线。 使用or 语句。 只要一个人是真的,那么结果就是真的。

因此,如果 isArrayLike 返回 true,则必须至少满足三个条件之一:

链表的宽度为0lengths。 该属性是小于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。

那么为了放开空论,是否意味着一些有争议的对象也被放开呢?

第三个条件:length 是一个数字,length > 0 并且最后一个元素存在。

为什么只要求最后一个元素存在?

我们先想一下该字段是否可以这样写:

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

也就是说,当我们在链表中直接用冒号跳过的时候,感觉该元素不存在,类字段对象中就不需要写这个元素了,但是最后一个元素一定要写,否则 length 和 width 不会是最后一个元素的键值加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);
};

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

悟空资源网 jquery jquery 循环数组-更具体的 JavaScript 类型确定 https://www.wkzy.net/game/197946.html

常见问题

相关文章

官方客服团队

为您解决烦忧 - 24小时在线 专业服务