【源码共读】第24期 | vue2工具函数

73 阅读4分钟

1. 前言

1、本文主要分享一下vue2里面shared模块中几十个实用工具函数
2、记录一下本人的学习历程

2.源码地址

打包后的 vue.js 14行到379行,接下来就是解释其中的这些方法。

3. 工具函数

3.1 emptyObject

冻结对象让对象无法修改,也可以用来判默认空值

var emptyObject =Object.freeze({})

3.2 isUndef 判断是否未定义

function isUndef(v){
    return v === undefined || v === null
}

3.3 isDef 判断是否定义

function isDef(v){
    return v !== undefined || v !== null
}

3.4 isTrue 判断是否为true

function isTrue(v){
    return v === true
}

3.5 isFalse 判断是否为false

function isFalse(v){
    return v === false
}

3.6 isPrimitive 判断是否为原始值

function isPrimitive(value){
    return (
       typeof value === 'string' ||
       typeof value === 'number' ||
       typeof value === 'symbol' ||
       typeof value === 'boolean'
    )
}

3.7 isObject 判断是否为对象

function isObject(obj){
    return obj !== null && typeof obj === 'object'
}
 isObject([]) // true
 // []与{}的typeof判断都为'object'

3.8 toRawType 获取值的原始类型字符串

// 提取toString方法,相当于object.toString()
var _toString = Object.prototype.toString; 
// obj打印出来[object Object],截取后面的类型
// _toString.call(new Date) [object Date] 
// _toString.call(new String) [object String]
function toRawType(value){
    return _toString.call(value).slice(8,-1)
}

3.9 isPlainObject 严格的对象类型检查

function isPlainObject(obj){
    return _toString.call(obj) === '[object Object]'
}

3.10 isRegExp 严格的RegExp类型检查

function isRegExp(v){
    return _toString.call(v) === '[object RegExp]'
}

3.11 isValidArrayIndex 检查val是否为有效的数组索引

isFinite 是用来判断是否为一个有限数值

function isValidArrayIndex(val){
    var n = parseFloat(String(val));
    return n >= 0 && Math.floor(n) === n && isFinite(val)
}

3.12 isPromise 判断是否为promise

function isPromise(val){
    return (
        isDef(val) &&
        typeof val.then === 'function' &&
        typeof val.catch === 'function'
    )
}

3.13 toString 将值转化为真实呈现的数据

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

拓展知识:JSON.stringify(value, replacer , space)

  • value:将要序列化成 一个JSON 字符串的值。
  • replacer(可选):如果该参数是一个函数,则在序列化过程中,被序列化的值的每个属性都会经过该函数的转换和处理;如果该参数是一个数组,则只有包含在这个数组中的属性名才会被序列化到最终的 JSON 字符串中;如果该参数为null或者未提供,则对象所有的属性都会被序列化。
  • space(可选):指定缩进用的空白字符串,用于美化输出(pretty-print);如果参数是个数字,它代表有多少的空格;上限为10。该值若小于1,则意味着没有空格;如果该参数为字符串(字符串的前十个字母),该字符串将被作为空格;如果该参数没有提供(或者为null)将没有空格。

3.14 toNumber 将值转化为数字,转化失败就保持原样

function toNumber(val){
    var n = parseFloat(n);
    return isNaN(n) ? val : n;
}

拓展:parseFloat() 函数解析字符串并返回浮点数

3.15 makeMap 生成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(){ return map[val.toLowerCase()] }
        : function(){ return map[val] }
}

3.16 isBuiltInTag 判断是否为内置tag

var isBuiltInTag = makeMap('slot,component', true)

3.17 isReservedAttribute 判断是否为内置属性

var isReservedAttribute = makeMap('key,ref,slot,slot-scope,is')

3.18 remove 删除数组的某项

function remove(arr, item){
    if(arr.length){
        var index = arr.indexOf(item);
        if(index > -1){
            return arr.splice(index,1)
        }
    }
}

3.19 hasOwn 检查对象是否具有属性

var hasOwnProperty = Object.prototype.hasOwnProperty;
var hasOwn = function(obj,key){
    return hasOwnProperty.call(obj, key)
}

3.20 cached 缓存函数

function cached(fn){
    var cache = Object.create(null);
    return(function cachedFn(str){
        var hit = cache[str];
        return hit || (cache[str] = fn(str))
    })
}

3.21 camelize 连字符转小驼峰

连字符 - 转驼峰 on-click => onClick

var camelizeRE = /-(\w)/g;
var camelize = cached(function(str){
    return str.replace(camelizeRE, function(_,c){ return c ? c.toUpperCase() : ''; })
})

3.22 capitalize 首字母转大写

var capitalize = cached(function (str) {
        return str.charAt(0).toUpperCase() + str.slice(1)
    });

3.23 hyphenate 小驼峰转连字符

onClick => on-click

    var hyphenateRE = /\B([A-Z])/g;
    var hyphenate = cached(function (str) {
        return str.replace(hyphenateRE, '-$1').toLowerCase()
    });

3.24 polyfillBind bind 的垫片

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 函数。

3.25 toArray 把类数组转成真正的数组

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
}

3.26 extend 合并

function extend(to, _from){
    for(var key in _from){
        to[key] = _from[key]
    }
    return to
}

3.27 toObject 转对象

function toObject(arr) {
    var res = {};
    for(var i = 0; i< arr.length; i++){
        if(arr[i]){
            extend(res,arr[i])
        }
    }
    return res
}

3.28 noop 空函数

function noop(a,b,c){}

3.29 no 一直返回 false

var no = function (a, b, c) { return false; };

3.30 identity 返回参数本身

var identity = function (_) { return _; };

3.31 genStaticKeys 生成静态属性

function genStaticKeys (modules) {
    return modules.reduce(function (keys, m) {
        return keys.concat(m.staticKeys || [])
    }, []).join(',')
}

3.32 looseEqual 宽松相等

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
        }
    }

3.33 looseIndexOf 宽松版 indexOf

function looseIndexOf (arr, val) {
    for (var i = 0; i < arr.length; i++) {
        if (looseEqual(arr[i], val)) { return i }
    }
    return -1
}

3.34 once 确保函数只执行一次

利用闭包缓存方式判断只执行了一次

function once (fn) {
        var called = false;
        return function () {
            if (!called) {
                called = true;
                fn.apply(this, arguments);
            }
        }
    }

4. 总结

本文通过查看 Vue2 源码中 shared 模块代码,感觉也不难,但最好动手写写,多思考一下哪些场景用得上并且学习源码里面的思路。