读Zepto.js源码心得

今天我们读源码, Let’s go.

相信打开这篇文章准备学习的你 , 一定知道阅读源码 , 学习其中设计思想的重要性 , 今天来和我一起阅读一款经典JS库的源码 , Zepto.js.

学习前提

学习目标

  • 分析zepto的设计思想
  • 看源码的实现方式

先来感受一下Zepto.js和原生JS在使用方面的一点区别

	// html
	<div>
		<span>1</span>
	 	<span>2</span>
	 	<span>3</span>
	</div>
	// JS
	var arr1 = [1, 2, 3];
	arr1.push(4);
	console.log( arr1 ); // [1, 2, 3, 4]
	console.log( arr1 instanceod Array ); // true
	console.log(arr1.addClass); // undefined
	// Zepto.js
	var arr2 = $('span');
	arr2.push(4);
	console.log(arr2); // [1, 2, 3, 4] // [span, span, span, 4, selector: "span"]
	console.log( arr2 instanceod Array ); // false
	console.log(arr2.addClass); // fn(t){...}
  • 这里很明显,arr1和arr2都形同数组,但arr1是真正的数组,arr2却是个“伪数组”。
  • 读到这里, 你肯定会好奇为什么arr1没有addClass方法,回想原型链的基础知识,既然arr1没有addClass方法,那么我们就给arr1加上addClass方法
	arr1.__proto__.addClass = function(){console.log('addClass!')}
	arr1.addClass(); // addClass!
  • 你或许也会好奇为什么arr2是个"伪数组",却拥有数组的push方法,它的addClass方法又是哪里来的,这里我们模拟一下
	var arr2 = $('span');
	arr2.__proto__ = { 
		addClass : function(){console.log('addClass!')},
		push : Array.prototype.push,
		splice : Array.prototype.splice // 记得吗?类数组一旦拥有splice方法就会和数组长得很像!这个伪军!
	}
	arr2.addClass(); // addClass!
	arr2.push(4); // [span, span, span, 4, selector: "span"]

接下来解开Zepto_1.1.6的面纱

	// 立即执行函数私有化变量防止污染全局
	var Zepto = (function(){})()
	
	// Zepto挂载到window
	window.Zepto = Zepto
	
	// 先确保window.$为undefined再将window.$指向Zepto
	window.$ === undefined && (window.$ = Zepto)
  • 结果就是window.$和window.Zepto都指向立即执行函数的返回值

  • 然后看看立即执行函数执行完到底返回了什么

	var Zepto = (function(){
		var $;

		$ = function(selector, context){
			return zepto.init(selector, context)
		}
		
		return $;
	})()
  • 发现返回的是一个函数,我们平时执行代码 $() 就是执行返回的该函数,最终的返回结果是init方法执行结果的返回值,接下来我们顺藤摸瓜,研究init方法
	var Zepto = (function(){
		var $,
			zepto = {};
		
		zepto.init = function(selector, context) {
			var dom;
			
			// 先不分析Z函数
			if (!selector) return zepto.Z()
			// 如果selector是字符串又分好几种情况
			else if (typeof selector == 'string') {...}
			// 如果selector是个方法, 保证DOM加载完成后再执行该方法
			else if (isFunction(selector)) return $(document).ready(selector) 
			// 如果selector本身就是个Zepto对象就直接返回
			else if (zepto.isZ(selector)) return selector
			// else又分好几种情况(例如selector是数组对象等),结合API文档和源码注释就能看明白
			else {...}
			
			// 先不分析Z函数
			return zepto.Z(dom, selector);
		}

		$ = function(selector, context){
			return zepto.init(selector, context)
		}
		
		return $;
	})()
  • 发现返回的结果无外乎是zepto.Z方法的执行结果,接下来分析zepto.Z方法
	var Zepto = (function(){
		var $,
			zepto = {};
		
		zepto.Z = function(dom, selector) {
		    dom = dom || []
		    dom.__proto__ = $.fn
		    dom.selector = selector || ''
		    return dom // [span, span, span, 4, selector: "span"]
		}
		
		zepto.init = function(selector, context) {
			var dom;
			// 此处省略一大堆分支判断
			return zepto.Z(dom, selector);
		}

		$ = function(selector, context){
			return zepto.init(selector, context)
		}
		
		return $;
	})()
  • 回想平时调用Zepto方法返回的Zepto对象的样貌大致长这个样子 : [span, span, span, 4, selector: “span”],显然唯一有疑问的就是Z方法中dom.__proto__指向的$.fn是啥,接下来分析 $.fn 对象
	$.fn = { 一堆Zepto对象的常用方法 } // 哈哈,这里面就都是Zepto对象的方法了
	
	zepto.Z.prototype = $.fn // zepto.Z.prototype指向$.fn对象,这是由于$.fn写起来短嘛,而且Zepto中定义的函数$在JS中本质也是对象,就可以加属性

在这里插入图片描述

  • Zepto.js_1.2.0对如上返回数组的方式进行了改进,将zepto.Z方法中的逻辑抽象出来成为一个构造函数Z,该构造函数返回值类型为类数组,zepto.Z方法返回该构造函数的实例化对象,然后将该构造函数的原型指向$.fn
	zepto.Z.prototype = Z.prototype = $.fn

在这里插入图片描述

发布了49 篇原创文章 · 获赞 29 · 访问量 1898

猜你喜欢

转载自blog.csdn.net/Brannua/article/details/104678079
今日推荐