foreword
In the source code of Vue
, many tool functions are encapsulated. Learning these functions, on the one hand, learn the implementation methods of the big guys, and on the other hand, review the basic knowledge. I hope everyone can use simple functions in their daily work. You can encapsulate it yourself to improve coding capabilities.
The tool functions 1-16 involved this time are in the source code of Vue3
, and the path is core/packages/shared/src/index.ts .
17-22 In the source code of Vue2
, the path is vue/src/shared/util.ts .
1. EMPTY_OBJ empty object
const EMPTY_OBJ = __DEV__
? Object.freeze({})
: {}
Notice:
Object.freeze
Only shallow freezing, if the attribute is an object, the modification of the attribute of the attribute cannot be frozen
const obj = {
name: '张三',
info: {
a: 1,
b: 2
}
};
Object.freeze(obj);
obj.name = '李四';
console.log(obj); // { name: '张三', info: { a: 1, b: 2 } }
obj.info.a = 66;
console.log(obj); // { name: '张三', info: { a: 66, b: 2 } }
Use in source code:
It can be seen that they are basically used as initialization or bottom line, which leads to doubts:
- Some of the places used are options, some are props, and the same object is used in different places. Is there any problem?
First of all, many initialization operations will be reassigned later,EMPTY_OBJ
is only used as a placeholder. Secondly, because ofObject.freeze
, it cannot be modifiedEMPTY_OBJ
, so any place that references this object will not be affected. - Why is Object.freeze used only when the judgment is __DEV__(process.env.NODE_ENV !== 'production')?
Object.freeze
MoreVue
by source code developers during debugging, they can report errors to prevent operations on empty objects and find source code problems faster. Therefore, the development environment will eventually avoid the assignment operation toEMPTY_OBJ
, so it is not meaningful to useObject.freeze
in the production environment.
2. EMPTY_ARR empty array
const EMPTY_ARR = __DEV__ ? Object.freeze([]) : []
3. NOOP empty function
const NOOP = () => {}
Still used as a pocket and placeholder:
4. NO function that always returns false
const NO = () => false
Use in source code:
5. isOn judges whether the string starts with on, and the first letter after on is not a lowercase letter
const onRE = /^on[^a-z]/;
const isOn = (key) => onRE.test(key);
// 示例
isOn('onChange'); // true
isOn('onchange'); // false
isOn('on3change'); // true
6. Type judgment
const isArray = Array.isArray
const isFunction = (val) => typeof val === 'function'
const isString = (val) => typeof val === 'string'
const isSymbol = (val) => typeof val === 'symbol'
const isObject = (val) => val !== null && typeof val === 'object'
const toTypeString = (value) => Object.prototype.toString.call(value)
const isMap = (val) => toTypeString(val) === '[object Map]'
const isSet = (val) => toTypeString(val) === '[object Set]'
const isDate = (val) => toTypeString(val) === '[object Date]'
const isPlainObject = (val) => Object.prototype.toString.call(val) === '[object Object]'
// isPlainObject 判断是不是普通对象(排除正则、数组、日期、new Boolean、new Number、new String 这些特殊的对象)
isObject([]) // true
isPlainObject([]) // false
const isPromise = (val) => {
return isObject(val) && isFunction(val.then) && isFunction(val.catch)
}
7. toRawType extract data raw type
const toRawType = (value) => {
return Object.prototype.toString.call(value).slice(8, -1)
}
// 示例
toRawType(''); 'String'
toRawType([]); 'Array'
Use in source code:
8. isIntegerKey determines whether it is a numeric string
const isIntegerKey = (key) => isString(key) &&
key !== 'NaN' &&
key[0] !== '-' &&
'' + parseInt(key, 10) === key;
// 例子:
isIntegerKey('a'); // false
isIntegerKey('0'); // true
isIntegerKey('011'); // false
isIntegerKey('11'); // true
isIntegerKey('-11'); // false
isIntegerKey(11); // false
isIntegerKey('NaN'); // false
9. makeMap separates the string into a map, case-sensitive, and returns a function to determine whether the map contains a certain key
function makeMap(str, expectsLowerCase) {
const map = Object.create(null);
const list = str.split(',');
for (let i = 0; i < list.length; i++) {
map[list[i]] = true;
}
return expectsLowerCase ? val => !!map[val.toLowerCase()] : val => !!map[val];
}
10. Is isReservedProp a reserved property
const isReservedProp = /*#__PURE__*/ makeMap(
// the leading comma is intentional so empty string "" is also included
',key,ref,ref_for,ref_key,' +
'onVnodeBeforeMount,onVnodeMounted,' +
'onVnodeBeforeUpdate,onVnodeUpdated,' +
'onVnodeBeforeUnmount,onVnodeUnmounted');
// ['', 'key', 'ref', 'ref_for', 'ref_key', 'onVnodeBeforeMount', 'onVnodeMounted', 'onVnodeBeforeUpdate', 'onVnodeUpdated', 'onVnodeBeforeUnmount', 'onVnodeUnmounted']
// 示例
isReservedProp('key'); // true
isReservedProp('onVnodeBeforeMount'); // true
isReservedProp(''); // true
isReservedProp(' '); // false
If there is /*#__PURE__*/
this flag, it means that it is a pure function. If it is not called, the packaging tool will directly pass tree-shaking
Delete it to reduce the code size.
11. Is isBuiltInDirective a built-in directive?
const isBuiltInDirective = /*#__PURE__*/ makeMap(
'bind,cloak,else-if,else,for,html,if,model,on,once,pre,show,slot,text,memo'
)
12. cacheStringFunction turns a function into a function with cacheable results
const cacheStringFunction = (fn) => {
const cache = Object.create(null);
return ((str) => {
const hit = cache[str];
return hit || (cache[str] = fn(str));
});
};
13. camelize & hyphenate
const camelizeRE = /-(\w)/g;
const camelize = cacheStringFunction((str) => {
return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ''));
});
// 清爽版
const camelize = str => str.replace(camelizeRE, (_, c) => {
return c ? c.toUpperCase() : '';
});
// 举例:on-click-a => onClickA
camelize('on-click-a');
const hyphenateRE = /\B([A-Z])/g;
const hyphenate = cacheStringFunction((str) => str.replace(hyphenateRE, '-$1').toLowerCase());
// 清爽版
const hyphenate = str => str.replace(hyphenateRE, '-$1').toLowerCase();
// 仿照 camelize 写法
const hyphenate = str => str.replace(hyphenateRE, (_, c) => {
return c ? `-${c.toLowerCase()}` : '';
});
// 举例:onClickA => on-click-a
hyphenate('onClickA');
14. hasChanged determines whether there is a change
const hasChanged = (value, oldValue) => value !== oldValue && (value === value || oldValue === oldValue);
// 示例
hasChanged(1, 1); // false
hasChanged(1, 2); // true
hasChanged(+0, -0); // false
hasChanged(NaN, NaN); // false
// 场景:watch 监测值是不是变化了
// 扩展 Object.is & ===
Object.is(+0, -0); // false
Object.is(NaN, NaN); // true
+0 === -0 // true
NaN === NaN // false
15. invokeArrayFns executes the function in the array
const invokeArrayFns = (fns, arg) => {
for (let i = 0; i < fns.length; i++) {
fns[i](arg);
}
};
// 示例
const arr = [
function(val){
console.log(val + '张三');
},
function(val){
console.log(val + '李四');
},
function(val){
console.log(val + '王五');
},
]
invokeArrayFns(arr, '我是:');
Use in source code:
16. toNumber to Number
const toNumber = (val) => {
const n = parseFloat(val);
return isNaN(n) ? val : n;
};
toNumber('111'); // 111
toNumber('a111'); // 'a111'
toNumber('11a11'); // '11'
toNumber(NaN); // NaN
// isNaN vs Number.isNaN
// isNaN 判断是不是数字 is Not a Number
// Number.isNaN 判断是不是 NaN
isNaN(NaN); // true
isNaN('a'); // true
Number.isNaN(NaN); // true
Number.isNaN('a'); // false
// Number.isNaN 的 polyfill
if (!Number.isNaN) {
Number.isNaN = function (n) {
// 方法一
return (window.isNaN(n) && typeof n === 'number');
// 方法二 利用只有 NaN 不跟自己相等的特性
return n !== n;
};
}
17. Is isPrimitive the original data?
function isPrimitive(value) {
return (
typeof value === 'string' ||
typeof value === 'number' ||
typeof value === 'symbol' ||
typeof value === 'boolean'
)
}
18. Is isValidArrayIndex a valid array index, integer and not infinity
function isValidArrayIndex(val) {
const n = parseFloat(String(val))
return n >= 0 && Math.floor(n) === n && isFinite(val)
}
// isFinite 如果参数是 NaN,正无穷大或者负无穷大,会返回 false,其他返回 true
19. bind compatible bind function
function polyfillBind(fn, ctx) {
function boundFn(a) {
const 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)
}
const bind = Function.prototype.bind ? nativeBind : polyfillBind
20. Convert toArray class array to array
function toArray(list, start) {
start = start || 0
let i = list.length - start
const ret = new Array(i)
while (i--) {
ret[i] = list[i + start]
}
return ret
}
21. once executes only once
function once(fn) {
let called = false
return function () {
if (!called) {
called = true
fn.apply(this, arguments)
}
}
}
22. Is isNative a native system function?
function isNative(Ctor) {
return typeof Ctor === 'function' && /native code/.test(Ctor.toString())
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。