1. /* @flow */
    2. export const emptyObject = Object.freeze({})
    3. // These helpers produce better VM code in JS engines due to their
    4. // explicitness and function inlining.
    5. export function isUndef (v: any): boolean %checks {
    6. return v === undefined || v === null
    7. }
    8. export function isDef (v: any): boolean %checks {
    9. return v !== undefined && v !== null
    10. }
    11. export function isTrue (v: any): boolean %checks {
    12. return v === true
    13. }
    14. export function isFalse (v: any): boolean %checks {
    15. return v === false
    16. }
    17. /**
    18. * Check if value is primitive.
    19. */
    20. export function isPrimitive (value: any): boolean %checks {
    21. return (
    22. typeof value === 'string' ||
    23. typeof value === 'number' ||
    24. // $flow-disable-line
    25. typeof value === 'symbol' ||
    26. typeof value === 'boolean'
    27. )
    28. }
    29. /**
    30. * Quick object check - this is primarily used to tell
    31. * Objects from primitive values when we know the value
    32. * is a JSON-compliant type.
    33. */
    34. export function isObject (obj: mixed): boolean %checks {
    35. return obj !== null && typeof obj === 'object'
    36. }
    37. /**
    38. * Get the raw type string of a value, e.g., [object Object].
    39. */
    40. const _toString = Object.prototype.toString
    41. export function toRawType (value: any): string {
    42. return _toString.call(value).slice(8, -1)
    43. }
    44. /**
    45. * Strict object type check. Only returns true
    46. * for plain JavaScript objects.
    47. * 严格的对象类型检查。仅对普通JavaScript对象返回true。
    48. */
    49. export function isPlainObject (obj: any): boolean {
    50. return _toString.call(obj) === '[object Object]'
    51. }
    52. export function isRegExp (v: any): boolean {
    53. return _toString.call(v) === '[object RegExp]'
    54. }
    55. /**
    56. * Check if val is a valid array index.
    57. */
    58. export function isValidArrayIndex (val: any): boolean {
    59. const n = parseFloat(String(val))
    60. return n >= 0 && Math.floor(n) === n && isFinite(val)
    61. }
    62. export function isPromise (val: any): boolean {
    63. return (
    64. isDef(val) &&
    65. typeof val.then === 'function' &&
    66. typeof val.catch === 'function'
    67. )
    68. }
    69. /**
    70. * Convert a value to a string that is actually rendered.
    71. */
    72. export function toString (val: any): string {
    73. return val == null
    74. ? ''
    75. : Array.isArray(val) || (isPlainObject(val) && val.toString === _toString)
    76. ? JSON.stringify(val, null, 2)
    77. : String(val)
    78. }
    79. /**
    80. * Convert an input value to a number for persistence.
    81. * If the conversion fails, return original string.
    82. */
    83. export function toNumber (val: string): number | string {
    84. const n = parseFloat(val)
    85. return isNaN(n) ? val : n
    86. }
    87. /**
    88. * Make a map and return a function for checking if a key
    89. * is in that map.
    90. */
    91. export function makeMap (
    92. str: string,
    93. expectsLowerCase?: boolean
    94. ): (key: string) => true | void {
    95. const map = Object.create(null)
    96. const list: Array<string> = str.split(',')
    97. for (let i = 0; i < list.length; i++) {
    98. map[list[i]] = true
    99. }
    100. return expectsLowerCase
    101. ? val => map[val.toLowerCase()]
    102. : val => map[val]
    103. }
    104. /**
    105. * Check if a tag is a built-in tag.
    106. */
    107. export const isBuiltInTag = makeMap('slot,component', true)
    108. /**
    109. * Check if an attribute is a reserved attribute.
    110. */
    111. export const isReservedAttribute = makeMap('key,ref,slot,slot-scope,is')
    112. /**
    113. * Remove an item from an array.
    114. */
    115. export function remove (arr: Array<any>, item: any): Array<any> | void {
    116. if (arr.length) {
    117. const index = arr.indexOf(item)
    118. if (index > -1) {
    119. return arr.splice(index, 1)
    120. }
    121. }
    122. }
    123. /**
    124. * Check whether an object has the property.
    125. */
    126. const hasOwnProperty = Object.prototype.hasOwnProperty
    127. export function hasOwn (obj: Object | Array<*>, key: string): boolean {
    128. return hasOwnProperty.call(obj, key)
    129. }
    130. /**
    131. * Create a cached version of a pure function.
    132. */
    133. export function cached<F: Function> (fn: F): F {
    134. const cache = Object.create(null)
    135. return (function cachedFn (str: string) {
    136. const hit = cache[str]
    137. return hit || (cache[str] = fn(str))
    138. }: any)
    139. }
    140. /**
    141. * Camelize a hyphen-delimited string.
    142. */
    143. const camelizeRE = /-(\w)/g
    144. export const camelize = cached((str: string): string => {
    145. return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : '')
    146. })
    147. /**
    148. * Capitalize a string.
    149. */
    150. export const capitalize = cached((str: string): string => {
    151. return str.charAt(0).toUpperCase() + str.slice(1)
    152. })
    153. /**
    154. * Hyphenate a camelCase string.
    155. */
    156. const hyphenateRE = /\B([A-Z])/g
    157. export const hyphenate = cached((str: string): string => {
    158. return str.replace(hyphenateRE, '-$1').toLowerCase()
    159. })
    160. /**
    161. * Simple bind polyfill for environments that do not support it,
    162. * e.g., PhantomJS 1.x. Technically, we don't need this anymore
    163. * since native bind is now performant enough in most browsers.
    164. * But removing it would mean breaking code that was able to run in
    165. * PhantomJS 1.x, so this must be kept for backward compatibility.
    166. */
    167. /* istanbul ignore next */
    168. function polyfillBind (fn: Function, ctx: Object): Function {
    169. function boundFn (a) {
    170. const l = arguments.length
    171. return l
    172. ? l > 1
    173. ? fn.apply(ctx, arguments)
    174. : fn.call(ctx, a)
    175. : fn.call(ctx)
    176. }
    177. boundFn._length = fn.length
    178. return boundFn
    179. }
    180. function nativeBind (fn: Function, ctx: Object): Function {
    181. return fn.bind(ctx)
    182. }
    183. export const bind = Function.prototype.bind
    184. ? nativeBind
    185. : polyfillBind
    186. /**
    187. * Convert an Array-like object to a real Array.
    188. */
    189. export function toArray (list: any, start?: number): Array<any> {
    190. start = start || 0
    191. let i = list.length - start
    192. const ret: Array<any> = new Array(i)
    193. while (i--) {
    194. ret[i] = list[i + start]
    195. }
    196. return ret
    197. }
    198. /**
    199. * Mix properties into target object.
    200. */
    201. export function extend (to: Object, _from: ?Object): Object {
    202. for (const key in _from) {
    203. to[key] = _from[key]
    204. }
    205. return to
    206. }
    207. /**
    208. * Merge an Array of Objects into a single Object.
    209. */
    210. export function toObject (arr: Array<any>): Object {
    211. const res = {}
    212. for (let i = 0; i < arr.length; i++) {
    213. if (arr[i]) {
    214. extend(res, arr[i])
    215. }
    216. }
    217. return res
    218. }
    219. /* eslint-disable no-unused-vars */
    220. /**
    221. * Perform no operation.
    222. * Stubbing args to make Flow happy without leaving useless transpiled code
    223. * with ...rest (https://flow.org/blog/2017/05/07/Strict-Function-Call-Arity/).
    224. */
    225. export function noop (a?: any, b?: any, c?: any) {}
    226. /**
    227. * Always return false.
    228. */
    229. export const no = (a?: any, b?: any, c?: any) => false
    230. /* eslint-enable no-unused-vars */
    231. /**
    232. * Return the same value.
    233. */
    234. export const identity = (_: any) => _
    235. /**
    236. * Generate a string containing static keys from compiler modules.
    237. */
    238. export function genStaticKeys (modules: Array<ModuleOptions>): string {
    239. return modules.reduce((keys, m) => {
    240. return keys.concat(m.staticKeys || [])
    241. }, []).join(',')
    242. }
    243. /**
    244. * Check if two values are loosely equal - that is,
    245. * if they are plain objects, do they have the same shape?
    246. */
    247. export function looseEqual (a: any, b: any): boolean {
    248. if (a === b) return true
    249. const isObjectA = isObject(a)
    250. const isObjectB = isObject(b)
    251. if (isObjectA && isObjectB) {
    252. try {
    253. const isArrayA = Array.isArray(a)
    254. const isArrayB = Array.isArray(b)
    255. if (isArrayA && isArrayB) {
    256. return a.length === b.length && a.every((e, i) => {
    257. return looseEqual(e, b[i])
    258. })
    259. } else if (a instanceof Date && b instanceof Date) {
    260. return a.getTime() === b.getTime()
    261. } else if (!isArrayA && !isArrayB) {
    262. const keysA = Object.keys(a)
    263. const keysB = Object.keys(b)
    264. return keysA.length === keysB.length && keysA.every(key => {
    265. return looseEqual(a[key], b[key])
    266. })
    267. } else {
    268. /* istanbul ignore next */
    269. return false
    270. }
    271. } catch (e) {
    272. /* istanbul ignore next */
    273. return false
    274. }
    275. } else if (!isObjectA && !isObjectB) {
    276. return String(a) === String(b)
    277. } else {
    278. return false
    279. }
    280. }
    281. /**
    282. * Return the first index at which a loosely equal value can be
    283. * found in the array (if value is a plain object, the array must
    284. * contain an object of the same shape), or -1 if it is not present.
    285. */
    286. export function looseIndexOf (arr: Array<mixed>, val: mixed): number {
    287. for (let i = 0; i < arr.length; i++) {
    288. if (looseEqual(arr[i], val)) return i
    289. }
    290. return -1
    291. }
    292. /**
    293. * Ensure a function is called only once.
    294. */
    295. export function once (fn: Function): Function {
    296. let called = false
    297. return function () {
    298. if (!called) {
    299. called = true
    300. fn.apply(this, arguments)
    301. }
    302. }
    303. }