vue的源码分析(全局工具函数)

VUE 2.6.8

(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  typeof define === 'function' && define.amd ? define(factory) :
  (global = global || self, global.Vue = factory());
}(this, function () { 'use strict';

建立一个自调用匿名函数,设计参数window,并传入window对象。不污染全局变量,也不会别的代码污染es6

emptyObject

var emptyObject = Object.freeze({});数组

字面上意义:空对象=冻结掉这个对象浏览器

Object.freeze({})这个方法核心在于对于这个对象将没法修改,添加。

isUndef

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

是否未被定义,若是参数等于undefined或者为空,返回true

isDef

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

是否认义,若是参数不等于undefined或者为空,返回true

isTrue

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

是否真,参数为真是返回true

isFalse

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

是否假,参数为真是返回true
function isPrimitive (value) {
    return (
    typeof value === 'string' ||
    typeof value === 'number' ||
    // $flow-disable-line
    typeof value === 'symbol' ||
    typeof value === 'boolean'
    )
}
是否为原始类型,typeof 返回

isObject

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

是否为对象,若是对象不等于空且typeof返回为object,返回true

_toString

var _toString = Object.prototype.toString;oop

该方法返回描述某个对象数据类型的字符串,如自定义的对象没有被覆盖,则会返回“[object type]”,其中,type则是实际的对象类型。在使用该方法检测的时候,可使用Object.prototype.toString.call()或者Object.prototype.toString.apply()进行测试,如

toRawType

function toRawType (value) {return _toString.call(value).slice(8, -1)}性能

slice(startIndex,endIndex),从0开始索引,其中8表明从第8位(包含)开始截取(本例中表明空格后面的位置),-1表明截取到倒数第一位(不含),因此正好截取到[object String]中的String。

isPlainObject

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

isPlainObject 静态函数 判断指定参数是不是一个纯粹的对象

isRegExp

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

判断指定参数是不是一个正则

isValidArrayIndex

function isValidArrayIndex (val) {
    var n = parseFloat(String(val));
    return n >= 0 && Math.floor(n) === n && isFinite(val)
}
是否为一个有效的数组,现将值转为字符串,而后用parseFloat解析,字符串中的首个字符是不是数字。若是是,则对字符串进行解析,直到到达数字的末端为止,而后以数字返回该数字,而不是做为字符串。
若是n>=0以及向下取整等于n以及isFinite是一个有限数。如1/0

isPromise

function isPromise (val) {
    return (
      isDef(val) &&
      typeof val.then === 'function' &&
      typeof val.catch === 'function'
    )
}
首先检测这个值是否被定义,而后判断它的then和catch是否为一个函数

toString

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

首先判断val是否为空,若是为空输出''不然
Array.isArray() 先肯定是否值为一个Array
或者前面的isPlainObject方法断定是否为一个对象
和值转成字符串是否等于Object.prototype.toString返回该对象的字符串;
若是条件知足JSON.stringify(val, null, 2)

stringify是有3个参数,
  • 第一个,参数是传入的值能够是String|Object|String|Number|Boolean|null
  • 第二个,则是过滤器,过滤器能够是数组,也能够是函数
  • 第三个,能够是空的格子(4)也能够是特殊符号/t
不然将val转成字符串

toNumber

function toNumber (val) {
    var n = parseFloat(val);
    return isNaN(n) ? val : n
}
先使用parseFloat函数转换值,而后isNaN检查是否为一个数值,若是是输出val,不然输出parseFloat(val)基本也就输出NaN了

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

  var isBuiltInTag = makeMap('slot,component', true);
  var isReservedAttribute = makeMap('key,ref,slot,slot-scope,is');

官方解释:制做一个映射并返回一个函数,用于检查键是否在该映射中。

建立一个空的对象复制给map
将值根据‘,’分割成字符串数组
遍历循环检测这个数组
若是expectsLowerCase有值且为true,将map中的数组转换为小写,不然直接输出map中的值

实际上主要就是检查map中是否存在某个key
检查标记是否为内置标记
检查属性是否为保留属性

remove

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

从数组中移除

indexOf获取数组中参数的位置
若是index > -1 存在,删除当前
.splice(index,howmany,item1,.....,itemX)补充,index为位置,howmany为删除数量若是为0不删除,这2个参数为必填。第三个则是向数组中添加

hasOwnProperty

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

这个是颇有意思的案例

首先hasOwnProperty();这个方法,它的参数颇有意思,字符串或者Symbol。而后返回true或false
Symbol是es6一种新的类型,因此有的时候问js有多少类型啊,记得多了一个。
  • 表示独一无二的值;
  • 声明时不能使用new Symbol(),而是 Symbol();
  • 声明时能够加参数,用于描述;
  • 做为key时不能被遍历;

hasOwn这个函数主要是检测当前对象是否有某种属性。

这个地方还能够作一些衍生
若是你想要实现支持setter和getter特性的拷贝,该怎么实现?

Object.defineproperties (定义属性)
Object.getOwnPropertyDescriptors(es2017,获取对象的多个属性)
Object.getOwnPropertyDescriptor(老一点,获取对象的单个属性的属性),但babel能够解决。

cached

function cached (fn) {
    var cache = Object.create(null);
    return (function cachedFn (str) {
      var hit = cache[str];
      return hit || (cache[str] = fn(str))
    })
  }
给变量cache赋值一个空的对象
返回一个cacheFn的函数,将函数的参数的key带入cache并赋值,返回hit。
若是cache中存在str那么返回hit,反之将其赋值到cache中返回

驼峰化

var camelizeRE = /-(\w)/g;
  var camelize = cached(function (str) {
    return str.replace(camelizeRE, function (_, c) { return c ? c.toUpperCase() : ''; })
  });
    
  var hyphenateRE = /\B([A-Z])/g;
  var hyphenate = cached(function (str) {
    return str.replace(hyphenateRE, '-$1').toLowerCase()
  });

注释写着将字符串驼峰化
匹配一个-组成单词的字符
调用前面cached函数,并将字符串replace(egexp/substr,replacement)中作一个判断

里面的函数 c这个参数理解不清楚。

补充_是占位符,c是组1

思考:.substring(0,1).toUpperCase()不知道是否能够这样写。或者直接用字符串型数组?.toUpperCase()

polyfillBind

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
}
获取boundFn中的arguments的长度
if(l){
    if(l>1){
        fn.apply(ctx, arguments)
    }else{
        fn.call(ctx, a)
    }    
 }else{ 
    fn.call(ctx)
}

若是参数不存在,直接绑定做用域调用该函数fn.call(ctx)
若是存在且只有一个,那么调用fn.call(ctx, a), a是入参
若是存在且不止一个,那么调用fn.apply(ctx, arguments)

call与apply的区别,call接受参数是一个一个接收,apply是做为数组来接收。如:
fn.call(this, 1,2,3)
fn.apply(this, [1,2,3])
对于不支持它的环境,使用简单的绑定polyfill,
例如,Phantomjs 1.x。从技术上讲,咱们再也不须要这个了。
由于在大多数浏览器中,本机绑定的性能已经足够了。
可是删除它意味着破坏可以运行的代码
PhantomJS 1.x,因此为了向后兼容,必须保留它。

nativeBind

function nativeBind (fn, ctx) {
  return fn.bind(ctx)
}
var bind = Function.prototype.bind
    ? nativeBind
    : polyfillBind;
原生的bind。以及断定这个bind是原生的仍是polyfill

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
}
数据转换。感受就是遍历循环一个个存储到一个新的ret上。

extend

function extend (to, _from) {
    for (var key in _from) {
      to[key] = _from[key];
    }
    return to
}
检查有多少个属性,而后赋值返回到to上

toObject

function toObject (arr) {
    var res = {};
    for (var i = 0; i < arr.length; i++) {
      if (arr[i]) {
        extend(res, arr[i]);
      }
    }
    return res
}
定义了一个res对象
遍历传递的arr数组。而后经过上面那个extend函数传递到res

noop

function noop (a, b, c) {}
一个空函数?。。。不知道干啥。往下再看看

no

var no = function (a, b, c) { return false; };
一个no??绑定了一个函数返回false?再看看

identity

var identity = function (_) { return _; };
一个传什么返回本身的东西?

genStaticKeys

function genStaticKeys (modules) {
    return modules.reduce(function (keys, m) {
      return keys.concat(m.staticKeys || [])
    }, []).join(',')
}
从编译器模块生成包含静态键的字符串

looseEqual(未完待续)

function looseEqual (a, b) {
    if (a === b) { return true }
    //若是:参数a和参数b恒等于返回true;
    //isObject这个函数已经看到过了,不为空且恒等于object  
    var isObjectA = isObject(a);
    var isObjectB = isObject(b);
    if (isObjectA && isObjectB) {
      //断定a和b是否知足
      try {
        var isArrayA = Array.isArray(a);
        var isArrayB = Array.isArray(b);
        //a和b是否为一个数组
        if (isArrayA && isArrayB) {
        //若是若是知足,检测a是否知足条件
        //补充every()函数 e是当前元素值,索引
        //array.every(function(currentValue,index,arr), thisValue)
          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
    }
  }
这一段有点长,我看下注释打在代码里.做用判断两个值是否相等。结构是否相同

looseIndexOf

function looseIndexOf (arr, val) {
    for (var i = 0; i < arr.length; i++) {
      if (looseEqual(arr[i], val)) { return i }
    }
    return -1
  }
返回索引,若是没找到-1,不然用上面的looseEqual()函数

once

function once (fn) {
    var called = false;
    return function () {
      if (!called) {
        called = true;
        fn.apply(this, arguments);
      }
    }
  }
一个闭包函数,只有在called = false时执行,若是调用事后,返回true。只能用一次。
相关文章
相关标签/搜索