10

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 of Object.freeze , it cannot be modified EMPTY_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 More Vue 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 to EMPTY_OBJ , so it is not meaningful to use Object.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())
}

References


诺顿
1.5k 声望327 粉丝

不忘初心,方得始终。