继上一篇文章说到了jq的构造函数, 本节主要是深究一下jquery的原型机制.

一、更新


[2019-4-21]

[Changed]

  • 改进文章格式

二、前置


2.1 堆内存

关于堆内存, 我将它提到了文章开头, 因为jQuery作者巧妙的运用了这一点.

Q: 那么什么是堆内存? 它有什么用?

这里就不再详细说了, 网上的资料非常详细, 只需明白一点:

  • js中堆内存存储了一个指向对象池特定对象的指针, 由此导致多路对于该对象的操作, 都会直接修改其本身.

三、原型


由于源码较少, 直接贴出:

1
2
3
4
5
6
7
8
 function jQuery() {
return new jQuery.fn.init();
}

var init = jQuery.fn.init;

// Give the init function the jQuery prototype for later instantiation
init.prototype = jQuery.fn;

可以看到上述源码内部的注释, 它的意思是:

PS: 将jQuery的原型赋值给init的原型, 便于后续操作

作者将init.prototypejQuery.prototype指向了同一个对象.

PS: 那么, 这么做有什么好处呢?

不急, 先来回想一下, 我们是怎么用jQuery的? 我想, 肯定是这样吧

1
$(xxx).xxx().xxx()

粗看好像没什么问题, 但是细想之下, 平时使用插件的时候, 是这么用的:

1
const xxx = new Plugin({});

对比之下, 两者区别在于:

  • jQuery不用进行new, 也就是实例化操作

结合前几篇文章的分析, 我们知道那是因为jQuery内部帮我们实现了实例化.

那么, 我们可以大胆地做个猜测, 他可能是这么实现的吧:

1
2
3
4
5
function jQuery(selector) {
return new jQuery(selector);
}

jQuery.prototype = {...};

但是, 细心的我们会发现, 该操作不但没有解决问题, 还会出现Maximum call stack size exceeded的报错, 很显然, 这是一个没有终止条件的递归操作.

由此, jQuery作者很轻松的想到了使用原型赋值的方式, 来解决递归爆栈的问题, 不禁感叹, 真是🐂🐂!

四、总结


感觉啥都没说, 这一节就写完了, 不得不佩服jQuery作者John Resig的高明之处, 一个简简单单的工厂函数, 能玩的如此美妙!

五、示例code


为了加深理解以及熟练ts, 已用ts重构代码, 详情见Github.