js 类型检测(红宝书)

4.1.4 检测类型

要检测一个变量是不是基本数据类型?第 3 章介绍的 typeof 操作符是最佳的工具。说得更具体一
点, typeof 操作符是确定一个变量是字符串、数值、布尔值,还是 undefined 的最佳工具。如果变
量的值是一个对象或 null,则 typeof 操作符会像下面例子中所示的那样返回"object":

var s = "Nicholas";
var b = true;
var i = 22;
var u;
var n = null;
var o = new Object();
alert(typeof s); //string
alert(typeof i); //number
alert(typeof b); //boolean
alert(typeof u); //undefined
alert(typeof n); //object
alert(typeof o); //object
DeterminingTypeExample01.htm

虽然在检测基本数据类型时 typeof 是非常得力的助手,但在检测引用类型的值时,这个操作符的
用处不大。 通常, 我们并不是想知道某个值是对象,而是想知道它是什么类型的对象。为此, ECMAScript
提供了 instanceof 操作符,其语法如下所示:
result = variable instanceof constructor
如果变量是给定引用类型(根据它的原型链来识别;第 6 章将介绍原型链)的实例,那么
instanceof 操作符就会返回 true。请看下面的例子:
alert(person instanceof Object); // 变量 person 是 Object 吗?
alert(colors instanceof Array); // 变量 colors 是 Array 吗?
alert(pattern instanceof RegExp); // 变量 pattern 是 RegExp 吗?
根据规定,所有引用类型的值都是 Object 的实例。因此,在检测一个引用类型值和 Object 构造
函数时, instanceof 操作符始终会返回 true。当然,如果使用 instanceof 操作符检测基本类型的
值,则该操作符始终会返回 false,因为基本类型不是对象。
使用 typeof 操作符检测函数时,该操作符会返回"function"。在 Safari 5 及
之前版本和 Chrome 7 及之前版本中使用 typeof 检测正则表达式时,由于规范的原
因,这个操作符也返回"function"。 ECMA-262 规定任何在内部实现[[Call]]方法
的对象都应该在应用 typeof 操作符时返回"function"。由于上述浏览器中的正则
表达式也实现了这个方法,因此对正则表达式应用 typeof 会返回"function"。在
IE 和 Firefox 中,对正则表达式应用 typeof 会返回"object"。

22.1.1 安全的类型检测

JavaScript 内置的类型检测机制并非完全可靠。事实上,发生错误否定及错误肯定的情况也不在少
数。比如说 typeof 操作符吧,由于它有一些无法预知的行为,经常会导致检测数据类型时得到不靠谱
的结果。 Safari(直至第 4 版)在对正则表达式应用 typeof 操作符时会返回"function",因此很难确
定某个值到底是不是函数。
再比如, instanceof 操作符在存在多个全局作用域(像一个页面包含多个 frame)的情况下,也
是问题多多。一个经典的例子(第 5 章也提到过)就是像下面这样将对象标识为数组。
var isArray = value instanceof Array;
以上代码要返回 true, value 必须是一个数组,而且还必须与 Array 构造函数在同个全局作用域
中。(别忘了, Array 是 window 的属性。)如果 value 是在另个 frame 中定义的数组,那么以上代码
就会返回 false。
在检测某个对象到底是原生对象还是开发人员自定义的对象的时候,也会有问题。出现这个问题的
原因是浏览器开始原生支持 JSON 对象了。因为很多人一直在使用 Douglas Crockford 的 JSON 库,而该
库定义了一个全局 JSON 对象。于是开发人员很难确定页面中的 JSON 对象到底是不是原生的。
解决上述问题的办法都一样。大家知道,在任何值上调用 Object 原生的 toString()方法,都会
返回一个[object NativeConstructorName]格式的字符串。每个类在内部都有一个[[Class]]属
性,这个属性中就指定了上述字符串中的构造函数名。举个例子吧。
alert(Object.prototype.toString.call(value)); //"[object Array]"
由于原生数组的构造函数名与全局作用域无关,因此使用 toString()就能保证返回一致的值。利
用这一点,可以创建如下函数:

function isArray(value){
return Object.prototype.toString.call(value) == "[object Array]";
}

同样,也可以基于这一思路来测试某个值是不是原生函数或正则表达式:

function isFunction(value){
return Object.prototype.toString.call(value) == "[object Function]";
}
function isRegExp(value){
return Object.prototype.toString.call(value) == "[object RegExp]";
}

不过要注意,对于在 IE 中以 COM 对象形式实现的任何函数, isFunction()都将返回 false(因
为它们并非原生的 JavaScript 函数,请参考第 10 章中更详细的介绍)。
这一技巧也广泛应用于检测原生 JSON 对象。 Object 的 toString()方法不能检测非原生构造函
数的构造函数名。因此,开发人员定义的任何构造函数都将返回[object Object]。有些 JavaScript 库会包
含与下面类似的代码。
var isNativeJSON = window.JSON && Object.prototype.toString.call(JSON) ==
"[object JSON]";
在 Web 开发中能够区分原生与非原生 JavaScript 对象非常重要。只有这样才能确切知道某个对象到
底有哪些功能。这个技巧可以对任何对象给出正确的结论。
请注意, Object.prototpye.toString()本身也可能会被修改。本节讨论的
技巧假设 Object.prototpye.toString()是未被修改过的原生版本。

jQuery和lodash都是采用安全的类型检测的,下面是lodash代码:

lodash/.internal/baseGetTag.js

const objectProto = Object.prototype
const hasOwnProperty = objectProto.hasOwnProperty
const toString = objectProto.toString
const symToStringTag = typeof Symbol != 'undefined' ? Symbol.toStringTag : undefined

/**
 * The base implementation of `getTag` without fallbacks for buggy environments.
 *
 * @private
 * @param {*} value The value to query.
 * @returns {string} Returns the `toStringTag`.
 */
function baseGetTag(value) {
  if (value == null) {
    return value === undefined ? '[object Undefined]' : '[object Null]'
  }
  if (!(symToStringTag && symToStringTag in Object(value))) {
    return toString.call(value)
  }
  const isOwn = hasOwnProperty.call(value, symToStringTag)
  const tag = value[symToStringTag]
  let unmasked = false
  try {
    value[symToStringTag] = undefined
    unmasked = true
  } catch (e) {}

  const result = toString.call(value)
  if (unmasked) {
    if (isOwn) {
      value[symToStringTag] = tag
    } else {
      delete value[symToStringTag]
    }
  }
  return result
}

export default baseGetTag

lodash/isFunction.js

import baseGetTag from './.internal/baseGetTag.js'
import isObject from './isObject.js'

/**
 * Checks if `value` is classified as a `Function` object.
 *
 * @since 0.1.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a function, else `false`.
 * @example
 *
 * isFunction(_)
 * // => true
 *
 * isFunction(/abc/)
 * // => false
 */
function isFunction(value) {
  if (!isObject(value)) {
    return false
  }
  // The use of `Object#toString` avoids issues with the `typeof` operator
  // in Safari 9 which returns 'object' for typed arrays and other constructors.
  const tag = baseGetTag(value)
  return tag == '[object Function]' || tag == '[object AsyncFunction]' ||
    tag == '[object GeneratorFunction]' || tag == '[object Proxy]'
}

export default isFunction

javascript中对变量类型的判断

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容

  • 第5章 引用类型(返回首页) 本章内容 使用对象 创建并操作数组 理解基本的JavaScript类型 使用基本类型...
    大学一百阅读 3,204评论 0 4
  • 有人说过,很多弯路到最后都成了直路,所有的坑到最后也都成了坦途;所谓的直路和坦途并不是摆在眼前的,都是不断的的...
    老衲法号一眉道人阅读 1,310评论 0 4
  • 个人博客:https://yeaseonzhang.github.io 花了半个多月的时间,终于又把“JS红宝书”...
    Yeaseon阅读 11,465评论 9 52
  • 哲学的意义是什么?无论哲学如何分类,其意义就是生活的目的。当你确实了生活的目的,哲学就变成了狗屁!
    曲曲菜阅读 367评论 0 0
  • 我讨厌自己
    伊兔阅读 61评论 0 0