前言
- 本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。
- 这是源码共读的第24期,链接:第24期 | vue2工具函数
学习目标
源码地址: vue/src/shared
打包后的工具函数:github.com/vuejs/vue/b…
工具函数
在线查看源码地址vue/util
由于源码使用了了Flow类型,下文仅贴出打包后的代码
1. emptyObject 冻结对象
var emptyObject = Object.freeze({});
Object.isFrozen(emptyObject) // true
1.1 Object.freeze
Object.freeze() 方法可以冻结一个对象。一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。此外,冻结一个对象后该对象的原型也不能被修改。freeze() 返回和传入的参数相同的对象。
1.2 Object.isFrozen
Object.isFrozen()方法判断一个对象是否被冻结。
2. isUndef 是否是未定义
function isUndef(v) {
return v === undefined || v === null;
}
3. isDef 是否是已经定义
function isDef(v) {
return v !== undefined && v !== null;
}
4. isTrue 是否是true
function isTrue(v) {
return v === true;
}
5. isFalse 是否是false
function isFalse(v) {
return v === false;
}
6. isPrimitive 判断字符串值是否是原始值
/**
* Check if value is primitive.
*/
function isPrimitive(value) {
return (
typeof value === "string" ||
typeof value === "number" ||
// $flow-disable-line
typeof value === "symbol" ||
typeof value === "boolean"
);
}
7. isObject 是否对象
/**
* Quick object check - this is primarily used to tell
* Objects from primitive values when we know the value
* is a JSON-compliant type.
*/
function isObject(obj) {
return obj !== null && typeof obj === "object";
}
8. toRawType 转换成原始类型
/**
* Get the raw type string of a value, e.g., [object Object].
*/
var _toString = Object.prototype.toString;
function toRawType(value) {
return _toString.call(value).slice(8, -1);
}
利用Object.prototype.toString 检测类型对象并截取类型
9. isPlainObject 是否是纯纯的对象
/**
* Strict object type check. Only returns true
* for plain JavaScript objects.
*/
function isPlainObject(obj) {
return _toString.call(obj) === "[object Object]";
}
10. isRegExp 是否是正则表达式
function isRegExp(v) {
return _toString.call(v) === "[object RegExp]";
}
11. isValidArrayIndex 是否是可用的数组索引值
/**
* Check if val is a valid array index.
*/
function isValidArrayIndex(val) {
var n = parseFloat(String(val));
return n >= 0 && Math.floor(n) === n && isFinite(val);
}
isFinite 函数用来判断被传入的参数值是否为一个有限数值(finite number)。在必要情况下,参数会首先转为一个数值。
12. isPromise 是否是Promise
function isPromise(val) {
return (
isDef(val) &&
typeof val.then === "function" &&
typeof val.catch === "function"
);
}
13. toString 转字符串
/**
* Convert a value to a string that is actually rendered.
*/
function toString(val) {
return val == null
? ""
: Array.isArray(val) || (isPlainObject(val) && val.toString === _toString)
? JSON.stringify(val, null, 2)
: String(val);
}
14. toNumber 转数字
/**
* Convert an input value to a number for persistence.
* If the conversion fails, return original string.
*/
function toNumber(val) {
var n = parseFloat(val);
return isNaN(n) ? val : n;
}
/**
* Convert an input value to a number for persistence.
* If the conversion fails, return original string.
*/
function toNumber(val) {
var n = parseFloat(val);
return isNaN(n) ? val : n;
}
15. makeMap 创建map(对象)
/**
* Make a map and return a function for checking if a key
* is in that 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];
};
}
传入一个以逗号分隔的字符串,生成一个 map(键值对),并且返回一个函数检测 key 值在不在这个 map 中。第二个参数是小写选项。
Object.create(null);可以创建一个原型链负担最小的对象
16. isBuiltInTag 是否是内置的 tag
/**
* Check if a tag is a built-in tag.
*/
var isBuiltInTag = makeMap("slot,component", true);
17. isReservedAttribute 是否是保留的属性
/**
* Check if an attribute is a reserved attribute.
*/
var isReservedAttribute = makeMap("key,ref,slot,slot-scope,is");
vue内置标签属性
18. remove 移除数组中的中一项
/**
* Remove an item from an array.
*/
function remove(arr, item) {
if (arr.length) {
var index = arr.indexOf(item);
if (index > -1) {
return arr.splice(index, 1);
}
}
}
查找并删除某项
19. hasOwn 检测是否是有某属性
/**
* Check whether an object has the property.
*/
var hasOwnProperty = Object.prototype.hasOwnProperty;
function hasOwn(obj, key) {
return hasOwnProperty.call(obj, key);
}
20. cached 缓存
/**
* Create a cached version of a pure function.
*/
function cached(fn) {
var cache = Object.create(null);
return function cachedFn(str) {
var hit = cache[str];
return hit || (cache[str] = fn(str));
};
}
闭包的利用,可当作优化运行时的,可以写个阶乘试试
21. camelize 连字符转小驼峰
/**
* Camelize a hyphen-delimited string.
*/
var camelizeRE = /-(\w)/g;
var camelize = cached(function (str) {
return str.replace(camelizeRE, function (_, c) {
return c ? c.toUpperCase() : "";
});
});
22. capitalize 首字母转大写
/**
* Capitalize a string.
*/
var capitalize = cached(function (str) {
return str.charAt(0).toUpperCase() + str.slice(1);
});
23. hyphenate 小驼峰转连字符
/**
* Hyphenate a camelCase string.
*/
var hyphenateRE = /\B([A-Z])/g;
var hyphenate = cached(function (str) {
return str.replace(hyphenateRE, "-$1").toLowerCase();
});
24. polyfillBind bind 的垫片
/**
* Simple bind polyfill for environments that do not support it,
* e.g., PhantomJS 1.x. Technically, we don't need this anymore
* since native bind is now performant enough in most browsers.
* But removing it would mean breaking code that was able to run in
* PhantomJS 1.x, so this must be kept for backward compatibility.
*/
/* istanbul ignore next */
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;
兼容了老版本浏览器不支持原生的 bind 函数
24. toArray 把类数组转成真正的数组
/**
* Convert an Array-like object to a real Array.
*/
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;
}
把数组或集合(如document.getElementsByClassName获取到的节点集合)转换成新数组,支持从哪个位置开始,默认从 0 开始。
25. extend 合并
/**
* Mix properties into target object.
*/
function extend(to, _from) {
for (var key in _from) {
to[key] = _from[key];
}
return to;
}
26. toObject 转对象
/**
* Merge an Array of Objects into a single Object.
*/
function toObject(arr) {
var res = {};
for (var i = 0; i < arr.length; i++) {
if (arr[i]) {
extend(res, arr[i]);
}
}
return res;
}
27. noop 空函数
/**
* Perform no operation.
* Stubbing args to make Flow happy without leaving useless transpiled code
* with ...rest (https://flow.org/blog/2017/05/07/Strict-Function-Call-Arity/).
*/
function noop(a, b, c) {}
28. no 一直返回 false
/**
* Always return false.
*/
var no = function (a, b, c) {
return false;
};
29. identity 返回参数本身
/**
* Return the same value.
*/
var identity = function (_) {
return _;
};
30. genStaticKeys 生成静态属性
/**
* Generate a string containing static keys from compiler modules.
*/
function genStaticKeys(modules) {
return modules
.reduce(function (keys, m) {
return keys.concat(m.staticKeys || []);
}, [])
.join(",");
}
31. looseEqual 宽松相等比较
/**
* Check if two values are loosely equal - that is,
* if they are plain objects, do they have the same shape?
*/
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;
}
}
对引用类型进行递归比对,判断是否相对
32. looseIndexOf 宽松的 indexOf
/**
* Return the first index at which a loosely equal value can be
* found in the array (if value is a plain object, the array must
* contain an object of the same shape), or -1 if it is not present.
*/
function looseIndexOf(arr, val) {
for (var i = 0; i < arr.length; i++) {
if (looseEqual(arr[i], val)) {
return i;
}
}
return -1;
}
33. once 确保函数只执行一次
/**
* Ensure a function is called only once.
*/
function once(fn) {
var called = false;
return function () {
if (!called) {
called = true;
fn.apply(this, arguments);
}
};
}
闭包了个变量作为锁,以此控制执行
34. 生命周期和公共变量
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",
];
总结
万丈高楼平地起,这些工具函数就像螺钉螺帽一样,在代码中调用后可以使得行文更紧凑、代码更美观、结构更牢固。