摘要:与之前的类似,利用构造函数创建的字符串是一个所以检测字符串,除了基本字符串以外还要注意字符串对象。也就是说该对象由构造函数创建,或者为。
</>复制代码
原文:https://fatge.github.io/2019/...
欢迎Star:https://github.com/FatGe/FatG...
JS 的基本数据类型有 Number,String,Boolean,Symbol,Null,Undefined,六种数据类型。一种引用类型 object。
基本数据类型 Number数值,根据 ECMAScript 标准,JavaScript 中只有一种数字类型:基于 IEEE 754 标准的双精度 64 位二进制格式的值(-(263 -1) 到 263 -1)。它并没有为整数给出一种特定的类型。
除了能够表示浮点数外,还有一些带符号的值:+Infinity,-Infinity 和 NaN (非数值,Not-a-Number)。
对应 lodash 中的检测函数有
isNumber 检查 value 是否是原始 Number 数值型 或者 对象;
isInteger 检查 value 是否为一个整数;
isNaN 检测 value 是否为 NaN;
isFinite 检测 value 是否是原始有限数值。
</>复制代码
function isNumber(value) {
return typeof value == "number" ||
(isObjectLike(value) && getTag(value) == "[object Number]")
}
typeof 操作符可以返回一个字符串,表示未经计算的操作数的类型。对于 Number、String、Boolean、Undefined、String 可以很明确的得到它的类型。
那么 lodash 为什么还要添加 (isObjectLike(value) && getTag(value) == "[object Number]")?
原因在于,JS 中也允许我们以如下形式创建一个数值
</>复制代码
const value = new Number(1)
console.log(value) // log 1
console.log(typeof value) // log "object"
这时,单单只是使用 typeof 操作符就没法判断 value 的类型是否为数值。所以要结合以下两个函数来判断,value 是否为 object 然后再通过过 toString() 来获取每个对象的类型。
</>复制代码
function getTag(value) {
if (value == null) {
return value === undefined ? "[object Undefined]" : "[object Null]"
}
return Object.prototype.toString.call(value)
}
function isObjectLike(value) {
return typeof value == "object" && value !== null
}
</>复制代码
Object.prototype.toString.call 每个对象都有一个toString()方法,当该对象被表示为一个文本值时,或者一个对象以预期的字符串方式引用时自动调用。
</>复制代码
function isInteger(value) {
return
typeof value == "number"
&& value == toInteger(value);
}
检查 value 是否为一个整数,判断是否 value 的类型是否为数值,并且是否与 Int 型相同。其取整过程如下
</>复制代码
function toInteger(value) {
var result = toFinite(value),
remainder = result % 1;
return result === result ?
(remainder ? result - remainder : result) : 0;
}
检查 value 是否是 NaN。
</>复制代码
function isNaN(value) {
return isNumber(value) && value != +value;
}
与 ES 2015 的 isNaN 不同的是,对于 undefined,{},原生的结果是 true,而 lodash 为 false。这是因为如果isNaN函数的参数不是Number类型, isNaN函数会首先尝试将这个参数转换为数值,然后才会对转换后的结果是否是NaN进行判断。
</>复制代码
// js native isNaN
var isNaN = function(value) {
var n = Number(value);
return n !== n;
};
但是无论是 ES 2015 还是 lodash,它们本质上都是利用 x != x 来判断 NaN。
检查 value 是否是原始有限数值。
</>复制代码
function isFinite(value) {
return typeof value == "number"
&& nativeIsFinite(value);
}
利用原生的 isFinite 结合 typeof 判断数字是否为有限值。
StringString 类型用于表示由零或多个16 位Unicode 字符组成的字符序列,即字符串。用于保存可以以文本形式表示的数据非常有用。
值得注意的是,不单单要注意基本字符串,还需要注意字符串对象,字符串字面量 (通过单引号或双引号定义) 和 直接调用 String 方法(没有通过 new 生成字符串对象实例)的字符串都是基本字符串。
</>复制代码
JavaScript会自动将基本字符串转换为字符串对象,只有将基本字符串转化为字符串对象之后才可以使用字符串对象的方法。
与之前的 number 类似,利用构造函数 String 创建的字符串是一个 object
</>复制代码
const s_prim = "foo";
const s_obj = new String(s_prim);
console.log(typeof s_prim); // Logs "string"
console.log(typeof s_obj); // Logs "object"
所以检测字符串,除了基本字符串以外还要注意字符串对象。
</>复制代码
function isString(value) {
const type = typeof value
return
type == "string" ||
(type == "object"
&& value != null
&& !Array.isArray(value)
&& getTag(value) == "[object String]")
}
可以利用 typeof 检测基本字符串,对于模板字符串采用了之前介绍的方案 getTag 来获取 value 的类型。
BooleanBoolean 类型是ECMAScript 中使用得最多的一种类型,该类型只有两个字面值:true 和 false。同样也需要区分基本的 Boolean 类型以及 Boolean 对象。
</>复制代码
function isBoolean(value) {
return
value === true || value === false ||
(isObjectLike(value)
&& getTag(value) == "[object Boolean]")
}
大部分在之前都已经涉及到了,这里出现了 isObjectLike,那么它是做什么的。
</>复制代码
function isObjectLike(value) {
return typeof value == "object" && value !== null
}
原来只是检测是否是一个非 null 的对象。
SymbolES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。Symbol 值通过Symbol函数生成。
</>复制代码
function isSymbol(value) {
const type = typeof value
return type == "symbol" ||
(isObjectLike(value) &&
getTag(value) == "[object Symbol]")
}
会发现 (isObjectLike(value) && getTag(value) == "[object Symbol]"),也对 Symbol 对象进行检测,但是如果直接 new Symbol 会 log 出 TypeError。
那么 lodash 为什么要对其进行检测,原来是创建一个显式包装器对象从 ECMAScript 6 开始不再被支持,现在可以利用如下代码来模拟,虽然没什么用。
</>复制代码
const sym = Symbol("foo");
typeof sym; // "symbol"
const symObj = Object(sym);
typeof symObj; // "object"
Undefined
Undefined 类型只有一个值,即特殊的 undefined。在使用 let 或 var 声明变量但未对其加以初始化时,这个变量的值就是 undefined。
</>复制代码
function isUndefined(value) {
return value === undefined;
}
Null
Null 类型是只有一个值的数据类型,这个特殊的值是 null 。与 undefined 不同的是,它是一个字面量,而 undefined 是全局对象的一个属性。
从逻辑角度来看,null 值表示一个空对象指针,null 是表示缺少的标识,指示变量未指向任何对象。而这也正是使用typeof 操作符检测null 值时会返回"object"的原因。
对其的判断也非常的简单,只需要
</>复制代码
function isNull(value) {
return value === null
}
当然你也可以使用
</>复制代码
console.log(Object.prototype.toString.call(null))
// [object Null]
以上是基本数据类型的判断,总结一下,主要是利用 typeOf 以及 Object.prototype.toString ,还有一些特殊值的特性。下面开始分析引用类型 Object
引用类型引用类型的值(对象)是引用类型的一个实例。在ECMAScript 中,引用类型是一种数据结构,用于将数据和功能组织在一起。具体的有 Object 、Array、Date、Error、RegExp、Function,还有ES2015 引入 Set、Map、WeakSet、WeakMap。
ObjectECMAScript 中的对象其实就是一组数据和功能的集合。它有一个很重要的用途,就是在 JavaScript 中的所有对象都来自 Object;所有对象从Object.prototype继承方法和属性,尽管它们可能被覆盖。即在ECMAScript 中,Object 类型是所有它的实例的基础。
所以 Lodash 去判断 value 是否为 Object 时,只使用了 typeOf 操作即可。
</>复制代码
function isObject(value) {
const type = typeof value
return value != null &&
(type == "object" || type == "function")
}
Function
Function 构造函数 创建一个新的Function对象。 在 JavaScript 中, 每个函数实际上都是一个Function对象。
</>复制代码
function isFunction(value) {
if (!isObject(value)) {
return false
}
const tag = getTag(value)
return tag == "[object Function]" ||
tag == "[object AsyncFunction]" ||
tag == "[object GeneratorFunction]" ||
tag == "[object Proxy]"
}
有个问题,typeOf 可以检测 Function对象的类型为 Function, 那为什么还需要 Object.prototype.toString 呢?
</>复制代码
// in Safari 9 which returns "object" for typed arrays and other constructors.
Array
Array 在 ECMAScript 中代表数组,它的每一项可以保存任何类型的数据。
对它的常规检测就是 Array.isArray,Lodash 也是使用这个 API,如果需要 Polyfill 方案的话,可以使用
</>复制代码
// plan 1
Object.prototype.toString.call(value) === "[object Array]"
// plan 2
value.constructor === Array
之前还有 value instanceof Array 会什么问题么?
</>复制代码
在存在不同全局变量的环境,通过语义 instanceof 检测数组的时候,value instanceof Array只有当 value 是由该页面的原始 Array 构造函数创建的数组时才能正常工作。具体请见,http://web.mit.edu/jwalden/ww...。
Date
ECMAScript 中的 Date 类型是在早期Java 中的java.util.Date 类基础上构建的。
</>复制代码
const nodeIsDate = nodeTypes && nodeTypes.isDate
const isDate = nodeIsDate
? (value) => nodeIsDate(value)
: (value) => isObjectLike(value) && getTag(value) == "[object Date]"
Lodash 分为两个环境来处理这个问题,如果是 Node 就利用 util.types.isDate(value) 来检测,如果是在游览器,就还是通过 Object.prototype.toString 来判断。
SetES2015 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
</>复制代码
const isSet = nodeIsSet
? (value) => nodeIsSet(value)
: (value) => isObjectLike(value) && getTag(value) == "[object Set]"
同样的还有 Map
</>复制代码
const isMap = nodeIsMap
? (value) => nodeIsMap(value)
: (value) => isObjectLike(value) && getTag(value) == "[object Map]"
WeakSet
WeakSet 结构与 Set 类似,也是不重复的值的集合。但是,它与 Set 有两个区别。
</>复制代码
function isWeakSet(value) {
return isObjectLike(value) && getTag(value) == "[object WeakSet]"
}
也是利用 Object.prototype.toString ,同样还有 WeakMap
</>复制代码
function isWeakMap(value) {
return isObjectLike(value) && getTag(value) == "[object WeakMap]"
}
Error
当运行时错误产生时,Error的实例对象会被抛出。
</>复制代码
function isError(value) {
if (!isObjectLike(value)) {
return false
}
const tag = getTag(value)
return tag == "[object Error]" ||
tag == "[object DOMException]" ||
(typeof value.message == "string" && typeof value.name == "string" && !isPlainObject(value))
}
有之前一致的 Object.prototype.toString 依然可以用来判断对象是否是一个 Error,除此之外,如果对象满足以下条件,也可以被视为一个 Error
具备 message、name 属性,且值为 string;
是普通对象。 也就是说该对象由 Object 构造函数创建,或者 [[Prototype]] 为 null 。
那么如何检测普通对象呢?
</>复制代码
function isPlainObject(value) {
if (!isObjectLike(value) || getTag(value) != "[object Object]") {
return false
}
if (Object.getPrototypeOf(value) === null) {
return true
}
let proto = value
while (Object.getPrototypeOf(proto) !== null) {
proto = Object.getPrototypeOf(proto)
}
return Object.getPrototypeOf(value) === proto
}
主要是利用 Object.getPrototypeOf() 方法返回指定对象的原型(内部[[Prototype]]属性的值),同时和 value 本身的 [[Prototype]] 做判断。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/106178.html
摘要:深拷贝是一件看起来很简单的事情,但其实一点儿也不简单。我们也可以利用这个实现对象的深拷贝。而是利用之前已经拷贝好的值。深拷贝的详细的源码可以在这里查看。大功告成我们虽然的确解决了深拷贝的大部分问题。 js深拷贝是一件看起来很简单的事情,但其实一点儿也不简单。对于循环引用的问题还有一些内置数据类型的拷贝,如Map, Set, RegExp, Date, ArrayBuffer 和其他内置...
摘要:在之前的文章中已经介绍过,检测的是对应的数组在二维数组中的索引,其行为跟一致,不存在于二维数组中时,返回,否则返回索引值。最后将缓存数量减少。 昨日我沿着河岸/漫步到/芦苇弯腰喝水的地方顺便请烟囱/在天空为我写一封长长的信 潦是潦草了些/而我的心意/则明亮亦如你窗前的烛光/稍有暧昧之处/势所难免/因为风的缘故 ——洛夫《因为风的缘故》 本文为读 lodash 源码的第七篇,后续文章会...
摘要:在之前的文章中已经介绍过,检测的是对应的数组在二维数组中的索引,其行为跟一致,不存在于二维数组中时,返回,否则返回索引值。最后将缓存数量减少。 昨日我沿着河岸/漫步到/芦苇弯腰喝水的地方顺便请烟囱/在天空为我写一封长长的信 潦是潦草了些/而我的心意/则明亮亦如你窗前的烛光/稍有暧昧之处/势所难免/因为风的缘故 ——洛夫《因为风的缘故》 本文为读 lodash 源码的第七篇,后续文章会...
摘要:上对位运算的解释是它经常被用来创建处理以及读取标志位序列一种类似二进制的变量。位运算,常用于处理同时存在多个布尔选项的情形。掩码中的每个选项的值都是的幂,位运算是位的。位运算,说白了就是直接对某个数据在内存中的二进制位,进行运算操作。 showImg(https://segmentfault.com/img/bVbrC56?w=2208&h=1242); 前言 上一篇文章 「前端面试题...
摘要:用于检测自己是否在自己的原型链上如果是函数,则取出该函数的原型对象否则,取出对象的原型对象其中,的判断,是为了确定的类型是对象或数组。相当于,而的构造函数是一个函数对象。 showImg(https://segmentfault.com/img/bVbq2N1?w=640&h=437); 前言 接着上一篇文章 lodash 是如何实现深拷贝的(上),今天会继续解读 _.cloneDee...
阅读 3310·2021-11-11 16:55
阅读 2718·2021-10-13 09:39
阅读 2521·2021-09-13 10:27
阅读 2231·2019-08-30 15:55
阅读 3180·2019-08-30 15:54
阅读 3210·2019-08-29 16:34
阅读 1907·2019-08-29 12:41
阅读 1142·2019-08-29 11:33