详解jQuery构造器 - jQuery源码探索(一)

本文约定:

  1. 文章中的jQuery指的是jQuery框架中的jQuery函数(构造器)

  2. 文章中的jQ指的是jQuery框架

为了防止混淆,方便阅读,故在此说明一下!

1.jQuery构造器的实现

jQuery构造器也就是jQuery函数,也是平时我们使用jQ时经常用到的美元符号$

在jQ源码的最后面有:

//这就是jQ暴露出来的接口
window.jQuery = window.$ = jQuery;
```

那么我们再来看看在jQ中jQuery函数是如何实现的:

```javascript
jQuery = function( selector, context ) {
        return new jQuery.fn.init( selector, context );
}
//在后面,又发现了这么一句
jQuery.fn = jQuery.prototype;
```

看完上面代码,可以知道我们无论传一个什么参数给jQuery函数,它都会返回一个**jQuery.prototype.init**的实例对象。也就是说,我们平时用的jQuery对象,其实并不是jQuery构造器创建出来的,而是**jQuery函数原型中的init函数(构造器)**创建出来的!

## 2.jQuery为什么使用jQuery.prototype.init作为构造器

**我们再来看看有关init函数的源码**

```javascript
jQuery = function( selector, context ) {
        return new jQuery.fn.init( selector, context );
}

jQuery.fn = jQuery.prototype = {
    constructor: jQuery,
    init: function () {
        //实例化代码
    }
    //后面原型方法省略,不是本文所探究
}
//修改init的原型对象为jQuery函数的原型
jQuery.fn.init.prototype = jQuery.fn;
```

**先将上面代码,转化成我们习惯阅度的如下代码:**

```javascript
jQuery = function( selector, context ) {
        return new jQuery.prototype.init( selector, context );
}

jQuery.prototype = {
    constructor: jQuery,
    init: function () {
        //实例化代码
    }
}
//修改init的原型对象为jQuery函数的原型
jQuery.prototype.init.prototype = jQuery.prototype;
```

由于将init.prototype=jQuery.prototype,因此init函数(构造器)创建出来的对象,它们的__proto__指针依然会指向jQuery.prototype,因此归根到底它们还是jQuery对象,只不过把,应该写在jQuery函数中的实例代码,写到了init函数中!

因此上面的代码就是:**换了个包装,配方还是原来的!**

**那我们思考一下,为什么jQ作者绕了几个弯把实例化代码写在jQuery.prototype.init里,而不是直接写在jQuery函数里?这么干有什么好处?**

如果想不出来,那我们不妨尝试先把实例化代码写在jQuery函数里!

## 3.把实例化代码写在jQuery函数上

不过在写之前,我们得清楚jQuery有一个很明显的特色是**"强制实例化"**,什么是"强制实例化"?还是说明一下吧,因为这词是我临时想出来的,你百度谷歌也查不出来...
顾名思义,强制实例化就是强行实例化!举个栗子,jQ中无论传什么参数,它最终都会返回一个jQuery对象给你,比如: $("div"),我们传入了一个div字符串,并没有使用new $("div"),却返回了一个jQuery对象给我们。

**那假如我们把jQuery的实例代码写在jQuery函数里,为了实现"强制实例化",那可以写成如下:**

```javascript
var jQuery = function(selector, context) {
    //通过判断是否是jQuery对象,实现"强制实例化"
   if(!(this instanceof jQuery)){
       return new jQuery(selector, context);
   }
  //伪实例代码
   this.test = selector;
};
var obj = jQuery('obj');
console.log(obj instanceof jQuery); //true
</script>
```

**上面方法实现了"强制实例化",但是又出现了另外一个问题,如果引用不当,就会出现无限递归的情况**

我们把上面的代码稍微修改一下:

```javascript
var jQuery = function(selector, context) {
        //比如在此处修改jQuery的原型对象
        jQuery.prototype = {};
        if(!(this instanceof jQuery)){

            return new jQuery(selector, context);
        }
        this.test = selector;
    };
    var obj = jQuery('obj');
    console.log(obj instanceof jQuery); 
```

上面代码将抛出: ***Uncaught RangeError: Maximum call stack size exceeded ***

程序进入了死循环、无限递归;


**同样在jQuery函数里修改jQuery的原型,把实例化代码写到init上**

```javascript
jQuery = function( selector, context ) {
   jQuery.prototype = {};
   return new jQuery.fn.init( selector, context );
};

jQuery.fn = jQuery.prototype = {
   constructor: jQuery,
   init: function ( selector, context ) {
       this.test = selector;
   }
}
jQuery.fn.init.prototype = jQuery.fn;
var obj = jQuery('obj');
console.log(obj);  //{test: "obj"}
```

我们在上面修改jQuery的原型对象后,不仅没有进入无限递归,并且一点不影响obj的实例化,obj的__proto__指针依旧指向原来原型对象,也就是jQuery.fn

在此额外说明一下,可能有些小伙伴会纠结,为什么要把jQuery.prototype = {}写在里面?写在外面就不会出错了!其实这只是我为了试错临时想的例子,用来验证代码的容错性。要知道,一个好的框架,容错性都是很高很高的!当然,这样做的好处不仅仅只有容错性,还有扩展性都很高,就是将init方法和jQuery方法分离成两个独立的构造器,以后静态方法写在jQuery上,实例方法写在init上,这样易于管理。

## 总结 

上面的测试证明,这样的代码设计有很多的优点,这里我总结了几条

1. 避免意外条件下,出现无限递归的情况

2. 防止原型被修改

3. 将init方法和jQuery方法分离成两个独立的构造器,这样易于代码的管理,代码扩展性强
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272

推荐阅读更多精彩内容

  • 本章将会介绍 存储属性的初始赋值自定义构造过程默认构造器值类型的构造器代理类的继承和构造过程可失败构造器必要构造器...
    寒桥阅读 749评论 0 0
  • 工厂模式类似于现实生活中的工厂可以产生大量相似的商品,去做同样的事情,实现同样的效果;这时候需要使用工厂模式。简单...
    舟渔行舟阅读 7,622评论 2 17
  • 构造过程 构造过程是使用类、结构体或枚举类型的实例之前的准备过程。在新实例可用前必须执行这个过程,具体操作包括设置...
    蛊毒_阅读 708评论 0 2
  • 过了懵懵懂懂的年纪,心智,认知,世界观等等逐渐稳定下来,一般的新的认识不足以触动认知变天,最近接触了一本书,在某一...
    心甲阅读 113评论 1 0
  • 今天晚上和母亲视频了,母亲甚少像今天这样刻意找我聊天。 母亲跟我说今天她买了好多的洗发水,这本来是一件极小的事情可...
    谭惜言阅读 381评论 0 2