【若川视野 x 源码共读】第24期 | vue2 工具函数

157 阅读3分钟

本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。

本文简单将vue2的工具函数简单整理了一下,然后进行了一个分类,希望可以帮助到大家

判断类型

isObject 是否是对象

function isObject (obj) {
  return obj !== null && typeof obj === 'object'
}

isPlainObject 是否是原始对象

function isPlainObject (obj) {
  return Object.prototype.toString.call(obj) === '[object Object]'
}

isRegExp 是否是正则表达式

function isRegExp (v) {
  return Object.prototype.toString.call(v) === '[object RegExp]'
}

isValidArrayIndex 是否是有效的数组索引

function isValidArrayIndex (val) {
  var n = parseFloat(String(val));
  return n >= 0 && Math.floor(n) === n && isFinite(val)
}

isPromise 是否是promise

function isPromise (val) {
  return (
    isDef(val) &&
    typeof val.then === 'function' &&
    typeof val.catch === 'function'
  )
}

比较值

isUndef 是否是未定义

function isUndef (v) {
  return v === undefined || v === null
}

isDef 是否已定义

function isDef (v) {
  return v !== undefined && v !== null
}

isTrue 是否是true

function isTrue (v) {
  return v === true
}

isFalse 是否是false

function isFalse (v) {
  return v === false
}

looseEqual 判断两个值是否相等

function looseEqual (a, b) {
  if (a === b) { return true }
  var isObjectA = isObject(a);
  var isObjectB = isObject(b);
  if (isObjectA && isObjectB) {
    try {
      var isArrayA = Array.isArray(a);
      var isArrayB = Array.isArray(b);
      if (isArrayA && isArrayB) {
        return a.length === b.length && a.every(function (e, i) {
          return looseEqual(e, b[i])
        })
      } else if (a instanceof Date && b instanceof Date) {
        return a.getTime() === b.getTime()
      } else if (!isArrayA && !isArrayB) {
        var keysA = Object.keys(a);
        var keysB = Object.keys(b);
        return keysA.length === keysB.length && keysA.every(function (key) {
          return looseEqual(a[key], b[key])
        })
      } else {
        return false
      }
    } catch (e) {
      return false
    }
  } else if (!isObjectA && !isObjectB) {
    return String(a) === String(b)
  } else {
    return false
  }
}

类型转换

toString 转为字符串

function toString (val) {
  return val == null ? '' : Array.isArray(val)
    || (isPlainObject(val) && val.toString === Object.prototype.toString)
    ? JSON.stringify(val, null, 2)
    : String(val)
}

toNumber 转为数值类型

function toNumber (val) {
  var n = parseFloat(val);
  return isNaN(n) ? val : n
}

toArray 转为数组

function toArray (list, start) {
  start = start || 0;
  var i = list.length - start;
  var ret = new Array(i);
  while (i--) {
    ret[i] = list[i + start];
  }
  return ret
}

toObject 转为对象

function toObject (arr) {
  var res = {};
  for (var i = 0; i < arr.length; i++) {
    if (arr[i]) {
      extend(res, arr[i]);
    }
  }
  return res
}

字符转换

camelize 小驼峰转连字符

var camelizeRE = /-(\w)/g;
var camelize = function (str) {
  return str.replace(camelizeRE, function (_, c) { return c ? c.toUpperCase() : ''; })
}

//例子
console.log(camelize('my-card'));  //my-card

capitalize 首字母转为大写

var capitalize = function (str) {
  return str.charAt(0).toUpperCase() + str.slice(1)
}

//例子
console.log(capitalize('abcd'));  //Abcd

hyphenate 小驼峰转连字符

var hyphenateRE = /\B([A-Z])/g;
var hyphenate = function (str) {
  return str.replace(hyphenateRE, '-$1').toLowerCase()
}

//例子
console.log(hyphenate('myCard'));  //my-card

浏览器兼容方法

polyfillBind 兼容bind方法

function polyfillBind (fn, ctx) {
  function boundFn (a) {
    var l = arguments.length;
    return l
      ? l > 1
        ? fn.apply(ctx, arguments)
        : fn.call(ctx, a)
      : fn.call(ctx)
  }


  boundFn._length = fn.length;
  return boundFn
}


function nativeBind (fn, ctx) {
  return fn.bind(ctx)
}


var bind = Function.prototype.bind
  ? nativeBind
  : polyfillBind;

模板方法相关

makeMap

function makeMap (
  str,
  expectsLowerCase
) {
  var map = Object.create(null);
  var list = str.split(',');
  for (var i = 0; i < list.length; i++) {
    map[list[i]] = true;
  }
  return expectsLowerCase
    ? function (val) { return map[val.toLowerCase()]; }
    : function (val) { return map[val]; }
}

个人感觉这个函数是在解析AST中,检测关键词时做性能优化用的,利用闭包的原理缓存一些关键词的名称,后面再进行二次封装进行检测

isBuiltInTag 是否是内置标签

var isBuiltInTag = makeMap('slot,component', true);

isReservedAttribute 是否是保留属性

var isReservedAttribute = makeMap('key,ref,slot,slot-scope,is');

其他函数

toRawType 获取原始类型

function toRawType (value) {
  return Object.prototype.toString.call(value).slice(8, -1)
}

hasOwn 检查对象是否具有该属性

function hasOwn (obj, key) {
  return Object.prototype.hasOwnProperty.call(obj, key)
}

looseIndexOf 获取数组第一个值匹配下标

function looseIndexOf (arr, val) {
  for (var i = 0; i < arr.length; i++) {
    if (looseEqual(arr[i], val)) { return i }
  }
  return -1
}

extend 扩展对象

function extend (to, _from) {
  for (var key in _from) {
    to[key] = _from[key];
  }
  return to
}

once 确保函数只调用一次

function once (fn) {
  var called = false;
  return function () {
    if (!called) {
      called = true;
      fn.apply(this, arguments);
    }
  }
}

cached 缓存函数

function cached (fn) {
  var cache = Object.create(null);
  return (function cachedFn (str) {
    var hit = cache[str];
    return hit || (cache[str] = fn(str))
  })
}

genStaticKeys 获取对象数组成员的静态属性(staticKeys)值组成的字符串

function genStaticKeys (modules) {
  return modules.reduce(function (keys, m) {
    return keys.concat(m.staticKeys || [])
  }, []).join(',')
}

//示例
genStaticKeys([
    {staticKeys:["1","a"]},
    {staticKeys:["2","b"]},
    {staticKeys:["3","c"]}
]) //'1,a,2,b,3,c'