在 vue 源码中,封装了很多工具函数,学习这些函数,一方面学习大佬们的实现方式,另一方面是温习基础知识,希望大家在日常工作中,简单的函数也可以自己封装,提高编码能力。
本次涉及的工具函数 1-16 在 Vue3 的源码中,路径是 core/packages/shared/src/index.ts。
17-22 在 Vue2 的源码中,路径是 vue/src/shared/util.ts。
const EMPTY_OBJ = __DEV__
? Object.freeze({})
: {}
注意:
Object.freeze 只能浅冻结,如果属性是对象,对属性的属性的修改就无法冻结了
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 } }
源码中的使用:
可以看出基本都是作为初始化或者兜底使用,由此产生疑问:
const EMPTY_ARR = __DEV__ ? Object.freeze([]) : []
const NOOP = () => {}
依旧作为兜底和占位使用:
const NO = () => false
源码中的使用:
const onRE = /^on[^a-z]/;
const isOn = (key) => onRE.test(key);
// 示例
isOn('onChange'); // true
isOn('onchange'); // false
isOn('on3change'); // true
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)
}
const toRawType = (value) => {
return Object.prototype.toString.call(value).slice(8, -1)
}
// 示例
toRawType(''); 'String'
toRawType([]); 'Array'
源码中的使用:
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
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];
}
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
如果有 /*#__PURE__*/ 这个标志,说明他是纯函数,如果没有调用它,打包工具会直接通 tree-shaking 把它删除,减少代码体积。
const isBuiltInDirective = /*#__PURE__*/ makeMap(
'bind,cloak,else-if,else,for,html,if,model,on,once,pre,show,slot,text,memo'
)
const cacheStringFunction = (fn) => {
const cache = Object.create(null);
return ((str) => {
const hit = cache[str];
return hit || (cache[str] = fn(str));
});
};
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');
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
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, '我是:');
源码中的使用:
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;
};
}
function isPrimitive(value) {
return (
typeof value === 'string' ||
typeof value === 'number' ||
typeof value === 'symbol' ||
typeof value === 'boolean'
)
}
function isValidArrayIndex(val) {
const n = parseFloat(String(val))
return n >= 0 && Math.floor(n) === n && isFinite(val)
}
// isFinite 如果参数是 NaN,正无穷大或者负无穷大,会返回 false,其他返回 true
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
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
}
function once(fn) {
let called = false
return function () {
if (!called) {
called = true
fn.apply(this, arguments)
}
}
}
function isNative(Ctor) {
return typeof Ctor === 'function' && /native code/.test(Ctor.toString())
}
在日常 Coding 中,码农们肯定少不了对数组的操作,其中很常用的一个操作就是对数组进行遍历,查看数组中的元素,然后一顿操作猛如虎。今天暂且简单地说说在 JavaScript 中 forEach。
克隆项目代码到本地(git应该都要会哈,现在源码几乎都会放github上,会git才方便,不会的可以自学一下哦,不会的也没关系,gitHub上也提供直接下载的链接);打开微信开发者工具;
随着这些模块逐渐完善, Nodejs 在服务端的使用场景也越来越丰富,如果你仅仅是因为JS 这个后缀而注意到它的话, 那么我希望你能暂停脚步,好好了解一下这门年轻的语言,相信它会给你带来惊喜
在 Vue 内部,有一段这样的代码:上面5个函数的作用是在Vue的原型上面挂载方法。initMixin 函数;可以看到在 initMixin 方法中,实现了一系列的初始化操作,包括生命周期流程以及响应式系统流程的启动
nextTick的使用:vue中dom的更像并不是实时的,当数据改变后,vue会把渲染watcher添加到异步队列,异步执行,同步代码执行完成后再统一修改dom,我们看下面的代码。
React更新的方式有三种:(1)ReactDOM.render() || hydrate(ReactDOMServer渲染)(2)setState(3)forceUpdate;接下来,我们就来看下ReactDOM.render()源码
在React中,为防止某个update因为优先级的原因一直被打断而未能执行。React会设置一个ExpirationTime,当时间到了ExpirationTime的时候,如果某个update还未执行的话,React将会强制执行该update,这就是ExpirationTime的作用。
算法对于前端工程师来说总有一层神秘色彩,这篇文章通过解读V8源码,带你探索 Array.prototype.sort 函数下的算法实现。来,先把你用过的和听说过的排序算法都列出来:
extend是jQuery中一个比较核心的代码,如果有查看jQuery的源码的话,就会发现jQuery在多处调用了extend方法。作用:对任意对象进行扩;’扩展某个实例对象
state也就是vuex里的值,也即是整个vuex的状态,而strict和state的设置有关,如果设置strict为true,那么不能直接修改state里的值,只能通过mutation来设置
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!