Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >lodash源码分析之获取数据类型

lodash源码分析之获取数据类型

作者头像
对角另一面
发布于 2018-04-19 10:54:48
发布于 2018-04-19 10:54:48
91800
代码可运行
举报
文章被收录于专栏:对角另一面对角另一面
运行总次数:0
代码可运行

所有的悲伤,总会留下一丝欢乐的线索,所有的遗憾,总会留下一处完美的角落,我在冰峰的深海,寻找希望的缺口,却在惊醒时,瞥见绝美的阳光! ——几米

本文为读 lodash 源码的第十八篇,后续文章会更新到这个仓库中,欢迎 star:pocket-lodash

gitbook也会同步仓库的更新,gitbook地址:pocket-lodash

作用与用法

我们都知道,可以借用 Object 原型上的 toString 方法来获取数据的类型。 baseGetTag 利用的也是这一特性,其返回的结果如 [object String] 这样的形式,调用方式如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
baseGetTag('string') // [object String] 

为什么可以用Object.prototype.toString

先看 es5 规范对 Object.prototyep.toString 的运行步骤规定:

当调用 toString 方法,采用如下步骤:

  1. 如果 this 的值是 undefined, 返回 "[object Undefined]".
  2. 如果 this 的值是 null, 返回 "[object Null]".
  3. 令 O 为以 this 作为参数调用 ToObject 的结果 .
  4. 令 class 为 O 的 [[Class]] 内部属性的值 .
  5. 返回三个字符串 "[object ", class, and "]" 连起来的字符串 .

在第三步的时候,会调用 ToObject 来转换成对象,而转换成对象后,会有个 [[Class]] 的内部属性,而这个内部属性的值正是 toString 的关键部分。

接下来再看规范对 [[Class]] 的规定:

本规范的每种内置对象都定义了 [[Class]] 内部属性的值。宿主对象的 [[Class]] 内部属性的值可以是除了 "Arguments", "Array", "Boolean", "Date", "Error", "Function", "JSON", "Math", "Number", "Object", "RegExp", "String" 的任何字符串。[[Class]] 内部属性的值用于内部区分对象的种类。注,本规范中除了通过 Object.prototype.toString ( 见 15.2.4.2) 没有提供任何手段使程序访问此值。

由规范可见,要获取这个 [[Class]] 内部属性的值的唯一手段是通过 Object.prototype.toString

源码分析

源码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const objectProto = Object.prototype
const hasOwnProperty = objectProto.hasOwnProperty
const toString = objectProto.toString
const symToStringTag = typeof Symbol != 'undefined' ? Symbol.toStringTag : undefined

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

Symbol.toStringTag

ES6 中,规范对 Object.prototype.toString 的步骤进行了重新定义,不再使用 [[Class]] 的内部属性进行获取,具体的规范如下:

在ES6,调用 Object.prototype.toString 时,会进行如下步骤:

  1. 如果 thisundefined ,返回 '[object Undefined]' ;
  2. 如果 thisnull , 返回 '[object Null]'
  3. O 为以 this 作为参数调用 ToObject 的结果;
  4. isArrayIsArray(O)
  5. ReturnIfAbrupt(isArray) (如果 isArray 不是一个正常值,比如抛出一个错误,中断执行);
  6. 如果 isArraytrue , 令 builtinTag'Array' ;
  7. else ,如果 O is an exotic String object , 令 builtinTag'String'
  8. else ,如果 O 含有 [[ParameterMap]] internal slot, , 令 builtinTag'Arguments'
  9. else ,如果 O 含有 [[Call]] internal method , 令 builtinTagFunction
  10. else ,如果 O 含有 [[ErrorData]] internal slot , 令 builtinTagError
  11. else ,如果 O 含有 [[BooleanData]] internal slot , 令 builtinTagBoolean
  12. else ,如果 O 含有 [[NumberData]] internal slot , 令 builtinTagNumber
  13. else ,如果 O 含有 [[DateValue]] internal slot , 令 builtinTagDate
  14. else ,如果 O 含有 [[RegExpMatcher]] internal slot , 令 builtinTagRegExp
  15. else , 令 builtinTagObject
  16. tagGet(O, @@toStringTag) 的返回值( Get(O, @@toStringTag) 方法,既是在 O 是一个对象,并且具有 @@toStringTag 属性时,返回 O[Symbol.toStringTag] );
  17. ReturnIfAbrupt(tag) ,如果 tag 是正常值,继续执行下一步;
  18. 如果 Type(tag) 不是一个字符串,let tag be builtinTag
  19. 返回由三个字符串 "[object", tag, and "]" 拼接而成的一个字符串。

规范对类型的判断进行了细化,前15步可以看成跟 es5 的作用一样,获取到数据的类型 builtinTag ,但是第16步调用了 @@toStringTag 的方法,如果再看规范的描述,可以知道这个其实是对象中的 Symbol.toStringTag 属性,如果这个属性返回的是一个字符串,则采用这个返回值 tag 作为数据的类型,否则才采用 builtinTag

处理null和undefined

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if (value == null) {
  return value === undefined ? '[object Undefined]' : '[object Null]'
}

这里是处理浏览器兼容性,在 es5 之前,并没有对 nullundefined 进行处理,所以返回的都是 [object Object]

处理不含Symbol.toStringTag的情况

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if (!(symToStringTag && symToStringTag in Object(value))) {
   return toString.call(value)
}

如果浏览器不支持 Symbol 或者 value 并不存在 Symbol.toStringTag 的方法,则可以直接调用 toString ,将结果返回了。

处理Symbol.toStringTag 的情况

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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]
 }
}

为了避免 Symbol.toStringTag 的影响,先将 valueSymbol.toStringTag 设置为 undefined ,这样可以屏蔽掉原型链上的 Symbol.toStringTag 属性,然后再使用 toString 方法获取到 value 的属性描述。

在获取到属性描述后,如果 Symbol.toStringTag 为自身的属性(不为原型链上的属性),则将原来保存下来的 tag 重新赋值,否则将 Symbol.toStringTag 属性移除。

参考

es5规范中文版

Standard ECMA-262

MDN:Symbol.toStringTag

ECMAScript 6 入门

谈谈 Object.prototype.toString 。

License

署名-非商业性使用-禁止演绎 4.0 国际 (CC BY-NC-ND 4.0)

最后,所有文章都会同步发送到微信公众号上,欢迎关注,欢迎提意见:

作者:对角另一面

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-04-07 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
谈谈 Object.prototype.toString 。
在ECMAScript 5中,Object.prototype.toString()被调用时,会进行如下步骤:
三毛
2018/08/30
5390
我的 Web 前端面试经历——百度
面试是个漫长的过程,从海投到收获电话面试,一面、二面、三面,一个步骤出错那么后面就宣告终结。同时,面试过程中你也可能会遇到一些面试官的刁难,甚至部分面试官会说些比较打击你的话,但是大部分面试官都是很棒的!
李才哥
2019/07/10
6740
我的 Web 前端面试经历——百度
Vue 源码解析之工具方法
检测是否是正则表达式 export function isRegExp (v) { return _toString.call(v) === '[object RegExp]' }
公众号---人生代码
2021/03/16
4210
JavaScript类型判断:解密变量真实身份的神奇技巧
JavaScript是一门弱类型语言,变量的类型是在运行时动态确定的。因此,在处理复杂的代码逻辑时,对于变量的类型判断变得尤为重要。本文将介绍一些JavaScript中常用的类型判断技巧,帮助开发者更好地理解和处理不同类型的变量。
IT_陈寒
2023/12/14
1740
JavaScript类型判断:解密变量真实身份的神奇技巧
JavaScript中的类型判断
类型判断在 web 开发中有非常广泛的应用,简单的有判断数字还是字符串,进阶一点的有判断数组还是对象,再进阶一点的有判断日期、正则、错误类型,再再进阶一点还有比如判断 plainObject、空对象、Window 对象等等。
ConardLi
2019/05/23
1.4K0
【源码共读】Vue2工具函数
点击在线地址查看,会发现该文件实际上有很多函数。实际上就是Vue2的工具函数库。下面就来简单学习一下。因为源码用的是ts,理解起来可能会加点成本,所以下面讲解会把类型部分去掉(其实是本人的ts水平不高,很难很好的解释)
赤蓝紫
2023/03/06
8760
【源码共读】Vue2工具函数
前端-如何继承 Date 对象?由一道题彻底弄懂 JS 继承
于是,随手用JS中经典的组合寄生法写了一个继承,然后,刚准备完美收工,一运行,却出现了以下的情景:
grain先森
2019/03/29
1.2K0
前端-如何继承 Date 对象?由一道题彻底弄懂 JS 继承
盘点六个阅读React源码后get到的基础知识
而阅读源码的终极目的还是应用,在这个想法下,我盘点了一些可以快速在工程中应用的( 或许冷门 )知识,希望读者可以get到
源心锁
2022/09/21
6460
盘点六个阅读React源码后get到的基础知识
初学者也能看懂的 Vue2 源码中那些实用的基础工具函数
想学源码,极力推荐之前我写的《学习源码整体架构系列》 包含jQuery、underscore、lodash、vuex、sentry、axios、redux、koa、vue-devtools、vuex4、koa-compose、vue-next-release、vue-this、create-vue、玩具vite等10余篇源码文章。
若川
2021/11/10
9860
underscore.js源码解析2
undefined派生自null,所以两者相等(==),但是不全等(===),所以这里如果obj是undefined则会返回false。
Ewall
2018/09/04
1.1K0
我从Vue源码中学到的一些JS编程技巧
在我们面试的过程中,经常会遇到问源码的环节,因为优秀的框架通常都会包含很多设计理念跟编程实践。这段时间我一直在看Vue2的源码,发现了很多有意思的实现。虽然现在Vue3都已经发布了,也无法否认Vue2是个优秀的框架这个事实,不影响我们从中学到一些最佳实践。
写代码的阿宗
2020/10/22
5650
我从Vue源码中学到的一些JS编程技巧
lodash 是如何做类型检测的
JS 的基本数据类型有 Number,String,Boolean,Symbol,Null,Undefined,六种数据类型。一种引用类型 object。
Javanx
2019/09/04
1.8K0
lodash 是如何做类型检测的
「面试基础小册」数据类型及其延伸
「面试基础小册」系列正式开写。主要是对一些基础相关的知识进行归纳整理与拓展。后续还有更多,敬请期待
小皮咖
2020/07/03
7180
JS篇之数据类型那些事儿
Web 浏览器只是 ECMAScript 实现可能存在的一种*「宿主环境(host environment)」。而宿主环境提供ECMAScript 的基准实现和与环境自身交互必需的扩展。(比如 DOM 使用 ECMAScript 核心类型和语法,提供特定于环境的额外功能)。
前端柒八九
2022/08/25
5550
JS篇之数据类型那些事儿
js基础之数据类型和拷贝
了解数据类型前先说说栈(stack)和堆(heap) *** stack为自动分配的内存空间,它由系统自动释放;而heap则是动态分配的内存,大小也不一定会自动释放 ***
用户1974410
2022/09/20
3550
js基础之数据类型和拷贝
初学者也能看懂的 Vue3 源码中那些实用的基础工具函数
我们也可以通过ts文件,查看使用函数的位置。同时在VSCode运行调试JS代码,我们比较推荐韩老师写的code runner插件。
用户7108768
2021/09/19
4430
深入了解JS 数据类型
由于JavaScript 是弱类型语言,而且JavaScript 声明变量的时候并没有预先确定的类型,变量的类型就是其值的类型,也就是说「变量当前的类型由其值所决定」,夸张点说上一秒是String,下一秒可能就是个Number类型了,这个过程可能就进行了某些操作发生了强制类型转换。虽然弱类型的这种「不需要预先确定类型」的特性给我们带来了便利,同时也会给我们带来困扰,为了能充分利用该特性就必须掌握类型转换的原理。本文我们将深入了解JavaScript 的类型机制。
用户8921923
2022/10/24
2K0
【模块化】:Webpack 是如何将不同规范(ESM、CJS、UMD、AMD、CMD)的模块化代码打包到一起并协调它们运行的?
在一个项目中同时使用 ES6、CJS、CMD、AMD、UMD 5种不同的模块化规范编写代码,并同时应用静态导入、动态导入(Dynamic Import)方法来引用这些模块。观察 Webpack 是如何将这些不同模块化规范的代码打包到一起和协调它们运行的。
WEBJ2EE
2022/03/30
7.3K2
【模块化】:Webpack 是如何将不同规范(ESM、CJS、UMD、AMD、CMD)的模块化代码打包到一起并协调它们运行的?
Ecmascript语法之Symbol
Symbol 概述 作为属性名的Symbol 实例:消除魔术字符串 属性名的遍历 Symbol.for(),Symbol.keyFor() 实例:模块的 Singleton 模式 内置的Symbol值 概述 ES5的对象属性名都是字符串,这容易造成属性名的冲突。比如,你使用了一个他人提供的对象,但又想为这个对象添加新的方法(mixin模式),新方法的名字就有可能与现有方法产生冲突。如果有一种机制,保证每个属性的名字都是独一无二的就好了,这样就从根本上防止属性名的冲突。这就是ES6引入Symbol的原因。 E
xiangzhihong
2018/02/06
1.3K0
JavaScript数据类型判断
instanceof可以用来判断对象是否是某个类的实例。instanceof的实现原理出门左转查看手撕instanceof
闲花手札
2021/09/08
1K0
相关推荐
谈谈 Object.prototype.toString 。
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验