Vue源码知识点分析

Vue源码相关函数分析

// 判断属性是否是内置属性

function isNative (Ctor) {
    return typeof Ctor === 'function' && /native code/.test(Ctor.toString())
 }

// 返回一个str循环n次的字符串
// 比如str=‘’a”,n=5,二进制中为101,while第一次循环,res+=‘a’,str+=‘a’,n的二进制右移一位,变成10,也就是十进制的2
// 第二轮循环,str=‘aa’,res不变,str+=‘aa’,再右移一位变成1,等于十进制的1
// 第三轮循环,str=‘aaaa’,res += ‘aaaa’,变成‘aaaaa’,二进制再右移一位变成0,此时循环条件不再满足,循环结束
// tip: 由此可知二进制每右移一位相当于Math.floor(n/2)用个for循环就能实现,不知道为啥作者非要这么骚气

 var repeat = function (str, n) {
	     var res = '';
	     while (n) {
	       if (n % 2 === 1) { res += str; }
	       if (n > 1) { str += str; }
	       n >>= 1;
	     }
	     return res
	   };

// js every和some方法
// every()是对数组中每一项运行给定函数,如果该函数对每一项返回true,则返回true。
// some()是对数组中每一项运行给定函数,如果该函数对任一项返回true,则返回true。

var arr = ['1',2, '3']

// every, 结果: false
 console.log(
     arr.every((item, index,array) => {
          return typeof item === 'string'
      })
 )
 
 // some, 结果:true
 console.log(
     arr.some((item, index,array) => {
          return typeof item === 'string'
      })
 )

// looseEqual意为 ‘宽松比较’,用来比较传入值a和b是否相等

  function looseEqual (a, b) {
  	var isObject = function (obj) {
  		return obj !== null && typeof obj === 'object'
	}
      if (a === b) { return true }  //  基本数据类型相等直接返回true
      var isObjectA = isObject(a);	
      var isObjectB = isObject(b);
      if (isObjectA && isObjectB) {  // a和b都为对象
        try {
          var isArrayA = Array.isArray(a);
          var isArrayB = Array.isArray(b);
          if (isArrayA && isArrayB) {   //a和b都为数组 
            return a.length === b.length && a.every(function (e, i) {  // 长度相等并且递归的每个元素相等
              return looseEqual(e, b[i])
            })
          } else if (a instanceof Date && b instanceof Date) {  //  a和b都为Date类型
            return a.getTime() === b.getTime()
          } else if (!isArrayA && !isArrayB) {  //  a和b都是对象
            var keysA = Object.keys(a); 
            var keysB = Object.keys(b);
            return keysA.length === keysB.length && keysA.every(function (key) {  // 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)  //  转换为String,比较是否相等
      } else {
        return false
      }
    }

// 闭包实现一个函数只被访问一次

    function once (fn) {
      var called = false;
      return function () {
        if (!called) {
          called = true;
          fn.apply(this, arguments);
        }
      }
    }
    
    //  调用demo
    function test () {
        console.log(111111)
    }
    var a = once(test)
    a()  // 输出111111
    a() // 不输出

// 检查一个字符串是否以 或 者 开 头 , s t r + ′ ′ 是 将 s t r 转 换 为 字 符 串 类 型 。 实 际 上 打 印 一 个 以 或者_开头,str+''是将str转换为字符串类型。实际上打印一个以 str+str开头的字符串会发现值为36,而36 === 0x24又是true,why? 因为0x24是ASCII的‘ ’ , 而 36 是 u n i c o d e 的 ‘ ’, 而36是unicode的‘ 36unicode’,不同编码格式而已。
// 说到这里就再缀几句,ASCII编码为8位二进制格式,有一位为0,所以一共128个字符,占一个byte;Unicode能够表示全世界所有的字节,Unicode最常用的是用两个byte表示一个字符(如果要用到非常偏僻的字符,就需要4个byte

function isReserved (str) {
      var c = (str + '').charCodeAt(0);
      return c === 0x24 || c === 0x5F
    }

// 又是一段很有意思的代码,先从字面理解吧,所谓supportsPassive中文指的就是是否支持passive,那passive又是什么呢?

// **关于passive的解释:**很多移动端的页面都会监听 touchstart 等 touch 事件,由于 touchstart 事件对象的 cancelable 属性为 true,也就是说它的默认行为可以被监听器通过 preventDefault() 方法阻止,那它的默认行为是什么呢,通常来说就是滚动当前页面(还可能是缩放页面),如果它的默认行为被阻止了,页面就必须静止不动。但浏览器无法预先知道一个监听器会不会调用 preventDefault(),它能做的只有等监听器执行完后再去执行默认行为,而监听器执行是要耗时的,有些甚至耗时很明显,这样就会导致页面卡顿。即便监听器是个空函数,也会产生一定的卡顿,毕竟空函数的执行也会耗时。

// 有 80% 的滚动事件监听器是不会阻止默认行为的,也就是说大部分情况下,浏览器是白等了。所以,passive 监听器诞生了,passive 的意思是“顺从的”,表示它不会对事件的默认行为说 no,浏览器知道了一个监听器是 passive 的,它就可以在两个线程里同时执行监听器中的 JavaScript 代码和浏览器的默认行为了。
具体分析请看注释:)

var supportsPassive = false; 
if (inBrowser) {
    try {
        var opts = {};
        Object.defineProperty(opts, 'passive', ({  // 空对象定义一个passive属性
            get: function get() {  
                /* istanbul ignore next */
                supportsPassive = true;
            }
        }));
        // 监听test-passive和事件函数,两者本身都不存在,opts调用了一次,如果opts的passive被触发,那么就会触发passive属性的get方法,修改supportsPassive为true; 
        window.addEventListener('test-passive', null, opts);   
    } catch (e) {}
}

// formatComponentName:规范化组件名字

      formatComponentName = function (vm, includeFile) {  //  组件的$root为自身时,也就代表不是子组件
        if (vm.$root === vm) {
          return '<Root>'
        }
        //  options包含组件的data对象、methods对象、template、挂载元素el、生命周期钩子等等当前组件的所有信息
        var options = typeof vm === 'function' && vm.cid != null
          ? vm.options
          : vm._isVue
            ? vm.$options || vm.constructor.options
            : vm;
         // name为定义的组件名称
        var name = options.name || options._componentTag;
        // options.__file为文件路径
        var file = options.__file;
        if (!name && file) {
          //  只有路径没有名称时,匹配不存在正反斜杠,并以.vue结尾的字符串,也就是文件名
          var match = file.match(/([^/\\]+)\.vue$/);
          name = match && match[1];  //  name 为不包含后缀的文件名
        }
  
        return (
          //   classify为另一个正则的函数,用来将字符串首字母大写并且去掉 "-" 及 "_" 字符
          (name ? ("<" + (classify(name)) + ">") : "<Anonymous>") +
          (file && includeFile !== false ? (" at " + file) : '')   //  提示文件具体位置,很明显,该函数输出提示语并提示该如何书写标签名
        )
      };

// 767到853行,定义VNode和原型方法,在此不缀了,比较容易理解

// 定义一个以数组原型为原型的对象

var arrayProto = Array.prototype;
var arrayMethods = Object.create(arrayProto);

// 该语句判断‘proto‘隐式原型是否在空对象中
// in :判断属性是否在指定的对象或者其原型链中

var hasProto = '__proto__' in {};

// 定义一个响应式的属性,入参:对象、属性key值、属性的值、警告函数,浅定义

    function defineReactive$$1 (
      obj,
      key,
      val,
      customSetter,
      shallow
    ) {
      var dep = new Dep();
  
      var property = Object.getOwnPropertyDescriptor(obj, key);
      if (property && property.configurable === false) {  // 属性描述符不能改变,且属性不能删除
        return
      }
  
      // cater for pre-defined getter/setters
      var getter = property && property.get;
      var setter = property && property.set;
      if ((!getter || setter) && arguments.length === 2) {
        val = obj[key];
      }
  
      var childOb = !shallow && observe(val);
      Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function reactiveGetter () {
          var value = getter ? getter.call(obj) : val;
          if (Dep.target) {
            dep.depend();
            if (childOb) {
              childOb.dep.depend();
              if (Array.isArray(value)) {
                dependArray(value);
              }
            }
          }
          return value
        },
        set: function reactiveSetter (newVal) {
          var value = getter ? getter.call(obj) : val;
          /* eslint-disable no-self-compare */
          if (newVal === value || (newVal !== newVal && value !== value)) {
            return
          }
          /* eslint-enable no-self-compare */
          if (customSetter) {
            customSetter();
          }
          // #7981: for accessor properties without setter
          if (getter && !setter) { return }
          if (setter) {
            setter.call(obj, newVal);
          } else {
            val = newVal;
          }
          childOb = !shallow && observe(newVal);
          dep.notify();
        }
      });
    }

// TODO…

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值