parseHtml
在 jq 中有有一个创建 DOM 对象的方法
$( ‘
’ )[ 0 ] 或者 .get( 0 )$( ‘#d’ )
$( ‘html’ )
$( dom )
( obj )
$( func )
function $ ( arg ) {
if ( arg 是 选择器 ) {
} else if ( arg 是函数 ) {
} else if ( arg 是 DOM 对象 ) {
} ...
...
}
如何实现 parseHTML 函数
方法的定义( 参数与返回值 )
参数: 字符串
返回: DOM 数组
有两种方法
第一种方法使用 createElement
第二种方法使用 innerHTML 技巧
两种方法虽然都可以但是在不同浏览器上性能是有区别的, chrome差不多,但是 IE 中第一种方法相对时间消耗会长一些,所以权衡之下,还是用第二种方法 innerHTML 比较好
性能测试:
第一种方法使用 createElement
// 在 js 中有一个对象 Date 它表示时间,
// 使用 + new Date() 可以得到当前的毫秒数
var start = +new Date;
// 第 1 种方法
// 使用 documentfragment 的对象 来表示内存节点
var dfg = document.createDocumentFragment();
for ( var i = 0; i < 10000; i++ ) {
var div = document.createElement( 'div' );
var txt = document.createTextNode( 'Hello JavaScript - ' + i );
div.style.border = '1px dashed red';
div.style.margin = '5px';
div.style.padding = '5px';
div.style.backgroundColor = 'pink';
div.appendChild( txt );
dfg.appendChild( div );
}
document.body.appendChild( dfg );
var end = +new Date;
console.log( end - start );
第二种方法使用 innerHTML 技巧
var start = +new Date;
var str = [];
for ( var i = 0; i < 10000; i++ ) {
str.push('<div style="border:1px dashed red; background-color: pink; margin: 5px; padding: 5px;>Hello JavaScript - ' + i + '</div>');
}
document.body.innerHTML = str.join( '' );
var end = +new Date;
console.log( end - start );
实现 parseHTML
function parseHTML( html ) {
// 1, 需要一个容器
var div = document.createElement( 'div' );
// 2, 将字符串赋值给该容器的 innerHTML 属性
div.innerHTML = html;
// 3, 遍历 其 childeNodes
var res = [];
for ( var i = 0; i < div.childNodes.length; i++ ) {
res.push( div.childNodes[ i ] );
}
// 4, 返回元素数组
return res;
}
var list = parseHTML( '<div> 你好, 我是一个呗创建出来的 div </div><p>p</p>' )
document.body.appendChild( list[ 0 ] );
框架结构的搭建
抽象框架的雏形
给数组原型添加appendTo
function $( html ) {
// 创建一个 div
var div = document.createElement( 'div' );
// 把字符串填充进来
div.innerHTML = html;
var res = []; // 声明数组保存返回的子节点
for( var i = 0; i < div.childNodes.length; i++ ) {
res.push( div.childNodes[ i ] ); // 遍历将子节点添加进去
}
// 添加方法
res.appendTo = function( dom ) {
for ( var i = 0; i < this.length; i++ ) {
dom.appendChild( this[ i ] );
}
}
return res; // 字符串转化成 res
}
// 需要数组提供 appendTo 方法, 给数组原型提供该方法. 至少给 res 提供
// 参数是一个 DOM 对象, 要求将数组中存储的每一个 DOM 对象加到它上面去
$( '<div style="border: 1px dashed red; background-color: pink;">123</div>' ).appendTo( document.body );
不应该给数组提供新的成员
这样无法实现复用
实际上应该是有一个构造函数,创建一个对象,该对象应该是一个伪数组,而由于有构造函数,就有原型,所有的方法应该加入到 原型中
function $( html ) {
return new F( html );
}
function F( html ) {
// dom 数组 存到了 实例伪数组 中
push.apply( this, parseHTML( html ) );
}
F.prototype = {
length: 0, // ??
appendTo: function( dom ) {}
}
基本抽象结构
function parseHTML ( html ) {
var div = document.createElement( 'div' );
div.innerHTML = html;
var res = [];
for ( var i = 0; i < div.childNodes.length; i++ ) {
res.push( div.childNodes[ i ] );
}
return res;
}
function $ ( html ) {
return new F( html );
}
function F( html ) {
console.log( this );
[].push.apply( this, parseHTML( html ) );
}
F.prototype = {
constructor: F,
appendTo: function ( dom ) {
for ( var i = 0; i < this.length; i++ ) {
dom.appendChild( this[ i ] );
}
}
}
var $1 = $( '<div>123</div><div>123</div><div>123</div>' );
var $2 = $( '<div>123</div><div>123</div>' );
$1.appendTo( document.body );
$2.appendTo( document.body );
jq 中 封装的更加完整
function jepson(...) {
new F(...)
}
将所有的功能都放到了 F 的 prototype 中,
那么如果需要对框架进行扩展, 需要引入两个名字到 外面
一个是 jepson, 一个是 F
增加新功能就是
F.prototype.新方法 = function() {...}
jq 将 F 替换为 jepson 原型中的一个方法
function jepson() {
return new jepson.prototype.init(...);
}
jepson.prototype = {
init: function( ... ) { ... }
}
// 新问题出现了
// 如何为其扩展
// 在 jq 中多加一个方法是利用 给 jepson.prototype 添加方法来实现的
// 问题: 现在给 jepson.prototype 添加方法, 可以让当前对象使用该方法吗 ?
// 对象是被 jepson.prototype.init 所创建的, 因此只需
// 要让 jepson.prototype.init.prototype 与 jepson.prototype 指向相同即可
有那么一点像了↓
function parseHTML ( html ) {
var div = document.createElement( 'div' );
div.innerHTML = html;
var res = [];
for ( var i = 0; i < div.childNodes.length; i++ ) {
res.push( div.childNodes[ i ] );
}
return res;
}
// 暴露出去的函数
function jepson ( html ) {
// 通过 原型方法 init 方法来构造
return new jepson.prototype.init( html );
}
jepson.prototype = {
constructor: jepson,
length: 0,
init: function ( html ) {
[].push.apply( this, parseHTML( html ) );
},
appendTo: function ( dom ) {
for ( var i = 0; i < this.length; i++ ) {
dom.appendChild( this[ i ] );
}
}
}
// 构造出来的对象,修改了原型链
jepson.prototype.init.prototype = jepson.prototype;
// 使用
var i = jepson( '<div>123</div>' );
i.appendTo( document.body );
jepson.prototype 取个别名,fn 方便的使用.代码压缩
function parseHTML ( html ) {
var div = document.createElement( 'div' );
div.innerHTML = html;
var res = [];
for ( var i = 0; i < div.childNodes.length; i++ ) {
res.push( div.childNodes[ i ] );
}
return res;
}
function jepson ( html ) {
return new jepson.prototype.init( html );
}
jepson.fn = jepson.prototype = {
constructor: jepson,
length: 0,
init: function ( html ) {
[].push.apply( this, parseHTML( html ) );
},
appendTo: function ( dom ) {
for ( var i = 0; i < this.length; i++ ) {
dom.appendChild( this[ i ] );
}
}
}
jepson.fn.init.prototype = jepson.prototype;
封装优化
(function ( window, undefined ) {
function parseHTML ( html ) {
var div = document.createElement( 'div' );
div.innerHTML = html;
var res = [];
for ( var i = 0; i < div.childNodes.length; i++ ) {
res.push( div.childNodes[ i ] );
}
return res;
}
function jepson ( html ) {
return new jepson.prototype.init( html );
}
jepson.fn = jepson.prototype = {
constructor: jepson,
length: 0,
init: function ( html ) {
[].push.apply( this, parseHTML( html ) );
},
appendTo: function ( dom ) {
for ( var i = 0; i < this.length; i++ ) {
dom.appendChild( this[ i ] );
}
}
}
jepson.fn.init.prototype = jepson.prototype;
window.jepson = window.J = jepson;
})( window );
整合选择器与框架
改良 init 函数,调整顺序, 添加混入
(function ( window, undefined ) {
var push = [].push;
function itcast ( html ) {
return new itcast.fn.init( html );
}
itcast.fn = itcast.prototype = {
constructor: itcast,
length: 0,
init: function ( html ) {
// [].push.apply( this, parseHTML( html ) );
if ( html == null || html === '' ) {
return;
}
if ( typeof html === 'function' ) {
}
if ( itcast.isString( html ) ) {
if ( /^</.test( html ) ) {
push.apply( this, parseHTML( html ) );
} else {
// 选择器
// select( html );
push.apply( this, itcast.select( html ) );
}
}
}
}
itcast.fn.init.prototype = itcast.fn;
// 添加可扩展的方法 extend
itcast.extend = itcast.fn.extend = function ( obj ) {
for ( var k in obj ) {
// if ( k 是 obj 自己提供的方法 ) obj.hasOwnProperty( k )
this[ k ] = obj[ k ];
}
}
// select 引擎专门用于搜素元素
var select =
(function () {
var push = [].push;
// 如果出现了错误那么就需要重写 push
try {
var div = document.createElement( 'div' );
div.innerHTML = '<p></p>';
var arr = [];
push.apply( arr, div.getElementsByTagName( 'p' ));
} catch ( e ) {
push = {
apply: function ( array1, array2 ) {
for ( var i = 0; i < array2.length; i++ ) {
array1[ array1.length++ ] = array2[ i ];
}
}
};
}
// 正则表达式
var rnative = /\{\s*\[native/;
var rtrim = /^\s+|\s+$/g;
// 1 2 3 4
var rbaseselector = /^(?:\#([\w\-]+)|\.([\w\-]+)|(\*)|(\w+))$/;
// 基本函数, support 对象, 验证 qsa 与 byclass
var support = {};
support.qsa = rnative.test( document.querySelectorAll + '' );
support.getElementsByClassName =
rnative.test( document.getElementsByClassName + '' );
support.trim = rnative.test( String.prototype.trim + '' );
support.indexOf = rnative.test( Array.prototype.indexOf + '' );
// 基本方法
function getByClassName ( className, node ) {
node = node || document;
var allElem, res = [], i;
if ( support.getElementsByClassName ) {
return node.getElementsByClassName( className );
} else {
allElem = node.getElementsByTagName( '*' );
for ( i = 0; i < allElem.length; i++ ) {
if ( allElem[ i ].className === className ) {
res.push( allElem[ i ] );
}
}
return res;
}
}
// 自定义实现 trim 方法
var myTrim = function ( str ) {
// 表示两端去空格, 然后返回去除空格的结果
if ( support.trim ) {
return str.trim();
} else {
// 自定义实现
return str.replace( rtrim, '' );
}
}
var myIndexOf = function ( array, search, startIndex ) {
startIndex = startIndex || 0;
if ( support.indexOf ) {
return array.indexOf( search, startIndex );
} else {
for ( var i = startIndex; i < array.length; i++ ) {
if ( array[ i ] === search ) {
return i;
}
}
return -1;
}
}
var unique = function ( array ) {
var resArray = [], i = 0;
for ( ; i < array.length; i++ ) {
if ( myIndexOf( resArray, array[ i ] ) == -1 ) {
resArray.push( array[ i ] );
}
}
return resArray;
}
function basicSelect ( selector, node ) {
node = node || document;
var m, res;
if ( m = rbaseselector.exec( selector ) ) {
if ( m[ 1 ] ) { // id
res = document.getElementById( m[ 1 ] );
if ( res ) {
return [ res ];
} else {
return [];
}
} else if ( m[ 2 ] ) { // class
// return node.getElementsByClassName( m[ 2 ] );
return getByClassName( m[ 2 ], node );
} else if ( m[ 3 ] ) {
return node.getElementsByTagName( m[ 3 ] );
} else if ( m[ 4 ] ) {
return node.getElementsByTagName( m[ 4 ] );
}
}
return [];
}
function select2 ( selector, results ) {
results = results || [];
var selectors = selector.split( ' ' );
// 假定 'div p .c'
var arr = [], node = [ document ];
for ( var j = 0; j < selectors.length; j++ ) {
for ( var i = 0; i < node.length; i++ ) {
push.apply( arr, basicSelect( selectors[ j ], node[ i ] ));
}
node = arr;
arr = [];
}
push.apply( results, node );
return results;
}
function select ( selector, results ) {
results = results || [];
var m, temp, selectors, i, subselector;
if ( typeof selector != 'string' ) return results;
// 表明参数都没有问题, 接下来就是如何选择
// 首先判断 qsa 是否可用
// 然后再 一步步的 自己实现
if ( support.qsa ) {
push.apply( results, document.querySelectorAll( selector ) );
} else {
// 不存在再来考虑自己实现
selectors = selector.split( ',' );
// 循环
for ( i = 0; i < selectors.length; i++ ) {
subselector = myTrim( selectors[ i ] );
// 接下来就是 处理 subselector
if ( rbaseselector.test( subselector ) ) {
// 基本选择器
push.apply( results, basicSelect( subselector ) );
} else {
// select2 函数
select2( subselector, results );
}
}
}
// 返回 result
return unique( results );
}
return select;
})();
itcast.select = select; // 在需要的时候 可以使用第三方的 选择器引擎
// 需要一些判断的方法
itcast.extend({
isString: function ( data ) {
return typeof data === 'string';
}
});
// DOM 操作的方法
// 将字符串转换为 DOM 对象的函数
var parseHTML = (function () {
var div = document.createElement( 'div' );
function parseHTML ( html ) {
div.innerHTML = html;
var res = [];
for ( var i = 0; i < div.childNodes.length; i++ ) {
res.push( div.childNodes[ i ] );
}
div.innerHTML = '';
return res;
}
return parseHTML;
})();
itcast.fn.extend({
appendTo: function ( dom ) {
for ( var i = 0; i < this.length; i++ ) {
dom.appendChild( this[ i ] );
}
}
});
window.itcast = window.I = itcast;
})( window )