在将字符串转化为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()方法原理解析的更多相关文章

  1. Spring源码:IOC原理解析(一)

    版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习! IOC(Inversion of Control),即控制反转,意思是将对象的创建和依赖关系交给第三方容器处理,我们要用的时候告诉容器我们 ...

  2. Spring源码:IOC原理解析(二)

    版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习! 接着上一章节的内容,我们来分析当new一个FileSystemXmlApplicationContext对象的时候,spring到底做了那 ...

  3. 持久层Mybatis3底层源码分析,原理解析

    Mybatis-持久层的框架,功能是非常强大的,对于移动互联网的高并发 和 高性能是非常有利的,相对于Hibernate全自动的ORM框架,Mybatis简单,易于学习,sql编写在xml文件中,和代 ...

  4. AJAX JSONP源码实现(原理解析)

    关于JSONP以及跨域问题,请自行搜索. 本文重点给出AJAX JSONP的模拟实现代码,代码中JSONP的基本原理也一目了然. <html xmlns="http://www.w3. ...

  5. jQuery 源码解析二:jQuery.fn.extend=jQuery.extend 方法探究

    终于动笔开始 jQuery 源码解析第二篇,写文章还真是有难度,要把自已懂的表述清楚,要让别人听懂真的不是一见易事. 在 jQuery 源码解析一:jQuery 类库整体架构设计解析 一文,大致描述了 ...

  6. jQuery 2.0.3 源码分析Sizzle引擎解析原理

    jQuery 2.0.3 源码分析Sizzle引擎 - 解析原理 声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 先来回答博友的提问: 如何解析 div > p + ...

  7. jquery 源码解析

    静态与实力方法共享设计 遍历方法 $(".a").each() //作为实例方法存在 $.each() //作为静态方法存在 Jquery源码 jQuery.prototype = ...

  8. jQuery源码:从原理到实战

    jQuery源码:从原理到实战 jQuery选择器对象 $(".my-class"); document.querySelectorAll*".my-class" ...

  9. JQuery源码解析(一)

    写在前面:本<JQuery源码解析>系列是基于一些前辈们的文章进行进一步的分析.细化.修改而写出来的,在这边感谢那些慷慨提供科普文档的技术大拿们. 要查阅JQ的源文件请下载开发版的JQ.j ...

随机推荐

  1. 关于学习python的想法

    选择学习python,就是一次对自己的挑战.自己之前并没有python的基础,只是学习了一点C语言的知识.对于这个课程了解的不是很多,只是上学期在网上自学了一点,自己也是对这门语言感兴趣,所以这个学期 ...

  2. c语言基础课第一次作业

    1)大学和高中最大的不同是没有人天天看着你,请看大学理想的师生关系是?有何感想? 通过阅读邹欣老师的博客,了解到了老师心中理想的师生关系是(健身教练与健身学员).在初中,高中我们一直都是填鸭式教育,像 ...

  3. appium解决每次运行都需要安装Unlock以及AppiumSetting的问题

    这是我用appium遇到的第三个坑?之前因为环境的问题,chromedriver驱动总是安装不对,后来发现是因为路径的原因.解决之后,现在出现新的问题,那就是“appium每次运行都要去重新安装Unl ...

  4. 关于Selenium.common.exceptions.WebDriverException: Message: Invalid locator strategy: css selector 的问题

    在执行脚本时报Selenium.common.exceptions.WebDriverException: Message: Invalid locator strategy: css selecto ...

  5. PS扣签名

    1.用PS打开签名: 2.在图层界面切换到[通道],选择一个黑白分明或者明暗明显的签名图通道.按着“Ctrl”键就会显示出手指和方块的图标,按键的同时左击一下选择的通道: 3.切换到第一项的[图层]界 ...

  6. 现网环境业务不影响,但是tomcat启动一直有error日志,ERROR org.apache.catalina.startup.ContextConfig- Unable to process Jar entry [module-info.class] from Jar [jar:file:/home/iufs/apache-tomcat/webapps/iufs/WEB-INF/lib/asm

    完整的错误日志信息: 2019-03-19 15:30:42,021 [main] INFO org.apache.catalina.core.StandardEngine- Starting Ser ...

  7. rem 是如何实现自适应布局的

    摘要:rem是相对于根元素<html>,这样就意味着,我们只需要在根元素确定一个px字号,则可以来算出元素的宽高.本文讲的是如何使用rem实现自适应.· rem这是个低调的css单位,近一 ...

  8. 2019浙大校赛--G--Postman(简单思维题)

    一个思维水题 题目大意为,一个邮递员要投递N封信,一次从邮局来回只能投递K封.求最短的投递总距离.需注意,最后一次投递后无需返回邮局. 本题思路要点: 1.最后一次投递无需返回邮局,故最后一次投递所行 ...

  9. 关于数据库连接时URL的问题

    最近在写一个简单的增删改查的代码时,遇到保存的中文都会变成问号(?),由于刚开始只是一些数据的保存,所以认为之后只要对数据库的编码进行修改即可,但是后来要对数据进行查找的时候发现根本查找不到, 当时用 ...

  10. How to setup Visual Studio without pain

    Visual Studio (VS) can be very hard to install. If you are lucky, one whole day may be enough to ins ...