http://www.imooc.com/code/3401

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

A.创建一个新对象

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

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

  D.返回这个新对象

最后一点就说明了,我们只要返回一个新对象即可。其实new操作符主要是把原型链跟实例的this关联起来,这才是最关键的一点,所以我们如果需要原型链就必须要new操作符来进行处理。否则this则变成window对象了。

下面这个例子,console结果一样。因为都用了new 。如果不用new就不一样了。

  1. var $$ = ajQuery = function(selector) {
  2. this.selector = selector;//this是aJquery {}name: "sss"__proto__: Object
  3. return this
  4. }
  5. var ins = new $$("111");
  6. console.log(ins);//
  7. var $$2 = ajQuery2 = function(selector) {
  8. this.selector = selector;
  9. }
  10. var ins2 = new $$2("2222");
  11. console.log(ins2);//

去掉new 后。ajQuery里的return this就起了作用。

  1. var $$ = ajQuery = function(selector) {
  2. this.selector = selector;
  3. return this
  4. }
  5. var ins = $$("111");
  6. console.log(ins);//window
  7. var $$2 = ajQuery2 = function(selector) {
  8. this.selector = selector;
  9. }
  10. var ins2 = $$2("2222");
  11. console.log(ins2);//undefined

我们常见的类式写法:

  1. var $$ = ajQuery = function(selector) {
  2. console.log(this instanceof ajQuery);//true ajQuery被new形式调用时,this就是ajQuery的实例
  3. this.selector = selector;
  4. return this
  5. }
  6. ajQuery.fn = ajQuery.prototype = {
  7. selectorName:function(){
  8. return this.selector;
  9. },
  10. constructor: ajQuery
  11. }
  12. var ins = new $$("111");
  13. console.log(ins.selectorName());

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

  1. var $$ = ajQuery = function(selector) {
  2. console.log(this);//先是window,再是ajQuery的实例
  3. if(!(this instanceof ajQuery)){
  4. return new ajQuery(selector);
  5. }
  6. this.selector = selector;
  7. return this
  8. }
  9. ajQuery.fn = ajQuery.prototype = {
  10. selectorName:function(){
  11. return this.selector;
  12. },
  13. constructor: ajQuery
  14. }
  15. var ins = $$("111");
  16. console.log(ins.selectorName());

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

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

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

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

  1. function aJquery(selector){
  2. return new aJquery.fn.init(selector);
  3. }
  4. aJquery.fn = aJquery.prototype = {
  5. showName:function(){
  6. console.log(this.name)
  7. },
  8. init: function(){
  9. console.log(this);
  10. }
  11. }
  12. var a = aJquery("sss");
  13. a.showName();//报错

这样确实解决了循环递归的问题,但是又问题来了,init是ajQuery原型上作为构造器的一个方法,那么其this就不是ajQuery了,所以this就完全引用不到ajQuery的原型了(showName报错),所以这里通过new把init方法与ajQuery给分离成2个独立的构造器。

静态与实例方法共享设计

jQuery遍历方法:

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

这是最常见的遍历方法,第一条语句是给有指定的上下文调用的,就是(".aaron")获取的DOM合集,第二条语句$.each()函数可用于迭代任何集合,无论是“名/值”对象(JavaScript对象)或数组。在迭代数组的情况下,回调函数每次都会传递一个数组索引和相应的数组值作为参数。本质上来说2个都是遍历,那么我们是不是要写2个方法呢?

我们来看看jQuery的源码:

  1. jQuery.prototype = {
  2. each: function( callback, args ) {
  3. return jQuery.each( this, callback, args );
  4. }
  5. }
  1. var Newobj = function(name){
  2. this.name = name;
  3. }
  4. Newobj.showName = function(args){//静态方法:不能被实例对象调用
  5. console.log(args);
  6. }
  7. Newobj.prototype = {
  8. showName: function(args){//实例方法
  9. return Newobj.showName(this.name + args );
  10. }
  11. }
  12. var s = new Newobj("sss");
  13. s.showName("bbb");//sssbbb

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

我们上节不是讲了内部会划分一个新的构造器init吗?jQuery通过new原型prototype上的init方法当作构造器,那么init的原型链方法就是实例的方法了,所以jQuery通过2个构造器划分2种不同的调用方式一种是静态,一种是原型。

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

看看jQuery给的方案:

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

  1. ajQuery.fn = ajQuery.prototype = {
  2. name: 'aaron',
  3. init: function(selector) {
  4. this.selector = selector;
  5. return this;
  6. },
  7. constructor: ajQuery
  8. }
  9. ajQuery.fn.init.prototype = ajQuery.fn

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

看看init的的构造图:

(单击图片查看大图)

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

  1. var $$ = ajQuery = function(selector) {
  2. //把原型上的init作为构造器
  3. return new ajQuery.fn.init( selector );
  4. }
  5. ajQuery.fn = ajQuery.prototype = {
  6. name: 'aaron',
  7. init: function(selector) {
  8. this.selector = selector;
  9. return this;
  10. },
  11. constructor: ajQuery
  12. }
  13. ajQuery.fn.init.prototype = ajQuery.fn;
  14. ajQuery.fn.foo = function () { alert("foo!"); };
  15. ajQuery(".some-selector").foo();
  16. //挂在ajQuery.fn上的实例方法变成了ajQuery(ajQuery.fn.init)上的原型方法

现在的ajQuery(".some-selector")

  1. 原型中有fooinit
  2.  
  3. http://stackoverflow.com/questions/1755080/why-jquery-do-this-jquery-fn-init-prototype-jquery-fn

jQuery分离构造器的更多相关文章

  1. jQuery 源码分析 8: 回头看jQuery的构造器(jQuery.fn,jQury.prototype,jQuery.fn.init.prototype的分析)

    在第一篇jQuery源码分析中,简单分析了jQuery对象的构造过程,里面提到了jQuery.fn.jQuery.prototype.jQuery.fn.init.prototype的关系. 从代码中 ...

  2. 【深入浅出jQuery】源码浅析2--奇技淫巧

    最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...

  3. 解密jQuery事件核心 - 委托设计(二)

    第一篇 http://www.cnblogs.com/aaronjs/p/3444874.html 从上章就能得出几个信息: 事件信息都存储在数据缓存中 对于没有特殊事件特有监听方法和普通事件都用ad ...

  4. JQuery源码分析(五)

    分离构造器 通过new 操作符构建一个对象,一般经过四部:   A.创建一个新对象   B.将构造函数的作用域赋给新对象(所以this就指向了这个新对象)   C.执行构造函数中的代码   D.返回这 ...

  5. 【深入浅出jQuery】源码浅析2--使用技巧

    最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...

  6. jquery 使用整理机制

    短路表达式 与 多重短路表达式 短路表达式这个应该人所皆知了.在 jQuery 中,大量的使用了短路表达式与多重短路表达式. 短路表达式:作为"&&"和" ...

  7. jQuery源码浅析2–奇技淫巧

    最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...

  8. jQuery中对象的构建

    <!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  9. jQuery源码解读----part 2

    分离构造器 通过new操作符构建一个对象,一般经过四步: A.创建一个新对象 B.将构造函数的作用域赋给新对象(所以this就指向了这个新对象) C.执行构造函数中的代码 D.返回这个新对象 最后一点 ...

随机推荐

  1. 算法 Tricks(三)—— 数组(序列)任意区间最小(大)值

    序列(数组)的区间通过左右端点确定,这样首先设置一个最值变量用来记录最值,从左端点一步步移动到右端点,自然移动的过程中也可以计算整个区间的和,也即一次线性遍历下来,可同时获得多个有用信息. // 区间 ...

  2. [Angular2Fire] Firebase auth (Google, Github)

    To do auth, first you need to go firebase.console.com to enable the auth methods, for example, enabl ...

  3. Android自定义组件系列【8】——遮罩文字动画

    遮罩文字的动画我们在Flash中非常常见,作为Android的应用开发者你是否也想将这种动画做到你的应用中去呢?这一篇文章我们来看看如何自定义一个ImageView来实现让一张文字图片实现文字的遮罩闪 ...

  4. php 文件夹是否存在,不存在就创建

    $lujing = "./nihao/wohao"; if(!is_dir($liujing)){ mkdir(iconv("UTF-8", "GBK ...

  5. 数据类型总结——String(字符串类型)

    相关文章 简书原文:https://www.jianshu.com/p/546a755c3eb6 数据类型总结——概述:https://www.cnblogs.com/shcrk/p/9266015. ...

  6. https://github.com/mvf/svn_wfx

    https://github.com/mvf/svn_wfx 2003.net对应的vc是7.0版本.需要更高的. 在哪里可以下载呢 https://www.tjupt.org/没有校外种子 Proj ...

  7. ASP.NET MVC中实现多个button提交的几种方法

    有时候会遇到这样的情况:在一个表单上须要多个button来完毕不同的功能,比方一个简单的审批功能. 假设是用webform那不须要讨论,但asp.net mvc中一个表单仅仅能提交到一个Action处 ...

  8. XML输出到浏览器报错

    在使用Firefox浏览器测试我编写的xml文件时,遇到如下错误:我的xml源代码如下: <?xml version="1.0" encoding="UTF-8&q ...

  9. PatentTips - Sprite Graphics Rendering System

    BACKGROUND This disclosure relates generally to the field of computer graphics. More particularly, b ...

  10. Android MagicIndicator系列之一 —— 使用MagicIndicator打造千变万化的ViewPager指示器

    说到 ViewPager 指示器,想必大家都不陌生,绝大部分应用中都有这个.使用频率非常之高.但系统对它的支持并不好,自带的 PagerTabStrip 和 PagerTitleStrip 太弱,很难 ...