VUE源码学习 - 01

118 阅读4分钟

公用工具方法 (src/shared/util.js)

emptyObject

/** 冻结{}对象 */
export const emptyObject = Object.freeze({});

isUndef、isDef、isTrue、isFalse

/** 是否未被定义,如果参数等于undefined或者为空,返回true */
export function isUndef(v) {
  return v === undefined || v === null;
}

/** 是否定义,如果参数不等于undefined或者为空,返回true */
export function isDef(v) {
  return v !== undefined && v !== null;
}

/** 是否真,参数为真时返回true */
export function isTrue(v) {
  return v === true;
}

/** 是否假,参数为假时返回true */
export function isFalse(v) {
  return v === false;
}

isPrimitive

/** 监测value类型是否为 string、number、symbol、boolean */
export function isPrimitive(value) {
  return (
    typeof value === 'string' ||
    typeof value === 'number' ||
    typeof value === 'symbol' ||
    typeof value === 'boolean'
  );
}

isObject

/** 是否为对象,如果对象不等于空且typeof返回为object,返回true */
export function isObject(obj) {
  return obj !== null && typeof obj === 'object';
}

_toString、toRawType、isPlainObject、isRegExp

/** 该方法返回描述某个对象数据类型的字符串,
 * 如自定义的对象没有被覆盖,则会返回“[object type]”,
 * 其中,type则是实际的对象类型。在使用该方法检测的时候,
 * 可以使用Object.prototype.toString.call()
 * 或者Object.prototype.toString.apply()进行测试 */
var _toString = Object.prototype.toString;

/** 返回value的类型 */
export function toRawType(value) {
  return _toString.call(value).slice(8, -1);
}

/** 判断指定参数是否是一个纯粹的对象 */
export function isPlainObject(obj) {
  return _toString.call(obj) === '[object Object]';
}

/** 是否是正则表达式 */
export function isRegExp(v) {
  return _toString.call(v) === '[object RegExp]';
}

isValidArrayIndex

/** 检查val是否是有效的数组索引值 */
export function isValidArrayIndex(val) {
  var n = parseFloat(String(val));
  return n >= 0 && Math.floor(n) === n && isFinite(val);
}

isPromise

/** val 是否是Promise函数 */
export function isPromise(val) {
  return isDef(val) && typeof val.then === 'function' && typeof val.catch === 'function';
}

toString、toNumber

/** 将val转换为实际呈现的字符串 */
export function toString(val) {
  let result;
  if (val == null) {
    result = '';
  } else {
    if (Array.isArray(val) || (isPlainObject(val) && val.toString === _toString)) {
      result = JSON.stringify(val, null, 2);
    } else {
      result = String(val);
    }
  }
  return result;

  // return val == null
  //   ? ''
  //   : Array.isArray(val) || (isPlainObject(val) && val.toString === _toString)
  //   ? JSON.stringify(val, null, 2)
  //   : String(val);
}

/** 将val转换为数字,否则返回val本身 */
export function toNumber(val) {
  var n = parseFloat(val);
  return isNaN(n) ? val : n;
}

makeMap、isBuiltInTag、isReservedAttribute

/**
 * 缓存一个键值对map, 返回一个检查val是否在这个键值对的函数(柯里化)
 * @param {*} str 用于缓存键值对的字符串
 * @param {*} expectsLowerCase 是否将返回函数的参数小写化
 */
export 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];
      };
}

/**检查一个标签是否是 slot 或者 component
 * @param {*} val 要检查的标签名(函数内处理时将val小写化)
 */
export var isBuiltInTag = makeMap('slot,component', true);

/**检查属性是否是保留属性(key,ref,slot,slot-scope,is)
 * @param {*} val 要检查的属性名
 */
export var isReservedAttribute = makeMap('key,ref,slot,slot-scope,is');

remove

/** 从数组arr中删除第一个出现的item */
export function remove(arr, item) {
  if (arr.length) {
    var index = arr.indexOf(item);
    if (index > -1) {
      return arr.splice(index, 1);
    }
  }
}

hasOwn

var hasOwnProperty = Object.prototype.hasOwnProperty;
/** 检查obj是否具有key对应的属性 */
export function hasOwn(obj, key) {
  return hasOwnProperty.call(obj, key);
}

cached、camelize、capitalize、hyphenate

/** 创建纯函数的缓存。(柯里化) */
export function cached(fn) {
  var cache = Object.create(null);
  return function cachedFn(str) {
    return cache[str] || (cache[str] = fn(str));
  };
}

/** 将 str 驼峰化 */
export var camelize = cached(function (str) {
  return str.replace(/-(\w)/g, function (_, c) {
    return c ? c.toUpperCase() : '';
  });
});

/** 将 str 首字母大写 */
export var capitalize = cached(function (str) {
  return str.charAt(0).toUpperCase() + str.slice(1);
});

const hyphenateRE = /\B([A-Z])/g;
/** 将驼峰 str 转换为 - 字符 */
export var hyphenate = cached(function (str) {
  return str.replace(/\B([A-Z])/g, '-$1').toLowerCase();
});

polyfillBind、nativeBind

/** bind方法的polyfill(为了兼容性) */
function polyfillBind(fn, ctx) {
  function boundFn(a) {
    var l = arguments.length;
    let result;
    if (l > 1) result = fn.apply(ctx, arguments);
    else if (l === 1) result = fn.call(ctx, a);
    else result = fn.call(ctx);

    return result;
  }

  boundFn._length = fn.length;
  return boundFn;
}

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

/** 判定函数bind是原生的还是polyfill */
export var bind = Function.prototype.bind ? nativeBind : polyfillBind;

toArray

/**
 *  将类数组转换为真正的数组
 * @param {*} list 要转换的类数组
 * @param {*} start 开始位置
 */
export 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;
}

extend

/** 将 _from 中的属性混合到 to中 */
export function extend(to, _from) {
  for (var key in _from) {
    to[key] = _from[key];
  }
  return to;
}

toObject

/**将对象数组合并为单个对象 */
export function toObject(arr) {
  var res = {};
  for (var i = 0; i < arr.length; i++) {
    if (arr[i]) {
      extend(res, arr[i]);
    }
  }
  return res;
}

noop、no、identify

/** 应该是为了Flow类型检测用的代码 */

export function noop(a, b, c) {}

/** 始终返回 false */
export var no = function (a, b, c) {
  return false;
};

/** 传入什么返回什么 */
export var identity = function (_) {
  return _;
};

genStaticKeys

/** 从编译器模块生成包含静态键的字符串,
 * 将 modules 数组每个元素的 staticKeys 用","拼接起来
 */
export function genStaticKeys(modules) {
  return modules
    .reduce(function (keys, m) {
      return keys.concat(m.staticKeys || []);
    }, [])
    .join(',');
}

looseEqual、looseIndexOf

/** 判断两个参数是否相等,也可以判断数组和对象是否相等 */
export function looseEqual(a, b) {
  //如果:参数a和参数b恒等于返回true;
  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) {
        //a和b为数组,且元素相同
        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) {
        // a和b是否为普通对象,且键值相同
        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;
  }
}

/** 返回arr数组中值等于val的第一个元素的下标 */
export function looseIndexOf(arr, val) {
  for (var i = 0; i < arr.length; i++) {
    if (looseEqual(arr[i], val)) {
      return i;
    }
  }
  return -1;
}

once