vue2工具函数

91 阅读7分钟

本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。

1. isUndef

    function isUndef (v) {
      return v === undefined || v === null
    }
   let a = null
   let b = undefined
   console.log(isUndef(a)); // true
   console.log(isUndef(b)); //true

2. isDef

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

与1相反

3. isTrue

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

4. isFalse

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

5. isPrimitive 是否是原始值

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

js的基本数据类型有string、number、boolean、symbol、null、undefined、bigint 这边我复习了一下symbol类型和浅浅学习了一下bigint类型

6. isObject

    function isObject (obj) {
      return obj !== null && typeof obj === 'object'
    }

排除了null object、array、function 都为ture

7. toRawType

    var _toString = Object.prototype.toString;
  
    function toRawType (value) {
      return _toString.call(value).slice(8, -1)
    }

可以看一下MDN中对Object.prototype.toString()的描述

复习一下slice方法以及splice方法 这里就不赘述了 e.g 有点忘记splice只能用来操作数组不能操作字符串而slice可以都可以操作

call apply bind 这个我还只知道用法,使用场景啥的都还不知道,有查阅资料但是现在看得不是很懂后续去看 三者都是改变this指向 call、apply参数不同 都可以立即执行 call(xx,参数1,参数1),参数1)) bind第二个参数是数组

bind 第二个参数是数组 返回值是一个函数 需要()调用

8. isPlainObject

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

9. isRegExp

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

检查是否是正则表达式 知道了toRowType 就很好知道这个

这个是最严谨的判断类型的方式

10. isValidArrayIndex

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

parseFloat 解析一个参数(必要时候先转换成字符串),并返回一个浮点数

遇到除了+、-、科学计数法e或E、[0-9]、小数点 之外的数字会忽略该字符以及之后的所有字符,返回当前已经解析到的浮点数 第二个小数点的出现也会使得解析停止 会去除首位的空格 parseFloat 解析 BigInt 为 Numbers, 丢失精度。因为末位 n 字符被丢弃。 parseFloat 也可以转换一个已经定义了 toString 或者 valueOf 方法的对象,它返回的值和在调用该方法的结果上调用 parseFloat 值相同。 console.log(parseFloat(Infinity)); //Infinity 推荐使用Number(val) 只要val中存在无效字符就会被转化成NaN

Math.floor() 舍去小数部分 用来判断是不是整数

Math.ceil() 返回大于或等于一个给定数字的最小整数。 (-2.1)=>-2 (2.1)=>3 Math.round() 四舍五入 注意 Math.round() 并不总是舍入到远离 0 的方向(尤其是在负数的小数部分恰好等于 0.5 的情况下

isFinite() 判断数是不是无穷大

11. isPromise

  function isPromise (val) {
    return (
      isDef(val) &&
      typeof val.then === 'function' &&
      typeof val.catch === 'function'
    )
  }
// 这个判断promise的方法我自己写了一个不是promise的函数好像好像也可以 有、、疑惑
// 就比如我下面写的例子
let promiseObj = {
  then: function() {
    console.log('This is then');
  },
  catch: function() {
    console.catch('This is catch')
  }
}

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

console.log(isPromise(promiseObj)); // true

12. toString

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

13. toNumber

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

如果传入的val不能转换成数字那么就返回val

14. makeMap

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

用了闭包! Object.create(proto, [propertiesObject]) 方法用于创建一个新对象,使用现有的对象来作为新创建对象的原型 可以去mdn看一下语法和描述

// 这个是mdn上例子 我这里使用了一下它的第二个参数 至于第二个参数写法可以去看一下mdn上的Object.defineProperties()的第二个属性
const person = {
  isHuman: false,
  printIntroduction: function() {
    console.log(this);
    console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`);
  }
};

const me = Object.create(person,{
  age: {
    enumerable: true,
    writable: true,
    configurable: true,
    value: 12
  }
});

me.name = 'Matthew';
me.isHuman = true; 

me.printIntroduction();
// expected output: "My name is Matthew. Am I human? true"

Object.create(null) expectsLowerCase是否忽略大小写

15. isBuiltInTag

  /**
   * Check if a tag is a built-in tag.
   */
  var isBuiltInTag = makeMap('slot,component', true);

16. isReservedAttribute

  /**
   * Check if an attribute is a reserved attribute.
   */
   // 是否是保留的属性
  var isReservedAttribute = makeMap('key,ref,slot,slot-scope,is');

17. remove

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

let arr = [1,2,3,4]
remove(arr,1)
console.log(arr);

splice

  1. splice返回值是删除元素组成的数组
  2. splice改变原数组
  3. splice实际上是替换其第三个参数可以用来替换删除的位置

18.hasOwn

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

检查对象是否存在某个自身的属性 继承的属性返回false

对于为啥要 hasOwnProperty = Object.prototype.hasOwnProperty 是因为 在日常开发中,有某些常见的用法,使得Object.prototype上的方法有时可能不可用或是会被重新定义了。

看下面例子

案例一:被重新定义了

let obj = {
  name: 'liuyang',
  hasOwnProperty: function() {
    return 'kkk'
  }
}

console.log(obj.hasOwnProperty('name')); 
// output: kkk
// 对象自身有一个hasOwnProperty属性 那么这个方法就会调用它本身的

案例二: Object.crete(null)创建的对象没有从原型上继承任何对象方法

Object.create(null).hasOwnProperty('name')
// output: TypeError: Object.create(...).hasOwnProperty is not a function

19. cached

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

第一次调用的时候 调用fn(str)方法 将值存到cache(str)中 str=>val 以后调用的时候直接从cache对象中拿 效率大大提高

闭包 高阶函数

20. camelize

    //连字符转小驼峰
    var camelizeRE = /-(\w)/g;
    var camelize = cached(function (str) {
      return str.replace(camelizeRE, function (_, c) { return c ? c.toUpperCase() : ''; })
    });

是我第一次遇到 replace的第二个参数用fun 去mdn看了下指定一个函数作为参数 所以这边的 _表示匹配到的字符串 c表示()里的规则匹配到的字符串

// e.g.
console.log(camelize("on-click")) // onClick
// _为_c  c为c

21. capitalize

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

首字母大写

  console.log(capitalize('abc')); // Abc

22.hyphenate

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

这也是我第一次遇到 replace 第二个参数这种写法 去看了下mdn使用字符串做参数

23. 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;

稍后研究

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

类数组对象转化成数组 还可以指定开始的下标(默认是从0开始)

let obj = {
    0: 'ly',
    1: 'ltt'
}
console.log(toArray(obj)) // [ly,ltt]

25. extend

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

将from对象的属性合并到to对象中 如果有属性重合 from覆盖to

let obj1 = {
  name: 'liuyang'
}

let obj2 = {
  age: '23',
  height: 160
}

console.log(extend(obj1,obj2)); 

console.log(obj1); // 改变
console.log(obj2); // 不改变

26. toObject

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

将一个对象数组转换成单个对象

let arr = [
  {
    a: 1
  },
  {
    b: 2
  }
]
console.log(toObject(arr)); // { a: 1, b: 2 }

27. noop

function noop (a, b, c) {}

啥也不干

28. no

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

不管传入啥都返回false

29. identity

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

返回传入的第一个参数

30. genStaticKeys

// Generate a static keys string from compiler modules 从编译器模块中生成静态的字符串
    function genStaticKeys (modules) {
      return modules.reduce(function (keys, m) {
        return keys.concat(m.staticKeys || [])
      }, []).join(',')
    }

返回modules中所有属性为staticKeys的值组成的字符串 还不清楚该函数在vue中的应用

let arr = [
  {
    staticKeys: 'name'
  },
  {
    
  },
  {
    staticKeys: 'height'
  },
  {
    staticKeys: 'width'
  }
]
console.log(genStaticKeys(arr));

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

是一个递归方法。如果是基本类型值则直接判断;如果是对象则遍历对象的属性,属性也有可能是引用类型,所以递归判断;如果是数组则遍历数组元素,数组元素可能是引用类型,所以递归判断;如果是日期类型则转成时间戳比较是否相等。

32. looseIndexOf

    function looseIndexOf (arr, val) {
      for (var i = 0; i < arr.length; i++) {
        if (looseEqual(arr[i], val)) { return i }
      }
      return -1
    }
var obj1 = {a:1}
var obj2 = {b:2}
const res = looseIndexOf([obj1, obj2], {a:1})
console.log(res) //0

val 在 arr中是否存在 存在返回下标 不存在则返回-1

33. once

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

闭包 高阶函数 感觉有点妙

    // e.g.
    function func() {
      console.log(111);
    }
    
    let a = once(func)
    a() //11
    a() // 

总结

出现的一些知识点已经另外做了笔记 比如类数组、typeof、instanceof等 没有见过缓存函数cache和once让函数仅调用一次的写法

感觉以后还得过几遍 把知识点理解透彻