999免费网站传奇,深圳罗湖企业网站优化价格,江苏网站建设seo优化,企业网站推广可以选择哪些方法?参考原文#xff1a;JavaScript专题之类型判断(下)
前言
在上篇《JavaScript专题之类型判断(上)》中#xff0c;我们抄袭 jQuery 写了一个 type 函数#xff0c;可以检测出常见的数据类型#xff0c;然而在开发中还有更加复杂的判断#xff0c;比如 plainObject、空对象…参考原文JavaScript专题之类型判断(下)
前言
在上篇《JavaScript专题之类型判断(上)》中我们抄袭 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({})) // trueconsole.log($.isPlainObject(new Object)) // trueconsole.log($.isPlainObject(Object.create(null))); // trueconsole.log($.isPlainObject(Object.assign({a: 1}, {b: 2}))); // trueconsole.log($.isPlainObject(new Person(yayu))); // falseconsole.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的以及一些宿主对象如Windowif (!obj || toString.call(obj) ! [object Object]) {return false;}/*** getPrototypeOf es5 方法获取 obj 的原型* 以 new Object 创建的对象为例的话* obj.__proto__ Object.prototype*/proto Object.getPrototypeOf(obj);// 没有原型的对象是纯粹的Object.create(null) 就在这里返回 trueif (!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至少要满足三个条件之一
是数组长度为 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 的。
所以是不是为了放过空的 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);
};结语
这一篇我们介绍了 jQuery 的 isPlainObject、isEmptyObject、isWindow、isArrayLike、以及 underscore 的 isElement 实现。我们可以看到即使是 jQuery 这样优秀的库一些方法的实现也并不是非常完美和严密的但是最后为什么这么做其实也是一种权衡权衡所失与所得正如玉伯在《从 JavaScript 数组去重谈性能优化》中讲到
所有这些点都必须脚踏实地在具体应用场景下去分析、去选择要让场景说话。