源码网商城,靠谱的源码在线交易网站 我的订单 购物车 帮助

源码网商城

实例详解jQuery的无new构建

  • 时间:2022-09-12 04:37 编辑: 来源: 阅读:
  • 扫一扫,手机访问
摘要:实例详解jQuery的无new构建
[b]jQuery的无new构建[/b] jQuery框架的核心就是从HTML文档中匹配元素并对其执行操作、 回想一下使用 jQuery 的时候,实例化一个 jQuery 对象的方法:
// 无 new 构造
$('#test').text('Test');
 
// 当然也可以使用 new
var test = new $('#test');
test.text('Test');
大部分人使用 jQuery 的时候都是使用第一种无 new 的构造方式,直接[code] $('')[/code] 进行构造,这也是 jQuery 十分便捷的一个地方。 当我们使用第一种无 new 构造方式的时候,其本质就是相当于 [code]new jQuery()[/code],那么在 jQuery 内部是如何实现的呢?看看:
(function(window, undefined) {
  var
  // ...
  jQuery = function(selector, context) {
    // The jQuery object is actually just the init constructor 'enhanced'
    return new jQuery.fn.init(selector, context, rootjQuery);
  },
 
  jQuery.fn = jQuery.prototype = {
    init: function(selector, context, rootjQuery) {
      // ...
    }
  }
  jQuery.fn.init.prototype = jQuery.fn;
})(window);
 没看懂?没关系,我们一步一步分析。 [b]函数表达式和函数声明[/b] 在ECMAScript中,创建函数的最常用的两个方法是函数表达式和函数声明,两者期间的区别是有点晕,因为ECMA规范只明确了一点:函数声明必须带有标示符([code]Identifier[/code])(就是大家常说的函数名称),而函数表达式则可以省略这个标示符: 
   //函数声明:
  function 函数名称 (参数:可选){ 函数体 }
  //函数表达式:
  function 函数名称(可选)(参数:可选){ 函数体 }
所以,可以看出,如果不声明函数名称,它肯定是表达式,可如果声明了函数名称的话,如何判断是函数声明还是函数表达式呢? ECMAScript是通过上下文来区分的,如果[code]function foo(){}[/code]是作为赋值表达式的一部分的话,那它就是一个函数表达式, 如果[code]function foo(){}[/code]被包含在一个函数体内,或者位于程序的最顶部的话,那它就是一个函数声明。
 function foo(){} // 声明,因为它是程序的一部分
 var bar = function foo(){}; // 表达式,因为它是赋值表达式的一部分
 new function bar(){}; // 表达式,因为它是new表达式
 (function(){
  function bar(){} // 声明,因为它是函数体的一部分
 })();
还有一种函数表达式不太常见,就是被括号括住的[code](function foo(){})[/code],他是表达式的原因是因为括号 ()是一个分组操作符,它的内部只能包含表达式  再来看jQuery源码:
(function(window, undefined) {
  /...
})(window)
可以将上面的代码结构分成两部分[code]:(function(){window, undefined})[/code] 和[code] (window) ,[/code] [b]第1个()是一个表达式,而这个表达式本身是一个匿名函数,[/b] [b]所以在这个表达式后面加(window)就表示执行这个匿名函数并传入参数window。[/b] [b]原型 prototype[/b] 认识一下什么是原型? 在JavaScript中,[b]原型也是一个对象,通过原型可以实现对象的属性继承[/b],JavaScript的对象中都包含了一个[code]" [[Prototype]]"[/code]内部属性,这个属性所对应的就是该对象的原型。 对于"prototype"和"__proto__"这两个属性有的时候可能会弄混,"Person.prototype"和"Person.__proto__"是完全不同的。 在这里对"prototype"和"__proto__"进行简单的介绍:     1.对于所有的对象,都有__proto__属性,这个属性对应该对象的原型     2.对于函数对象,除了__proto__属性之外,还有prototype属性,[b]当一个函数被用作构造函数来创建实例时,该函数的prototype属性值将被作为原型赋值给所有对象实例(也就是设置实例的__proto__属性) [/b]
function Person(name, age){
  this.name = name;
  this.age = age;
}
Person.prototype.getInfo = function(){
  console.log(this.name + " is " + this.age + " years old");
};
//调用
var will = new Person("Will", 28);
will.getInfo();//"Will is 28 years old"
[b]闭包[/b] [b]闭包的定义:[/b] 当一个内部函数被其外部函数之外的变量引用时,就形成了一个闭包。 [b]闭包的作用:[/b] 在了解闭包的作用之前,我们先了解一下 javascript中的GC机制: 在javascript中,[b]如果一个对象不再被引用,那么这个对象就会被GC回收,否则这个对象一直会保存在内存中。[/b] 在上述例子中,B定义在A中,因此B依赖于A,而外部变量 c 又引用了B, 所以A间接的被 c 引用, 也就是说,A不会被GC回收,会一直保存在内存中。为了证明我们的推理,看如下例子:
function A(){
  var count = 0;
  function B(){
    count ++;
    console.log(count);
  }
  return B;
}
var c = A();
c();// 1
c();// 2
c();// 3
count是A中的一个变量,它的值在B中被改变,函数B每执行一次,count的值就在原来的基础上累加1。因此,A中的count一直保存在内存中。 这就是闭包的作用,有时候我们需要一个模块中定义这样一个变量:[b]希望这个变量一直保存在内存中但又不会“污染”全局的变量[/b],这个时候,我们就可以用闭包来定义这个模块 [b]在看jQuery源码:[/b]
(function(window, undefined) {
  var
  // ...
  jQuery = function(selector, context) {
    // The jQuery object is actually just the init constructor 'enhanced'
    return new jQuery.fn.init(selector, context, rootjQuery);
  },
  jQuery.fn = jQuery.prototype = {
    init: function(selector, context, rootjQuery) {
      // ...
    }
  }
  jQuery.fn.init.prototype = jQuery.fn;
})(window);
 
我们知道了 什么是闭包:当一个内部函数被其外部函数之外的变量引用时,就形成了一个闭包。 jQuery.fn的init 函数被jQuery 的构造函数调用了,这里形成了一个闭包。 构造函数及调用代码:
// ...
  jQuery = function(selector, context) {
    // The jQuery object is actually just the init constructor 'enhanced'
    return new jQuery.fn.init(selector, context, rootjQuery);
  },
问题关键来了。 [b]如何实现无new构建[/b] JavaScript是函数式语言,函数可以实现类,类就是面向对象编程中最基本的概念
var aQuery = function(selector, context) {
    //构造函数
}
aQuery.prototype = {
  //原型
  name:function(){},
  age:function(){}
}
var a = new aQuery();
a.name();
 
这是常规的使用方法,显而易见jQuery不是这样玩的 要实现这样,那么jQuery就要看成一个类,那么$()应该是返回类的实例才对 按照jQuery的抒写方式
$().ready() 
$().noConflict()
要实现这样,那么jQuery就要看成一个类,那么$()应该是返回类的实例才对 所以把代码改一下:
var aQuery = function(selector, context) {
    return new aQuery();
}
aQuery.prototype = {
  name:function(){},
  age:function(){}
}
通过[code]new aQuery(),[/code]虽然返回的是一个实例,但是也能看出很明显的问题,[b]死循环了[/b]! [b]那么如何返回一个正确的实例?[/b] 在javascript中实例this只跟原型有关系 那么可以把jQuery类当作一个工厂方法来创建实例,把这个方法放到aQuery.prototye原型中
var aQuery = function(selector, context) {
    return aQuery.prototype.init(selector);
}
aQuery.prototype = {
  init:function(selector){
    return this;
  }
  name:function(){},
  age:function(){}
}
当执行[code]aQuery() [/code]返回的实例: [img]http://files.jb51.net/file_images/article/201608/201682150343608.png?20167215354[/img] 很明显[code]aQuery()[/code]返回的是[code]aQuery[/code]类的实例,那么在init中的this其实也是指向的[code]aQuery[/code]类的实例 问题来了init的this指向的是[code]aQuery[/code]类,如果把init函数也当作一个构造器,那么内部的this要如何处理?
var aQuery = function(selector, context) {
    return aQuery.prototype.init(selector);
}
aQuery.prototype = {
  init: function(selector) {
    this.age = 18
    return this;
  },
  name: function() {},
  age: 20
}
aQuery().age //18
 因为this只是指向[code]aQuery[/code]类的,所以[code]aQuery[/code]的[code]age[/code]属性是可以被修改的。 [b]这样看似没有问题,其实问题很大的[/b] [b]为什么是new jQuery.fn.init?[/b] 看如下代码:
var aQuery = function(selector, context) {
    return aQuery.prototype.init(selector);
}
aQuery.prototype = {
  init: function(selector) {
    if(selector=="a")
      this.age = 18
    return this;
  },
  name: function() {},
  age: 20
}
aQuery("a").age //18
aQuery("b").age //18
 
当我调用 传入"a"的时候,修改age=18,及aQuery("a").age 的值为18 但是当我  传入"b"的时候 并没又修改 age的值,[b]我也希望得到默认age的值20[/b],但是aQuery("b").age 的值为18. 因为在 调用aQuery("a").age 的时候age被修改了。 [b]这样的情况下就出错了,所以需要设计出独立的作用域才行。[/b] [b]jQuery框架分隔作用域的处理[/b]
jQuery = function( selector, context ) {
    // The jQuery object is actually just the init constructor 'enhanced'
    return new jQuery.fn.init( selector, context, rootjQuery );
  },
很明显通过实例[code]init[/code]函数,每次都构建新的[code]init[/code]实例对象,来分隔[code]this[/code],避免交互混淆 我们修改一下代码:
var aQuery = function(selector, context) {
    return new aQuery.prototype.init(selector);
}
aQuery.prototype = {
  init: function(selector) {
    if(selector=="a")
      this.age = 18
    return this;
  },
  name: function() {},
  age: 20
}
aQuery("a").age //18
aQuery("b").age //undefined
aQuery("a").name() //Uncaught TypeError: Object [object Object] has no method 'name' 
又出现一个新的问题, [code]age  :undefined,[/code] [code]name() :[/code]抛出错误,无法找到这个方法,所以很明显new的[code]init[/code]跟jquery类的[code]this[/code]分离了 [b]怎么访问jQuery类原型上的属性与方法?[/b]      做到既能隔离作用域还能使用jQuery原型对象的作用域呢,还能在返回实例中访问jQuery的原型对象? [b]实现的关键点[/b]
// Give the init function the jQuery prototype for later instantiation
jQuery.fn.init.prototype = jQuery.fn;
我们再改一下:
var aQuery = function(selector, context) {
    return new aQuery.prototype.init(selector);
}
aQuery.prototype = {
  init: function(selector) {
    if(selector=="a")
      this.age = 18
    return this;
  },
  name: function() {
     return age;
  },
  age: 20
}
aQuery.prototype.init.prototype = aQuery.prototype; 

aQuery("a").age //18
aQuery("b").age //20
aQuery("a").name()  //20
最后在看一下jQuery源码:
(function(window, undefined) {
  var
  // ...
  jQuery = function(selector, context) {
    // The jQuery object is actually just the init constructor 'enhanced'
    return new jQuery.fn.init(selector, context, rootjQuery);
  },
  jQuery.fn = jQuery.prototype = {
    init: function(selector, context, rootjQuery) {
      // ...
    }
  }
  jQuery.fn.init.prototype = jQuery.fn;
})(window);
是不是明白了? 哈哈哈~~~ 在简单说两句: 大部分人初看[code] jQuery.fn.init.prototype = jQuery.fn [/code]这一句都会被卡主,很是不解。但是这句真的算是 jQuery 的绝妙之处。理解这几句很重要,分点解析一下: 1)首先要明确,使用[code] $('xxx') [/code]这种实例化方式,其内部调用的是[code] return new jQuery.fn.init(selector, context, rootjQuery)[/code] 这一句话,也就是构造实例是交给了[code] jQuery.fn.init() [/code]方法取完成。 2)将 [code]jQuery.fn.init [/code]的 prototype 属性设置为 [code]jQuery.fn[/code],那么使用 [code]new jQuery.fn.init() [/code]生成的对象的原型对象就是 [code]jQuery.fn[/code] ,所以挂载到[code] jQuery.fn [/code]上面的函数就相当于挂载到[code] jQuery.fn.init() [/code]生成的 jQuery 对象上,所有使用 [code]new jQuery.fn.init() [/code]生成的对象也能够访问到[code] jQuery.fn [/code]上的所有原型方法。 3)也就是实例化方法存在这么一个关系链      1.jQuery.fn.init.prototype = jQuery.fn = jQuery.prototype ;     2.new jQuery.fn.init() 相当于 new jQuery() ;     3.jQuery() 返回的是 new jQuery.fn.init(),而 var obj = new jQuery(),所以这 2 者是相当的,所以我们可以无 new 实例化 jQuery 对象。 [b]总结[/b] 以上就是jQuery的无new构建的全部内容,希望本文对大家学习jQuery有所帮助。也请大家继续支持编程素材网。
  • 全部评论(0)
联系客服
客服电话:
400-000-3129
微信版

扫一扫进微信版
返回顶部