jQuery源码——.html()方法原理解析
在将字符串转化为html碎片时,一般会将字符串作为容器的innerHTML属性赋值。但innerHTML有很多局限性,比如我们想转化的字符串中有<script>标签并且包含一个立即执行的函数,如果将此字符串通过innerHTML转化为html碎片,<script>标签中的函数并不会被执行。
jQuery中的.html()函数可以弥补innerHTML的缺陷,我们看下这个方法是如何实现的。
其实原理很简单:正则匹配<script>标签,获取js函数,然后用eval()函数解析。jQuery在处理此工程中有几个细节值得学习。
首先看一下html()函数的主入口:
html: function( value ) {
return access( this, function( value ) {
var elem = this[ 0 ] || {},
i = 0,
l = this.length; if ( value === undefined ) {
return elem.nodeType === 1 ?
elem.innerHTML.replace( rinlinejQuery, "" ) :
undefined;
} // See if we can take a shortcut and just use innerHTML
if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
( support.htmlSerialize || !rnoshimcache.test( value ) ) &&
( support.leadingWhitespace || !rleadingWhitespace.test( value ) ) &&
!wrapMap[ (rtagName.exec( value ) || [ "", "" ])[ 1 ].toLowerCase() ] ) { value = value.replace( rxhtmlTag, "<$1></$2>" ); try {
for (; i < l; i++ ) {
// Remove element nodes and prevent memory leaks
elem = this[i] || {};
if ( elem.nodeType === 1 ) {
jQuery.cleanData( getAll( elem, false ) );
elem.innerHTML = value;
}
} elem = 0; // If using innerHTML throws an exception, use the fallback method
} catch(e) {}
} if ( elem ) {
this.empty().append( value );
}
}, null, value, arguments.length );
},
1. html()函数返回一个单例闭包access()函数,避免作用域污染;
2. 第14行,首先确定value是string类型,并且用 rnoInnerhtml.test( value ) 正则匹配value中是否包含<script>标签;
3. 第26行,首先清理容器的内容,然后将value作为容器的innerHTML属性赋值,然后将代表容器的局部变量elem赋值为0,跳过37行逻辑。有些同学会疑惑,将elem赋值为0为什么不会影响dom元素?这里面涉及到JavaScript中值类型和引用类型的区别,请自行查阅相关资料;
4. 第38行,如果value中包括<script>标签,则用append()方法进行后续操作。
那么append()函数是怎么处理的呢?
append: function() {
return this.domManip( arguments, function( elem ) {
if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
var target = manipulationTarget( this, elem );
target.appendChild( elem );
}
});
},
append()函数调用domManip()函数,回调函数中的参数elem是经domManip()函数处理后的documentFragment,domManip()内部代码如下:
domManip: function( args, callback ) { // Flatten any nested arrays
args = concat.apply( [], args ); var first, node, hasScripts,
scripts, doc, fragment,
i = 0,
l = this.length,
set = this,
iNoClone = l - 1,
value = args[0],
isFunction = jQuery.isFunction( value ); // We can't cloneNode fragments that contain checked, in WebKit
if ( isFunction ||
( l > 1 && typeof value === "string" &&
!support.checkClone && rchecked.test( value ) ) ) {
return this.each(function( index ) {
var self = set.eq( index );
if ( isFunction ) {
args[0] = value.call( this, index, self.html() );
}
self.domManip( args, callback );
});
} if ( l ) {
fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this );
first = fragment.firstChild; if ( fragment.childNodes.length === 1 ) {
fragment = first;
} if ( first ) {
scripts = jQuery.map( getAll( fragment, "script" ), disableScript );
hasScripts = scripts.length; // Use the original fragment for the last item instead of the first because it can end up
// being emptied incorrectly in certain situations (#8070).
for ( ; i < l; i++ ) {
node = fragment; if ( i !== iNoClone ) {
node = jQuery.clone( node, true, true ); // Keep references to cloned scripts for later restoration
if ( hasScripts ) {
jQuery.merge( scripts, getAll( node, "script" ) );
}
} callback.call( this[i], node, i );
} if ( hasScripts ) {
doc = scripts[ scripts.length - 1 ].ownerDocument; // Reenable scripts
jQuery.map( scripts, restoreScript ); // Evaluate executable scripts on first document insertion
for ( i = 0; i < hasScripts; i++ ) {
node = scripts[ i ];
if ( rscriptType.test( node.type || "" ) &&
!jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) { if ( node.src ) {
// Optional AJAX dependency, but won't run scripts if not present
if ( jQuery._evalUrl ) {
jQuery._evalUrl( node.src );
}
} else {
jQuery.globalEval( ( node.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) );
}
}
}
} // Fix #11809: Avoid leaking memory
fragment = first = null;
}
} return this;
}
1. 第28行-55行,生成docmentFragment,并将<script>节点克隆以便后续的解析执行;
2. 第57行-79行,执行<script>内部的代码,注意75行的 node.text || node.textContent || node.innerHTML || "" ,这是兼容写法,即获取<script>标签内部的文本。
3. 第69行,如果<script>标签是引用外部资源,则请求资源url;
3. 第75行,如果<script>标签是行内代码,则调用globaleEval()函数执行<script>内部的逻辑,代码如下:
globalEval: function( data ) {
if ( data && jQuery.trim( data ) ) {
// We use execScript on Internet Explorer
// We use an anonymous function so that context is window
// rather than jQuery in Firefox
( window.execScript || function( data ) {
window[ "eval" ].call( window, data );
} )( data );
}
},
jQuery源码——.html()方法原理解析的更多相关文章
- Spring源码:IOC原理解析(一)
版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习! IOC(Inversion of Control),即控制反转,意思是将对象的创建和依赖关系交给第三方容器处理,我们要用的时候告诉容器我们 ...
- Spring源码:IOC原理解析(二)
版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习! 接着上一章节的内容,我们来分析当new一个FileSystemXmlApplicationContext对象的时候,spring到底做了那 ...
- 持久层Mybatis3底层源码分析,原理解析
Mybatis-持久层的框架,功能是非常强大的,对于移动互联网的高并发 和 高性能是非常有利的,相对于Hibernate全自动的ORM框架,Mybatis简单,易于学习,sql编写在xml文件中,和代 ...
- AJAX JSONP源码实现(原理解析)
关于JSONP以及跨域问题,请自行搜索. 本文重点给出AJAX JSONP的模拟实现代码,代码中JSONP的基本原理也一目了然. <html xmlns="http://www.w3. ...
- jQuery 源码解析二:jQuery.fn.extend=jQuery.extend 方法探究
终于动笔开始 jQuery 源码解析第二篇,写文章还真是有难度,要把自已懂的表述清楚,要让别人听懂真的不是一见易事. 在 jQuery 源码解析一:jQuery 类库整体架构设计解析 一文,大致描述了 ...
- jQuery 2.0.3 源码分析Sizzle引擎解析原理
jQuery 2.0.3 源码分析Sizzle引擎 - 解析原理 声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 先来回答博友的提问: 如何解析 div > p + ...
- jquery 源码解析
静态与实力方法共享设计 遍历方法 $(".a").each() //作为实例方法存在 $.each() //作为静态方法存在 Jquery源码 jQuery.prototype = ...
- jQuery源码:从原理到实战
jQuery源码:从原理到实战 jQuery选择器对象 $(".my-class"); document.querySelectorAll*".my-class" ...
- JQuery源码解析(一)
写在前面:本<JQuery源码解析>系列是基于一些前辈们的文章进行进一步的分析.细化.修改而写出来的,在这边感谢那些慷慨提供科普文档的技术大拿们. 要查阅JQ的源文件请下载开发版的JQ.j ...
随机推荐
- 创建JavaScript函数的几种方式
window.onload = function() { // console.log('ok'); //正规的创建函数 function test(abc, d) { return abc(d); ...
- ----关于JS中迭代的三个“FOR”----
for each...in 使用一个变量迭代一个对象的所有属性值,对于每一个属性值,有一个指定的语句块被执行. 一些对象的内置属性是无法被遍历到的,包括所有的内置方法,例如String对象的index ...
- list.get(0)防止NPE
package com.chuangjin.p2p; import com.chuangjin.core.utils.JsonUtils; import java.util.ArrayList; im ...
- node-sass 不能正常安装解决办法
web前端在安装node包时,总是报错,究其原因是node-sass没有被正常安装. 根本原因是国内网络的原因. 最终的解决方法是通过淘宝的npm镜像安装node-sass 首先安装cnpm npm ...
- 关于在spring boot里使用Thymeleaf模板的application.properties配置
spring.thymeleaf.cache=false spring.thymeleaf.encoding=utf- spring.thymeleaf.mode=HTML5 spring.thyme ...
- 关于下载calipso数据集以及用python将其读到记事本小结
今天终于把老板交代的事情忙完了,对于我这位计算机语言的小白来说,其中的艰辛不用说,一把辛酸泪啊!在有计算机语言经验的老手而言,我这些问题似乎也不能算是问题,但我却卡了很久,对此,想把自己所遇到的困难和 ...
- 4yue 22
1 # 1 . 进程 线程 协程 之间的相同点和不同点 #相同点:都能帮助我们实现并发操作,规避IO时间,提高执行效率 #进程:内存隔离 操作系统级别 可以利用多核(高计算) 计算机中资源分配的最小单 ...
- homework1-201521410029
姓名:孙浩学号: 201521410029指导教师:高见 实验日期:2018年8月9日 1. 虚拟机安装与调试 安装好xp和kali虚拟机之后,查看这三者(包括主机)的i ...
- HTML的Tomcat
修改D:\software\apache-tomcat-8.0.44\webapps\ROOT\WEB-INF\web.xml: <?xml version="1.0" en ...
- ES6中的箭头函数和普通函数有什么区别?
1.普通函数中的this总是指向调用它的那个对象, 箭头函数没有自己的this,他的this永远指向其定义环境,任何方法都改变不了其指向,如call().bind().apply().(正是因为它没有 ...