JQuery源码分析(五)


分离构造器

通过new 操作符构建一个对象,一般经过四部:

A.创建一个新对象

B.将构造函数的作用域赋给新对象(所以this就指向了这个新对象)

C.执行构造函数中的代码

D.返回这个新对象

最后一点就说明了,我们只要返回一个新对象即可。其实new操作符主要是把原型链跟实例的this关联起来,这才是最关键的一点,

所以我们如果需要原型链就必须要new操作符来进行处理。否则this则变成window对象了。

我们来剖析下jQuery的这个结构,以下是我们常见的类式写法:

var $$ = ajQuery = function(selector) {
    this.selector = selector;
    return this
}
ajQuery.fn = ajQuery.prototype = {
    selectorName:function(){
        return this.selector;
    },
    constructor: ajQuery
}
var a = new $$('aaa');  //实例化
a.selectorName() //aaa //得到选择器名字

首先改造jQuery无new的格式,我们可以通过instanceof判断this是否为当前实例:

var $$ = ajQuery = function(selector) {
    if(!(this instanceof ajQuery)){
        return new ajQuery(selector);
    }
    this.selector = selector;
    return this
}

但是注意千万不要像下面这样写:

var $$ = ajQuery = function(selector) {
    this.selector = selector;
    return new ajQuery(selector);
}
Uncaught RangeError: Maximum call stack size exceeded

这样会无限递归自己,从而造成死循环并且溢出。

jQuery为了避免出现这种死循环的问题,采取的手段是把原型上的一个init方法作为构造器

var $$ = ajQuery = function(selector) {
    //把原型上的init作为构造器
    return new ajQuery.fn.init( selector );
}

ajQuery.fn = ajQuery.prototype = {
    name: 'aaron',
    init: function() {
        console.log(this)
    },
    constructor: ajQuery
}

这样确实解决了循环递归的问题,但是又问题来了,init是ajQuery原型上作为构造器的一个方法,

那么其this就不是ajQuery了,所以this就完全引用不到ajQuery的原型了,所以这里通过new把init方法与ajQuery给分离成2个独立的构造器。

具体的实例:

<div id="test"></div>

<script type="text/javascript">

//常见写法
var $$ = ajQuery = function(selector) {
    this.selector = selector;
	return this
}
ajQuery.fn = ajQuery.prototype = {
	selectorName:function(){
		return this.selector;
	},
	constructor: ajQuery
}
var a = new $$('aaa');  //实例化

//得到选择器名字
//aaa
$("#test").html( a.selectorName() )

</script>

静态与实例方法共享设计

jQuery在接口的设计:

遍历方法:

$(".aaron").each()   //作为实例方法存在
$.each()             //作为静态方法存在

这是最常见的遍历方法,第一条语句是给有指定的上下文调用的,就是(".aaron")获取的DOM合集,第二条语句$.each()函数可用于迭代任何集合,

无论是“名/值”对象(JavaScript对象)或数组。在迭代数组的情况下,回调函数每次都会传递一个数组索引和相应的数组值作为参数。

本质上来说2个都是遍历,那么我们是不是要写2个方法呢?

看看jQuery的源码:

实例方法取于静态方法,换句话来说这是静态与实例方法共享设计,静态方法挂在jQuery构造器上,原型方法挂在哪里呢?

jQuery通过new原型prototype上的init方法当作构造器,那么init的原型链方法就是实例的方法了,

所以jQuery通过2个构造器划分2种不同的调用方式一种是静态,一种是原型。

方法是共享的,并且实例方法取于静态方法,2个构造器是完全隔离的 ,这个要如何处理?

看看jQuery给的方案:

init.prototype = jQuery.fn

把jQuery.prototype原型的引用赋给jQuery.fn.init.prototype的原型,这样就把2个构造器的原型给关联起来了。

ajQuery.fn = ajQuery.prototype = {
        name: 'aaron',
        init: function(selector) {
               this.selector = selector;
               return this;
        },
        constructor: ajQuery
}
ajQuery.fn.init.prototype = ajQuery.fn

这段代码就是整个结构设计的最核心的东西了,有这样的一个处理,整个结构就活了!不得不佩服作者的设计思路,别具匠心。

看看init的的构造图:

通过原型传递解决问题,把jQuery的原型传递给jQuery.prototype.init.prototype。

换句话说jQuery的原型对象覆盖了init构造器的原型对象,因为是引用传递所以不需要担心这个循环引用的性能问题。

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script src="http://img.mukewang.com/down/540812440001e40e00000000.js" type="text/javascript"></script>
<title></title>
</head>
<body>
    
打印$$().say()
<div id="aaron"></div>

<script type="text/javascript">

var $$ = ajQuery = function(selector) {
    return new ajQuery.fn.init(selector);
}

ajQuery.fn = ajQuery.prototype = {
	name: 'aaron',
	init: function(selector) {
		this.selector = selector;
		return this;
	},
	constructor: ajQuery
}

ajQuery.fn.init.prototype = ajQuery.fn

ajQuery.fn.say = function() {
	$("#aaron").html(this.name)
}

$$().say()

</script>
</body>
</html>

优质内容筛选与推荐>>
1、ArcGIS Server REST中文乱码问题解决办法
2、mysql::创建多个表空间添加记录数据会添加到哪个表空间中?
3、antd源码分析之——标签页(tabs 2.Tabs关键组件功能实现)
4、python进阶之 ——装饰器
5、[导入]高校新生入学将注册电子学籍


长按二维码向我转账

受苹果公司新规定影响,微信 iOS 版的赞赏功能被关闭,可通过二维码转账支持公众号。

    阅读
    好看
    已推荐到看一看
    你的朋友可以在“发现”-“看一看”看到你认为好看的文章。
    已取消,“好看”想法已同步删除
    已推荐到看一看 和朋友分享想法
    最多200字,当前共 发送

    已发送

    朋友将在看一看看到

    确定
    分享你的想法...
    取消

    分享想法到看一看

    确定
    最多200字,当前共

    发送中

    网络异常,请稍后重试

    微信扫一扫
    关注该公众号





    联系我们

    欢迎来到TinyMind。

    关于TinyMind的内容或商务合作、网站建议,举报不良信息等均可联系我们。

    TinyMind客服邮箱:support@tinymind.net.cn