您好,欢迎访问一九零五行业门户网

jQuery无new构建使用详解

这次给大家带来jquery无new构建使用详解,jquery无new构建使用的注意事项有哪些,下面就是实战案例,一起来看一下。
jquery的无new构建
jquery框架的核心就是从html文档中匹配元素并对其执行操作、
回想一下使用 jquery 的时候,实例化一个 jquery 对象的方法:
// 无 new 构造 $('#test').text('test');   // 当然也可以使用 new var test = new $('#test'); test.text('test');
大部分人使用 jquery 的时候都是使用第一种无 new 的构造方式,直接 $('') 进行构造,这也是 jquery 十分便捷的一个地方。
当我们使用第一种无 new 构造方式的时候,其本质就是相当于 new jquery(),那么在 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);
没看懂?没关系,我们一步一步分析。
函数表达式和函数声明
在ecmascript中,创建函数的最常用的两个方法是函数表达式和函数声明,两者期间的区别是有点晕,因为ecma规范只明确了一点:函数声明必须带有标示符(identifier)(就是大家常说的函数名称),而函数表达式则可以省略这个标示符:
//函数声明: function 函数名称 (参数:可选){ 函数体 } //函数表达式: function 函数名称(可选)(参数:可选){ 函数体 }
所以,可以看出,如果不声明函数名称,它肯定是表达式,可如果声明了函数名称的话,如何判断是函数声明还是函数表达式呢?
ecmascript是通过上下文来区分的,如果function foo(){}是作为赋值表达式的一部分的话,那它就是一个函数表达式,
如果function foo(){}被包含在一个函数体内,或者位于程序的最顶部的话,那它就是一个函数声明。
function foo(){} // 声明,因为它是程序的一部分  var bar = function foo(){}; // 表达式,因为它是赋值表达式的一部分  new function bar(){}; // 表达式,因为它是new表达式  (function(){   function bar(){} // 声明,因为它是函数体的一部分  })();
还有一种函数表达式不太常见,就是被括号括住的(function foo(){}),他是表达式的原因是因为括号 ()是一个分组操作符,它的内部只能包含表达式
再来看jquery源码:
(function(window, undefined) {   /... })(window)
可以将上面的代码结构分成两部分:(function(){window, undefined}) 和 (window) ,
第1个()是一个表达式,而这个表达式本身是一个匿名函数,
所以在这个表达式后面加(window)就表示执行这个匿名函数并传入参数window。
原型 prototype
认识一下什么是原型?
在javascript中,原型也是一个对象,通过原型可以实现对象的属性继承,javascript的对象中都包含了一个 [[prototype]]内部属性,这个属性所对应的就是该对象的原型。
对于prototype和proto这两个属性有的时候可能会弄混,person.prototype和person.proto是完全不同的。
在这里对prototype和proto进行简单的介绍:
1.对于所有的对象,都有proto属性,这个属性对应该对象的原型
2.对于函数对象,除了proto属性之外,还有prototype属性,当一个函数被用作构造函数来创建实例时,该函数的prototype属性值将被作为原型赋值给所有对象实例(也就是设置实例的proto属性)
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
闭包
闭包的定义:
当一个内部函数被其外部函数之外的变量引用时,就形成了一个闭包。
闭包的作用:
在了解闭包的作用之前,我们先了解一下 javascript中的gc机制:
在javascript中,如果一个对象不再被引用,那么这个对象就会被gc回收,否则这个对象一直会保存在内存中。
在上述例子中,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一直保存在内存中。
这就是闭包的作用,有时候我们需要一个模块中定义这样一个变量:希望这个变量一直保存在内存中但又不会“污染”全局的变量,这个时候,我们就可以用闭包来定义这个模块
在看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);
我们知道了 什么是闭包:当一个内部函数被其外部函数之外的变量引用时,就形成了一个闭包。
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);   },
问题关键来了。
如何实现无new构建
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(){} }
通过new aquery(),虽然返回的是一个实例,但是也能看出很明显的问题,死循环了!
那么如何返回一个正确的实例?
在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(){} }
当执行aquery() 返回的实例:
很明显aquery()返回的是aquery类的实例,那么在init中的this其实也是指向的aquery类的实例
问题来了init的this指向的是aquery类,如果把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只是指向aquery类的,所以aquery的age属性是可以被修改的。
这样看似没有问题,其实问题很大的
为什么是new jquery.fn.init?
看如下代码:
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的值,我也希望得到默认age的值20,但是aquery(b).age 的值为18.
因为在 调用aquery(a).age 的时候age被修改了。
这样的情况下就出错了,所以需要设计出独立的作用域才行。
jquery框架分隔作用域的处理
jquery = function( selector, context ) {     // the jquery object is actually just the init constructor 'enhanced'     return new jquery.fn.init( selector, context, rootjquery );   },
很明显通过实例init函数,每次都构建新的init实例对象,来分隔this,避免交互混淆
我们修改一下代码:
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'
又出现一个新的问题,
age  :undefined,
name() :抛出错误,无法找到这个方法,所以很明显new的init跟jquery类的this分离了
怎么访问jquery类原型上的属性与方法?
做到既能隔离作用域还能使用jquery原型对象的作用域呢,还能在返回实例中访问jquery的原型对象?
实现的关键点
// 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);
是不是明白了?
哈哈哈~~~
在简单说两句:
大部分人初看 jquery.fn.init.prototype = jquery.fn 这一句都会被卡主,很是不解。但是这句真的算是 jquery 的绝妙之处。理解这几句很重要,分点解析一下:
1)首先要明确,使用 $('xxx') 这种实例化方式,其内部调用的是 return new jquery.fn.init(selector, context, rootjquery) 这一句话,也就是构造实例是交给了 jquery.fn.init() 方法取完成。
2)将 jquery.fn.init 的 prototype 属性设置为 jquery.fn,那么使用 new jquery.fn.init() 生成的对象的原型对象就是 jquery.fn ,所以挂载到 jquery.fn 上面的函数就相当于挂载到 jquery.fn.init() 生成的 jquery 对象上,所有使用 new jquery.fn.init() 生成的对象也能够访问到 jquery.fn 上的所有原型方法。
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 对象。
相信看了本文案例你已经掌握了方法,更多精彩请关注其它相关文章!
推荐阅读:
jquery总体架构分析与使用详解
jquery判断上传图片类型与大小方法详解
以上就是jquery无new构建使用详解的详细内容。
其它类似信息

推荐信息