1. 程式人生 > >Zepto源代碼分析之二~三個API

Zepto源代碼分析之二~三個API

isa bsp scrip shee 字符串 ng- add child fin

因為時間關系:本次僅僅對這三個API($.camelCase、$.contains、$.each)方法進行分析
第一個方法變量轉駝峰:$.camelCase(‘hello-world-welcome‘); 源代碼: var camelize; /** * 字符串替換 * 使用replace第二個參數帶回調 */ camelize = function(str) { return str.replace(/-+(.)?/g, function(match, chr) { return chr ? chr.toUpperCase() : ‘‘; } );
}; /** * 將一組字符串變成駝峰命名法 */ $.camelCase = camelize;
第二個方法檢查父節點是否包括給定的dom節點,假設兩者是同樣節點,返回false:$.contains(parent, node) 源代碼: /**
* $.contains(parent, node);
* 檢查父節點是否包括給定的dom節點,假設兩者是同樣節點,返回false
* return {boolean} true/false
*/ $.contains = document.documentElement.contains ?

function (parent, node) { return parent !== node && parent.contains(node); } : function (parent, node) { while (node && (node = node.parentNode)) { if (node === parent) { return true; } } return false; };
第三個方法遍歷數組或以key-value值對方式遍歷對象。回調函數返回false時停止遍歷。 $.each(collection, function(index, item) { ... }) => collection 遍歷數組元素或以key-value值對方式遍歷對象。

回調函數返回false時停止遍歷。

$.each([‘a‘, ‘b‘, ‘c‘], function(index, item) { console.log(‘item %d is: %s‘, index, item); }); var hash = { name: ‘zepto.js‘, size: ‘micro‘ }; $.each(hash, function(key, value) { console.log(‘%s: %s‘, key, value); });
源代碼: /** * Zepto對象叠代器 * @param {object|array} elements 數據對象 * @param {function} callback 回調函數 * return {object|array} elements 數據對象 */ $.each = function(elements, callback) { var i; var key; // 數組檢測 if (likeArray(elements)) { for (i = 0; i < elements.length; i++) { if (callback.call(elements[i], i, elements[i]) === false) { return elements; } } } else { for (key in elements) { if (callback.call(elements[key], key, elements[key]) === false) { return elements; } } } return elements; };
三個方法最後頁面:demo.html <!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
<meta charset="utf-8" />
<title>Zepto源代碼分析</title>
<link rel="stylesheet" href="demo.css" type="text/css" />
</head>
<body>
<div id="test">
測試zepto源代碼
<span class="aa">22</span>
<span class="bb">2332</span>
</div>
<div class="wrap">content</div>
<script src="zepto-dev.js"></script>
<script>
console.log($(‘div‘));
console.log($(‘.aa‘));
console.log($(‘<div>這是測試內容</div>‘));
console.log($("<span />", { text: "測試測試111", id: "ceshi_111", css: { color: ‘red‘ } }));
Zepto(function($) {
console.log(‘Ready to Zepto!‘);
});
</script>
<script>
console.log($.camelCase(‘hello-there-body‘));
console.log($.contains($(‘#test‘)[0], $(‘.aa‘)[0]));
$.each([‘a‘, ‘b‘, ‘c‘], function(index, item) {
console.log(‘item %d is: %s‘, index, item);
});
var hash = { name: ‘zepto-dev.js‘, size: ‘micro‘ };
$.each(hash, function(key, value) {
console.log(‘%s: %s‘, key, value);
});
</script>
</body>
</html>
zepto-dev.js源代碼: var Zepto = (function() {
/**
* 變量初始化
*/
var $;
var zepto = {};
var fragmentRE = /^\s*<(\w+|!)[^>]*>/;
var singleTagRE = /^<(\w+)\s*\/?>(?:<\/\1>|)$/;
var tagExpanderRE = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig;
var undefined;
var emptyArray = [];
var slice = emptyArray.slice;
var cssNumber = {
‘column-count‘: 1,
‘columns‘: 1,
‘font-weight‘: 1,
‘line-height‘: 1,
‘opacity‘: 1,
‘z-index‘: 1,
‘zoom‘: 1
};
// 特殊屬性集合
var methodAttributes = [ ‘val‘, ‘css‘, ‘html‘, ‘text‘, ‘data‘, ‘width‘, ‘height‘, ‘offset‘ ];
var table = document.createElement(‘table‘);
var tableRow = document.createElement(‘tr‘);
var containers = {
‘tr‘: document.createElement(‘tbody‘),
‘tbody‘: table,
‘thead‘: table,
‘tfoot‘: table,
‘td‘: tableRow,
‘th‘: tableRow,
‘*‘: document.createElement(‘div‘)
};
var readyRE = /complete|loaded|interactive/;
var simpleSelectorRE = /^[\w-]*$/;
var class2type = {};
var toString = class2type.toString;
var camelize;
var isArray = Array.isArray || function(object) {
return object instanceof Array;
};


/**
* 檢測函數
*/
function type(obj) {
return obj == null ? String(obj) :
class2type[toString.call(obj)] || "object";
}
function isFunction(value) {
return type(value) == "function";
}
function isWindow(obj) {
return obj != null && obj == obj.window;
}
function isDocument(obj) {
return obj != null && obj.nodeType == obj.DOCUMENT_NODE;
}
function isObject(obj) {
return type(obj) == "object";
}
function isPlainObject(obj) {
return isObject(obj) && !isWindow(obj) && Object.getPrototypeOf(obj) == Object.prototype;
}
function likeArray(obj) {
return typeof obj.length == ‘number‘;
}
/**
* 字符串替換
* 使用replace第二個參數帶回調
*/
function camelize(str) {
return str.replace(/-+(.)?/g,
function(match, chr) {
console.log(chr);
return chr ? chr.toUpperCase() : ‘‘;
}
);
}
function dasherize(str) {
return str.replace(/::/g, ‘/‘)
.replace(/([A-Z]+)([A-Z][a-z])/g, ‘$1_$2‘)
.replace(/([a-z\d])([A-Z])/g, ‘$1_$2‘)
.replace(/_/g, ‘-‘)
.toLowerCase()
}
function maybeAddPx(name, value) {
return (typeof value == "number" && !cssNumber[dasherize(name)]) ?

value + "px" : value;
}
/**
* `$.zepto.fragment`須要一個html字符串和一個可選標記名來生成dom
* 產生的dom返回一個數組形式
* 該功能能夠被插件覆蓋
* 沒有覆蓋全部瀏覽器
*/
zepto.fragment = function(html, name, properties) {
var dom;
var nodes;
var container;
// 標簽特殊化處理
if (singleTagRE.test(html)) {
dom = $(document.createElement(RegExp.$1));
}
if (!dom) {
if (html.replace) {
html = html.replace(tagExpanderRE, "<$1></$2>");
}
if (name === undefined) {
name = fragmentRE.test(html) && RegExp.$1;
}
if (!(name in containers)) {
name = ‘*‘;
}
container = containers[name];
container.innerHTML = ‘‘ + html;
dom = $.each(slice.call(container.childNodes), function() {
container.removeChild(this);
});
}
if (isPlainObject(properties)) {
nodes = $(dom);
$.each(properties, function(key, value) {
if (methodAttributes.indexOf(key) > -1) {
nodes[key](value);
}
else {
nodes.attr(key, value);
}
});
}
return dom;
};
/**
* `$.zepto.Z`將給定`dom`節點數組的原型賦上`$.fn`提供的全部Zepto函數
* 請註意。`__proto__`不支持IE瀏覽器
*/
zepto.Z = function(dom, selector) {
dom = dom || [];
dom.__proto__ = $.fn;
dom.selector = selector || ‘‘;
return dom;
};
/**
* `$.zepto.isZ`檢查給定的對象是一個Zepto的集合,可被插件覆蓋
*/
zepto.isZ = function(object) {
return object instanceof zepto.Z;
};
/**
* `$.zepto.init`是Zepto借鑒jQuery的`$.fn.init`方法
* 採用css選擇器和一個可選的上下文(處理各種特殊情況)
* 該方法可被插件覆蓋
*/
zepto.init = function(selector, context) {
// 假設沒有給出,返回一個空的Zepto集合
if (!selector) {
return zepto.Z();
}
// 檢測字符串類型
else if (typeof selector == ‘string‘) {
selector = selector.trim();
/**
* 假設是一個HTML片段,創建節點註意,在chrome21和FF15版本號。
* DOM錯誤12不是以<被拋出
*/
if (selector[0] == ‘<‘ && fragmentRE.test(selector)) {
dom = zepto.fragment(selector, RegExp.$1, context);
selector = null;
// 假設存在一個上下文環境,建立收集。並從中選擇節點
} else if (context !== undefined) {
return $(context).find(selector);
// 假設是一個css選擇器。用它來選擇節點
} else {
dom = zepto.qsa(document, selector);
}
// 假設一個函數存在,在domready就緒後觸發
} else if (isFunction(selector)) {
return $(document).ready(selector);
}
// 假設zepto已經收集給出,直接返回
else if (zepto.isZ(selector)) {
return selector;
} else {
// 假設節點已經為數組,進行聚合
if (isArray(selector)) {
dom = compact(selector);
}
// 包裝DOM節點
else if (isObject(selector)) {
dom = [selector];
selector = null;
}
// 假設是一個HTML片段,對該片段創建節點
else if (fragmentRE.test(selector)) {
dom = zepto.fragment(selector.trim(), RegExp.$1, context);
selector = null;
}
// 假設存在上下文環境,先建立收集。並從中選擇節點
else if (context !== undefined) {
return $(context).find(selector);
}
// 假設是一個css選擇器。用它來選擇節點
else {
dom = zepto.qsa(document, selector);
}
}
// 對發現的節點創建一個新的Zepto集合
return zepto.Z(dom, selector);
};
// `$`作為Zepto的元對象。當調用`$`該函數將轉由`$.zepto.init`處理
$ = function(selector, context) {
return zepto.init(selector, context);
};
/**
* `$.zepto.qsa`是Zepto的css選擇器,使用document.querySelectorAll及特殊情況
* 可被插件覆蓋
*/
zepto.qsa = function(element, selector) {
var found;
var maybeID = (selector[0] == ‘#‘);
var maybeClass = !maybeID && selector[0] == ‘.‘;
// 確認下標從1開始後的字符串
var nameOnly = maybeID || maybeClass ?

selector.slice(1) : selector;
var isSimple = simpleSelectorRE.test(nameOnly);
return (isDocument(element) && isSimple && maybeID) ?
((found = element.getElementById(nameOnly)) ? [found] : []) :
slice.call((isSimple && !maybeID) ?
maybeClass ?

element.getElementsByClassName(nameOnly) : // class名稱
element.getElementsByTagName(selector) : // tag名稱
element.querySelectorAll(selector) // 查詢全部匹配到的
);
};
/**
* $.contains(parent, node); 演示樣例$.contains($(‘#test‘)[0], $(‘.aa‘)[0])
* 檢查父節點是否包括給定的dom節點,假設兩者是同樣節點,返回false
* return {boolean} true/false
*/
$.contains = document.documentElement.contains ?
function (parent, node) {
return parent !== node && parent.contains(node);
} :
function (parent, node) {
while (node && (node = node.parentNode)) {
if (node === parent) {
return true;
}
}
return false;
};
function setAttribute(node, name, value) {
value == null ? node.removeAttribute(name) : node.setAttribute(name, value);
}
// 函數參數
function funcArg(context, arg, idx, payload) {
return isFunction(arg) ? arg.call(context, idx, payload) : arg;
}
$.type = type;
$.isFunction = isFunction;
$.isWindow = isWindow;
$.isArray = isArray;
$.isPlainObject = isPlainObject;
$.camelCase = camelize;
/**
* Zepto對象叠代器
* @param {object|array} elements 數據對象
* @param {function} callback 回調函數
* return {object|array} elements 數據對象
*/
$.each = function(elements, callback) {
var i;
var key;
// 數組檢測
if (likeArray(elements)) {
for (i = 0; i < elements.length; i++) {
if (callback.call(elements[i], i, elements[i]) === false) {
return elements;
}
}
} else {
for (key in elements) {
if (callback.call(elements[key], key, elements[key]) === false) {
return elements;
}
}
}
return elements;
};
// 配置類型映射
$.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
class2type["[object " + name + "]"] = name.toLowerCase();
});
/**
* 定義的方法,適用於全部的Zepto對象
*/
$.fn = {
ready: function(callback) {
// 檢查document.body存在且文檔渲染完畢
if (readyRE.test(document.readyState) && document.body) {
callback($);
} else {
document.addEventListener(‘DOMContentLoaded‘, function() {
callback($);
}, false);
}
},
each: function(callback) {
emptyArray.every.call(this, function(el, idx) {
return callback.call(el, idx, el) !== false;
});
return this;
},
text: function(text) {
return 0 in arguments ?


this.each(function(idx) {
var newText = funcArg(this, text, idx, this.textContent);
this.textContent = (newText == null) ? ‘‘ : ‘‘ + newText;
}) :
(0 in this ? this[0].textContent : null);
},
attr: function(name, value) {
var result;
return (typeof name == ‘string‘ && !(1 in arguments)) ?
(!this.length || this[0].nodeType !== 1 ? undefined :
(!(result = this[0].getAttribute(name)) && name in this[0]) ? this[0][name] : result
) :
this.each(function(idx){
if (this.nodeType !== 1) {
return;
}
if (isObject(name)) {
for (key in name) {
setAttribute(this, key, name[key]);
}
} else {
setAttribute(this, name, funcArg(this, value, idx, this.getAttribute(name)));
}
});
},
// css屬性設置
css: function(property, value) {
if (arguments.length < 2) {
var element = this[0];
var computedStyle = getComputedStyle(element, ‘‘);
if (!element) {
return;
}
if (typeof property == ‘string‘) {
return element.style[camelize(property)] || computedStyle.getPropertyValue(property);
} else if (isArray(property)) {
var props = {};
$.each(isArray(property) ? property : [property], function(_, prop) {
props[prop] = (element.style[camelize(prop)] || computedStyle.getPropertyValue(prop));
});
return props;
}
}
var css = ‘‘;
if (type(property) == ‘string‘) {
if (!value && value !== 0) {
this.each(function() {
this.style.removeProperty(dasherize(property));
});
} else {
css = dasherize(property) + ":" + maybeAddPx(property, value);
}
} else {
for (var key in property) {
if (!property[key] && property[key] !== 0) {
this.each(function() {
this.style.removeProperty(dasherize(key));
});
} else {
css += dasherize(key) + ‘:‘ + maybeAddPx(key, property[key]) + ";";
}
}
}
return this.each(function() {
this.style.cssText += ‘;‘ + css;
});
}
};
// 繼承
zepto.Z.prototype = $.fn;
$.zepto = zepto;
return $;
})();


// 全局變量接口
window.Zepto = Zepto;
window.$ === undefined && (window.$ = Zepto);


瀏覽器輸出結果: 技術分享 技術分享










Zepto源代碼分析之二~三個API