给vue源码写备注加自己理解备注,第一天

227 阅读4分钟

为什么开始?

很早就有阅读vue源码的想法,
迫于入行不久,功力太过浅认为啃不太动就一直搁置

年后有段时间看了lodash的部分源码
发现源码也不是完全看不懂
不过,当时看得懂,没做过什么笔记过段时间看又忘了

之前在网上断断续续的看过其他道友发的vue的源码理解,也是看完没有印象了,
索性这次直接给他看的时候加上备注和理解,
方便下次看的时候更快理解吧

每天看个一点点,日拱一卒。 耶巴蒂莱维贝贝!!!!

  • 第一天工具类方法查看理解

特别提醒

每个方法都有备注 部分有理解


    /*  */
    // 冻结的空对象
    var emptyObject = Object.freeze({});
  
    // These helpers produce better VM code in JS engines due to their
    // explicitness and function inlining.
    // 判空处理是  undefined和null
    function isUndef (v) {
      return v === undefined || v === null
    }
    // 非undefined和null判断
    function isDef (v) {
      return v !== undefined && v !== null
    }
    // 全等判断布尔值true
    function isTrue (v) {
      return v === true
    }
    // 全等判断布尔值false
    function isFalse (v) {
      return v === false
    }
  
    /**
     * Check if value is primitive.
     */
    // js原始值类型判断
    function isPrimitive (value) {
      return (
        typeof value === 'string' ||
        typeof value === 'number' ||
        // $flow-disable-line
        typeof value === 'symbol' ||
        typeof value === 'boolean'
      )
    }
  
    /**
     * Quick object check - this is primarily used to tell
     * Objects from primitive values when we know the value
     * is a JSON-compliant type.
     */
    // 判断是否是对象 数组其实也能通过这个方法的验证
    function isObject (obj) {
      return obj !== null && typeof obj === 'object'
    }
  
    /**
     * Get the raw type string of a value, e.g., [object Object].
     */
    
    var _toString = Object.prototype.toString;
    // 获取值得原始类型  true "[object Boolean]"
    // var a = Object.prototype.toString;
    // a.call(true)
    // "[object Boolean]"
    // var a = Object.prototype.toString;
    // a.call([1,2,3])
    // "[object Array]"
    // var a = Object.prototype.toString;
    // a.call(null)
    // "[object Null]"
    // var a = Object.prototype.toString;
    // a.call(undefined)
    // "[object Undefined]"
    // var a = Object.prototype.toString;
    // a.call('')
    // "[object String]"
    // var a = Object.prototype.toString;
    // a.call(1)
    // "[object Number]"
    function toRawType (value) {
      return _toString.call(value).slice(8, -1)
    }
  
    /**
     * Strict object type check. Only returns true
     * for plain JavaScript objects.
     */
    // 对象判断 数组不能通过校验
    function isPlainObject (obj) {
      return _toString.call(obj) === '[object Object]'
    }
    // 正则数据类型校验
    function isRegExp (v) {
      return _toString.call(v) === '[object RegExp]'
    }
  
    /**
     * Check if val is a valid array index.
     */
    function isValidArrayIndex (val) {
    //  取浮点数
      var n = parseFloat(String(val));
    //   取整数组的下标 是否 是整数  是否是无线的
      return n >= 0 && Math.floor(n) === n && isFinite(val)
    }
    
    /*
     是否是promise
     */
    function isPromise (val) {
      return (
        isDef(val) &&
        typeof val.then === 'function' &&
        typeof val.catch === 'function'
      )
    }
  
    /**
     * Convert a value to a string that is actually rendered.
     */
    /* 
        转为字符串 对象数组也转为字符串
        null和undefined返回 空字符串
    */
    function toString (val) {
      return val == null
        ? ''
        : Array.isArray(val) || (isPlainObject(val) && val.toString === _toString)
          ? JSON.stringify(val, null, 2)
          : String(val)
    }
  
    /**
     * Convert an input value to a number for persistence.
     * If the conversion fails, return original string.
     */
    function toNumber (val) {
      var n = parseFloat(val);
      return isNaN(n) ? val : n
    }
  
    /**
     * Make a map and return a function for checking if a key
     * is in that map.
     */
    /* 
      利用闭包将 str 处理成
      如  str = 'a,b'
      处理成了 {
          a:true,
          b:true
      }封存在内存中
      根据 expectsLowerCase 值判断是否需要将key小写处理
      如果是expectsLowerCase 为true则返回 内存中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 (val) { return map[val.toLowerCase()]; }
        : function (val) { return map[val]; }
    }
  
    /**
     * Check if a tag is a built-in tag.
     */
    // 检查是 slot  还是component 标签
    var isBuiltInTag = makeMap('slot,component', true);
  
    /**
     * Check if an attribute is a reserved attribute.
     * 检查属性是否为保留属性
     */
    // 检查属性 。
    var isReservedAttribute = makeMap('key,ref,slot,slot-scope,is');
  
    /**
     * Remove an item from an array.
     */
    // 从数组中删除一个值
    function remove (arr, item) {
      if (arr.length) {
        var index = arr.indexOf(item);
        if (index > -1) {
          return arr.splice(index, 1)
        }
      }
    }
  
    /**
     * Check whether an object has the property.
     * 检查对象是否具有属性
     */
    var hasOwnProperty = Object.prototype.hasOwnProperty;
    function hasOwn (obj, key) {
      return hasOwnProperty.call(obj, key)
    }
  
    /**
     * Create a cached version of a pure function.
     */
    // 使用闭包 将不同回调方法处理的值封闭在内存中
    function cached (fn) {
      var cache = Object.create(null);
      return (function cachedFn (str) {
        var hit = cache[str];
        return hit || (cache[str] = fn(str))
      })
    }
  
    /**
     * Camelize a hyphen-delimited string.
     */
    /* 
        "asdfasdf-asdf".replace(/-(\w)/g, function (_, c) { return c ? c.toUpperCase() : ''; })
        "asdfasdfAsdf"
        正则处理  将- 连接的字符串 转换成  小驼峰规则的字符串
        当前正则匹配 -后第一个英文字符 将其设为大写
    */
    var camelizeRE = /-(\w)/g;
    var camelize = cached(function (str) {
      return str.replace(camelizeRE, function (_, c) { return c ? c.toUpperCase() : ''; })
    });
  
    /**
     * Capitalize a string.
     */
    /* 
        将字符串的第一个字母大写
    */
    var capitalize = cached(function (str) {
      return str.charAt(0).toUpperCase() + str.slice(1)
    });
  
    /**
     * Hyphenate a camelCase string.
     */
    var hyphenateRE = /\B([A-Z])/g;
    // 将 B大写的开头中间含有大写的字符串转为 小写 且前面使用- 连接
    /* 
        'BasdAAsdfasdf'.replace(/\B([A-Z])/g, '-$1').toLowerCase()
        "basd-a-asdfasdf"
    */
    var hyphenate = cached(function (str) {
      return str.replace(hyphenateRE, '-$1').toLowerCase()
    });
  
    /**
     * Simple bind polyfill for environments that do not support it,
     * e.g., PhantomJS 1.x. Technically, we don't need this anymore
     * since native bind is now performant enough in most browsers.
     * But removing it would mean breaking code that was able to run in
     * PhantomJS 1.x, so this must be kept for backward compatibility.
     */
  
    /* istanbul ignore next */
    // bind方法的call和apply方法的实现 理解成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
    }
    // bind方法的原生实现
    function nativeBind (fn, ctx) {
      return fn.bind(ctx)
    }
    // 判断当前环境是否有原生bind方法 如果有  就给 bind变量赋值  如果没有就用垫片方式实现bind
    var bind = Function.prototype.bind
      ? nativeBind
      : polyfillBind;
  
    /**
     * Convert an Array-like object to a real Array.
     */
    /* 
        将类数组转为数组
        同时实现了截取功能

    */
    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
    }
  
    /**
     * Mix properties into target object.
     */
    // 实现extend方法 对象合并 相同key值 后面的把前面的覆盖掉
    function extend (to, _from) {
      for (var key in _from) {
        to[key] = _from[key];
      }
      return to
    }
  
    /**
     * Merge an Array of Objects into a single Object.
     */
    // 将一组对象合并为一个对象。
   /*  
    toObject([{a:1},{c:2}])
        {a: 1, c: 2} 
    */
    function toObject (arr) {
      var res = {};
      for (var i = 0; i < arr.length; i++) {
        if (arr[i]) {
          extend(res, arr[i]);
        }
      }
      return res
    }
  
    /* eslint-disable no-unused-vars */
  
    /**
     * Perform no operation.
     * Stubbing args to make Flow happy without leaving useless transpiled code
     * with ...rest (https://flow.org/blog/2017/05/07/Strict-Function-Call-Arity/).
     */
    // 就是定义了一个空方法,还有很多地方用到 等用到的时候在进行备注
    function noop (a, b, c) {}
  
    /**
     * Always return false.
     */
    // 一直返回false的方法
    var no = function (a, b, c) { return false; };
  
    /* eslint-enable no-unused-vars */
  
    /**
     * Return the same value.
     */
    // 返回入参的方法
    var identity = function (_) { return _; };
  
    /**
     * Generate a string containing static keys from compiler modules.
     */
    /* 
        // 从编译器模块生成包含静态键的字符串
        genStaticKeys([{staticKeys:1},{staticKeys:2}])
        "1,2"
        个人理解  读取类似于这样的对象数组 ,将每个对象中的staticKeys 拿出来并用, 号分隔
    */
    function genStaticKeys (modules) {
      return modules.reduce(function (keys, m) {
        return keys.concat(m.staticKeys || [])
      }, []).join(',')
    }
  
    /**
     * Check if two values are loosely equal - that is,
     * if they are plain objects, do they have the same shape?
     */
    /* 
        全等判断
    */
    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
      }
    }
  
    /**
     * Return the first index at which a loosely equal value can be
     * found in the array (if value is a plain object, the array must
     * contain an object of the same shape), or -1 if it is not present.
     */
    // 判断数组中的值是否包含 val  个人理解类似于indexOf 方法
    function looseIndexOf (arr, val) {
      for (var i = 0; i < arr.length; i++) {
        if (looseEqual(arr[i], val)) { return i }
      }
      return -1
    }
  
    /**
     * Ensure a function is called only once.
     */
    // 使用闭包构造出只执行一次的方法
    /* 
        function test(res) {
            console.log(res);
            a()
        }
        function a() {
            alert(111)
        }
        var a = once(test);
        a('bqc')
        
        VM1696:6 111111
        VM1724:2 bqc
    */


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

更多原生js的个人学习总结欢迎查看 star

设计模式个人学习总结,点开有惊喜哦