【若川视野 x 源码共读】24期-Vue2源码中那些实用的基础工具函数

148 阅读2分钟

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

git vue2学习链接github.com/vuejs/vue/b…

若川学习链接juejin.cn/post/702427…

1、emptyObject

 var emptyObject = Object.freeze({});

Object.freeze({})对象冻结,不允许对象增加或删除新的元素,所以第一次没办法修改。

object.isFrozen() 判断是否冻结

2、isUndef 是否未定义

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

3、isDef 是否定义

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

4、isTrue是否为真

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

5、isFalse是否为假

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

6、isPrimitive 是否是原始值(基础类型)

  function isPrimitive (value) {
    return (
      typeof value === 'string' ||
      typeof value === 'number' ||
      // $flow-disable-line
      typeof value === 'symbol' ||
      typeof value === 'boolean'
    )
  }

7、isObject 是否为对象

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

8、toRawType 转换原始类型

  var _toString = Object.prototype.toString;  //判断对象类型

  function toRawType (value) {
    return _toString.call(value).slice(8, -1)   //-1 字符串倒数第一个字
  }
 //得到的对象类型  [object string]
  例如:toRawType('') // 'String'

9、isPlainObject 是否是纯对象

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

10、isRegExp 是否是正则对象

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

11、isValidArrayIndex 是否是可用的下标索引值

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

12、是否是promise对象

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

13、toString 转成字符串

 function toString (val) {
    return val == null
      ? ''
      : Array.isArray(val) || (isPlainObject(val) && val.toString === _toString)
        ? JSON.stringify(val, null, 2)  //上层会比下层缩放2个单位
        : String(val)
  }
// console.log(toString(['a', 'b']))  //没有2正常打印["a","b"]
//有2时打印
//[
//     "a",
//     "b"
//   ]

14、toNumber转成数字

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

parseFloat('a') //NaN

parseFloat('1a') //1

parseFloat('a1') //NaN

parseFloat('1') //NaN

15、makeMap生成map

  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]; }
  }
  //Object.create(null); 创建没有原型的空对象
  //makeMap('111111,222,22')('22') //true
  //map { '22': true, '222': true, '111111': true }

16、isBuiltInTag是否时内置的标签

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

17、isReservedAttribute 是否是保留的属性

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

18、remove 删除数组中某一项

  function remove (arr, item) {
    if (arr.length) {
      var index = arr.indexOf(item);
      if (index > -1) {
        return arr.splice(index, 1)
      }
    }
  }

19、hasOwn 对象是否有自己的属性

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

20、cached 缓存

 function cached (fn) {
    var cache = Object.create(null);
    return (function cachedFn (str) {
      var hit = cache[str];
      return hit || (cache[str] = fn(str))
    })
  }
  利用闭包的原理

21、camelize 连转符转换小驼峰命名

  var camelizeRE = /-(\w)/g;  //匹配字母或数字或下划线或汉字 
  var camelize = cached(function (str) {
    return str.replace(camelizeRE, function (_, c) { return c ? c.toUpperCase() : ''; })
  });
  //on-click  =>onClick
//replace _后第一个参数是捕捉到的内容,第二个数匹配的内容的下标,第三个是原始数据
//var str = 'kkk-yyy-hhh'
 str.replace(camelizeRE, function (_, a, b, c) {
  //第一个参数是捕捉到的内容,第二个数匹配的内容的下标,第三个是原始数据
  console.log(a, b, c, c ? c.toUpperCase() : '')
  return a ? a.toUpperCase() : ''
})
console.log(camelize)
// y 3 kkk-yyy-hhh KKK-YYY-HHH
// h 7 kkk-yyy-hhh KKK-YYY-HHH
// kkkKKK-YYY-HHHyyKKK-YYY-HHHhh

22、capitalize 首字母大写

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

23、hyphenate 小驼峰转连字符

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

24、polyfillBind nativeBind兼容了老版本浏览器不支持原生的 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;

对于多参数 使用applay

25、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
  } 
  //fn(1,2,3,4,5);   [1, 2, 3, 4, 5]

26、extend 合并

  function extend (to, _from) {
    for (var key in _from) {
      to[key] = _from[key];
    }
    return to
  }
const data = { name: '1' };
const data2 = extend(data, { mp: '12', name: '11' });
console.log(data); // { name: "11", mp: "12" }

27、toObject 数组转换为对象

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

28、noop返回空对象

  function noop (a, b, c) {}

29、no 返回false

  var no = function (a, b, c) { return false; };

30、identity 返回参数本身

 var identity = function (_) { return _; };

31、genStaticKeys 生成静态属性

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

32、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 {
          /* istanbul ignore next */
          return false
        }
      } catch (e) {
        /* istanbul ignore next */
        return false
      }
    } else if (!isObjectA && !isObjectB) {
      return String(a) === String(b)
    } else {
      return false
    }
  }

33、looseIndexOf 宽松相等的indexOf

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

//宽松相等的indexOf 。原生的 indexOf 是严格相等。

35、once 函数只执行一次

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

36、生命周期、属性等

var SSR_ATTR = 'data-server-rendered';

  var ASSET_TYPES = [
    'component',
    'directive',
    'filter'
  ];

  var LIFECYCLE_HOOKS = [
    'beforeCreate',
    'created',
    'beforeMount',
    'mounted',
    'beforeUpdate',
    'updated',
    'beforeDestroy',
    'destroyed',
    'activated',
    'deactivated',
    'errorCaptured',
    'serverPrefetch'
  ];