重点关注的函数

cached

缓存函数

looseEqual

判断两个值是否全等

once

确保该函数只调用一次

补充说明

makeMap

这个函数是用来将字符串查找变成对象取值,
虽然增加一个步骤,但是缩减了代码
name1,name2
[name1,name2]
{name1: true, name2: true}
字符代码少,数组性能差,对象代码多,性能好
这个函数就是平衡性能和代码量的产物

  1. /* @flow */
  2. // Object.freeze()阻止修改现有属性的特性和值,并阻止添加新属性。
  3. export const emptyObject = Object.freeze({})
  4. // These helpers produce better VM code in JS engines due to their
  5. // explicitness and function inlining.
  6. //判断数据 是否是undefined或者null
  7. export function isUndef (v: any): boolean %checks {
  8. return v === undefined || v === null
  9. }
  10. //判断数据 是否不等于 undefined或者null
  11. export function isDef (v: any): boolean %checks {
  12. return v !== undefined && v !== null
  13. }
  14. //判断是否真的等于true
  15. export function isTrue (v: any): boolean %checks {
  16. return v === true
  17. }
  18. // 判断是否是false
  19. export function isFalse (v: any): boolean %checks {
  20. return v === false
  21. }
  22. /**
  23. * Check if value is primitive.
  24. * 判断数据类型是否是string,number,symbol,boolean
  25. */
  26. export function isPrimitive (value: any): boolean %checks {
  27. return (
  28. typeof value === 'string' ||
  29. typeof value === 'number' ||
  30. // $flow-disable-line
  31. typeof value === 'symbol' ||
  32. typeof value === 'boolean'
  33. )
  34. }
  35. /**
  36. * Quick object check - this is primarily used to tell
  37. * Objects from primitive values when we know the value
  38. * is a JSON-compliant type.
  39. * 快速对象检查-当我们知道值是符合JSON的类型时,
  40. * 它主要用于区分对象和原始值
  41. */
  42. //判断是否是对象
  43. export function isObject (obj: mixed): boolean %checks {
  44. return obj !== null && typeof obj === 'object'
  45. }
  46. /**
  47. * Get the raw type string of a value, e.g., [object Object].
  48. */
  49. //获取toString 简写
  50. const _toString = Object.prototype.toString
  51. //类型判断 返会Array ,Function,String,Object,Re 等
  52. export function toRawType (value: any): string {
  53. return _toString.call(value).slice(8, -1)
  54. }
  55. /**
  56. * Strict object type check. Only returns true
  57. * for plain JavaScript objects.
  58. */
  59. //判断是否是对象
  60. export function isPlainObject (obj: any): boolean {
  61. return _toString.call(obj) === '[object Object]'
  62. }
  63. //判断是否是正则对象
  64. export function isRegExp (v: any): boolean {
  65. return _toString.call(v) === '[object RegExp]'
  66. }
  67. /**
  68. * Check if val is a valid array index.
  69. * 检查数值是否是有效的数组索引
  70. */
  71. export function isValidArrayIndex (val: any): boolean {
  72. // Math.floor 向下取整
  73. // isFinite 如果 number 是有限数字(或可转换为有限数字),那么返回 true。
  74. // 否则,如果 number 是 NaN(非数字),或者是正、负无穷大的数,则返回 false。
  75. // 也就是说n要是大于0的整数,不为正负无穷大
  76. const n = parseFloat(String(val))
  77. return n >= 0 && Math.floor(n) === n && isFinite(val)
  78. }
  79. // 判断是否为promise对象
  80. export function isPromise (val: any): boolean {
  81. return (
  82. isDef(val) &&
  83. typeof val.then === 'function' &&
  84. typeof val.catch === 'function'
  85. )
  86. }
  87. /**
  88. * Convert a value to a string that is actually rendered.
  89. * 将对象或者其他基本数据 变成一个 字符串
  90. */
  91. /*
  92. value
  93. 将要序列化成 一个 JSON 字符串的值。
  94. replacer 可选
  95. 如果该参数是一个函数,则在序列化过程中,被序列化的值的每个属性都会经过该函数的转换和处理;
  96. 如果该参数是一个数组,则只有包含在这个数组中的属性名才会被序列化到最终的 JSON 字符串中;
  97. 如果该参数为 null 或者未提供,则对象所有的属性都会被序列化;
  98. space 可选
  99. 指定缩进用的空白字符串,用于美化输出(pretty-print);
  100. 如果参数是个数字,它代表有多少的空格;上限为10。该值若小于1,则意味着没有空格;
  101. 如果该参数为字符串(当字符串长度超过10个字母,取其前10个字母),该字符串将被作为空格;
  102. 如果该参数没有提供(或者为 null),将没有空格。
  103. */
  104. export function toString (val: any): string {
  105. return val == null
  106. ? ''
  107. : Array.isArray(val) || (isPlainObject(val) && val.toString === _toString)
  108. ? JSON.stringify(val, null, 2)
  109. : String(val)
  110. }
  111. /**
  112. * Convert an input value to a number for persistence.
  113. * If the conversion fails, return original string.
  114. * 字符串转数字,如果失败则返回字符串
  115. */
  116. export function toNumber (val: string): number | string {
  117. const n = parseFloat(val)
  118. return isNaN(n) ? val : n
  119. }
  120. /**
  121. * Make a map and return a function for checking if a key
  122. * is in that map.
  123. * map 对象中的[name1,name2]
  124. * 变成这样的map{name1:true,name2:true},
  125. * 传入一个key值就能判断是否该key值是否存在
  126. */
  127. export function makeMap (
  128. str: string,
  129. expectsLowerCase?: boolean
  130. ): (key: string) => true | void {
  131. const map = Object.create(null)
  132. const list: Array<string> = str.split(',')
  133. for (let i = 0; i < list.length; i++) {
  134. map[list[i]] = true
  135. }
  136. return expectsLowerCase
  137. ? val => map[val.toLowerCase()]
  138. : val => map[val]
  139. }
  140. /**
  141. * Check if a tag is a built-in tag.
  142. * 检查标记是否为内置标记。
  143. */
  144. export const isBuiltInTag = makeMap('slot,component', true)
  145. /**
  146. * Check if an attribute is a reserved attribute.
  147. * 检查属性是否为保留属性。
  148. */
  149. export const isReservedAttribute = makeMap('key,ref,slot,slot-scope,is')
  150. /**
  151. * Remove an item from an array.
  152. * 删除数组
  153. */
  154. export function remove (arr: Array<any>, item: any): Array<any> | void {
  155. if (arr.length) {
  156. const index = arr.indexOf(item)
  157. if (index > -1) {
  158. return arr.splice(index, 1)
  159. }
  160. }
  161. }
  162. /**
  163. * Check whether an object has the property.
  164. * 检查对象属性是否是实例化还是原型上面的
  165. */
  166. const hasOwnProperty = Object.prototype.hasOwnProperty
  167. export function hasOwn (obj: Object | Array<*>, key: string): boolean {
  168. return hasOwnProperty.call(obj, key)
  169. }
  170. /**
  171. * Create a cached version of a pure function.
  172. * 创建纯函数的缓存版本。
  173. * 创建一个函数,缓存到闭包里,再返回柯里化函数
  174. * 这个函数的作用就是当传入第二次传入同一个值的时候,
  175. * 直接返回已经缓存的函数结果
  176. */
  177. export function cached<F: Function> (fn: F): F {
  178. const cache = Object.create(null)
  179. return (function cachedFn (str: string) {
  180. const hit = cache[str]
  181. return hit || (cache[str] = fn(str))
  182. }: any)
  183. }
  184. /**
  185. * Camelize a hyphen-delimited string.
  186. * 用连字符分隔的字符串变为驼峰式
  187. */
  188. const camelizeRE = /-(\w)/g
  189. export const camelize = cached((str: string): string => {
  190. return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : '')
  191. })
  192. /**
  193. * Capitalize a string.
  194. * 将首字母变成大写
  195. */
  196. export const capitalize = cached((str: string): string => {
  197. return str.charAt(0).toUpperCase() + str.slice(1)
  198. })
  199. /**
  200. * Hyphenate a camelCase string.
  201. * \B是非单词分界符,即可以查出是否包含某个字,
  202. * 如“ABCDEFGHIJK”中是否包含“BCDEFGHIJK”这个字。
  203. */
  204. const hyphenateRE = /\B([A-Z])/g
  205. export const hyphenate = cached((str: string): string => {
  206. //大写字母,加完减号又转成小写了 比如把驼峰 aBc 变成了 a-bc
  207. //匹配大写字母并且两面不是空白的 替换成 '-' + '字母' 再全部转换成小写
  208. return str.replace(hyphenateRE, '-$1').toLowerCase()
  209. })
  210. /**
  211. * Simple bind polyfill for environments that do not support it,
  212. * e.g., PhantomJS 1.x. Technically, we don't need this anymore
  213. * since native bind is now performant enough in most browsers.
  214. * But removing it would mean breaking code that was able to run in
  215. * PhantomJS 1.x, so this must be kept for backward compatibility.
  216. * 简单绑定polyfill适用于不支持它的环境,例如PhantomJS 1.x。
  217. * 从技术上讲,我们不再需要它了,因为本机绑定现在在大多数浏览器中已经有足够的性能了。
  218. * 但是删除它将意味着破坏能够在PhantomJS 1.x中运行的代码,
  219. * 因此必须保留这些代码以实现向后兼容。
  220. */
  221. /* istanbul ignore next */
  222. function polyfillBind (fn: Function, ctx: Object): Function {
  223. function boundFn (a) {
  224. const l = arguments.length
  225. return l
  226. ? l > 1
  227. ? fn.apply(ctx, arguments)
  228. : fn.call(ctx, a)
  229. : fn.call(ctx)
  230. }
  231. boundFn._length = fn.length
  232. return boundFn
  233. }
  234. function nativeBind (fn: Function, ctx: Object): Function {
  235. return fn.bind(ctx)
  236. }
  237. export const bind = Function.prototype.bind
  238. ? nativeBind
  239. : polyfillBind
  240. /**
  241. * Convert an Array-like object to a real Array.
  242. * 将类数组转换成真的数组
  243. */
  244. export function toArray (list: any, start?: number): Array<any> {
  245. start = start || 0
  246. let i = list.length - start
  247. const ret: Array<any> = new Array(i)
  248. while (i--) {
  249. ret[i] = list[i + start]
  250. }
  251. return ret
  252. }
  253. /**
  254. * Mix properties into target object.
  255. * 浅拷贝
  256. * 对象浅拷贝,参数(to, _from)循环_from的值,会覆盖掉to的值
  257. */
  258. export function extend (to: Object, _from: ?Object): Object {
  259. for (const key in _from) {
  260. to[key] = _from[key]
  261. }
  262. return to
  263. }
  264. /**
  265. * Merge an Array of Objects into a single Object.
  266. * 合并对象数组合并成一个对象
  267. */
  268. export function toObject (arr: Array<any>): Object {
  269. const res = {}
  270. for (let i = 0; i < arr.length; i++) {
  271. if (arr[i]) {
  272. extend(res, arr[i])
  273. }
  274. }
  275. return res
  276. }
  277. /* eslint-disable no-unused-vars */
  278. /**
  279. * Perform no operation.
  280. * Stubbing args to make Flow happy without leaving useless transpiled code
  281. * with ...rest (https://flow.org/blog/2017/05/07/Strict-Function-Call-Arity/).
  282. * 不执行任何操作。在不留下无用的透明代码和…rest的情况下,存根参数使flow愉快
  283. */
  284. export function noop (a?: any, b?: any, c?: any) {}
  285. /**
  286. * Always return false.
  287. * 返回假
  288. */
  289. export const no = (a?: any, b?: any, c?: any) => false
  290. /* eslint-enable no-unused-vars */
  291. /**
  292. * Return the same value.
  293. * 返回相同值
  294. */
  295. export const identity = (_: any) => _
  296. /**
  297. * Generate a string containing static keys from compiler modules.
  298. * [{ staticKeys:1},{staticKeys:2},{staticKeys:3}]
  299. * 连接数组对象中的 staticKeys key值,连接成一个字符串 str=‘1,2,3’
  300. */
  301. export function genStaticKeys (modules: Array<ModuleOptions>): string {
  302. return modules.reduce((keys, m) => {
  303. //累加staticKeys的值变成数组
  304. return keys.concat(m.staticKeys || [])
  305. }, []).join(',')//转换成字符串
  306. }
  307. /**
  308. * Check if two values are loosely equal - that is,
  309. * if they are plain objects, do they have the same shape?
  310. * 检测a和b的数据类型,是否是不是数组或者对象,
  311. * 对象的key长度一样即可,数组长度一样即可
  312. */
  313. export function looseEqual (a: any, b: any): boolean {
  314. //如果a和b是完全相等 则true
  315. if (a === b) return true
  316. const isObjectA = isObject(a)
  317. const isObjectB = isObject(b)
  318. if (isObjectA && isObjectB) { //如果a和都是对象则让下走
  319. try {
  320. const isArrayA = Array.isArray(a)
  321. const isArrayB = Array.isArray(b)
  322. if (isArrayA && isArrayB) { //如果a和b都是数组
  323. return a.length === b.length && a.every((e, i) => { //如果a长度和b长度一样的时候
  324. return looseEqual(e, b[i]) //递归
  325. })
  326. } else if (a instanceof Date && b instanceof Date) { //或者a和b是日期类型
  327. return a.getTime() === b.getTime() //获取毫秒数来判断是否相等
  328. } else if (!isArrayA && !isArrayB) { //或者a和b都不是数组
  329. const keysA = Object.keys(a) // 获取到a的key值 变成一个数组
  330. const keysB = Object.keys(b) // 获取到b的key值 变成一个数组
  331. return keysA.length === keysB.length && keysA.every(key => { //他们的对象key值长度是一样的时候 则加载every 条件函数
  332. return looseEqual(a[key], b[key]) //递归
  333. })
  334. } else {
  335. /* istanbul ignore next */
  336. return false
  337. }
  338. } catch (e) {
  339. /* istanbul ignore next */
  340. return false
  341. }
  342. } else if (!isObjectA && !isObjectB) { //b和a 都不是对象的时候
  343. return String(a) === String(b) //把a和b变成字符串,判断他们是否相同
  344. } else {
  345. return false
  346. }
  347. }
  348. /**
  349. * Return the first index at which a loosely equal value can be
  350. * found in the array (if value is a plain object, the array must
  351. * contain an object of the same shape), or -1 if it is not present.
  352. * 返回第一个索引,在该索引处可以在数组中找到大致相等的值
  353. * 返回第一个索引,在该索引处可以在数组中找到大致相等的值
  354. * (如果值是普通对象,则数组必须包含全等的对象),
  355. * 如果不存在则返回-1。
  356. */
  357. export function looseIndexOf (arr: Array<mixed>, val: mixed): number {
  358. for (let i = 0; i < arr.length; i++) {
  359. if (looseEqual(arr[i], val)) return i
  360. }
  361. return -1
  362. }
  363. /**
  364. * Ensure a function is called only once.
  365. * 确保该函数只调用一次 闭包函数
  366. */
  367. export function once (fn: Function): Function {
  368. let called = false
  369. return function () {
  370. if (!called) {
  371. called = true
  372. fn.apply(this, arguments)
  373. }
  374. }
  375. }