源码中应用 prototype 原型来做处理。那么用 prototype 处理的好处是什么呢?
首先举个例子:
用构造函数生成实例对象:
上面例子说明了,每个实例对象都有自己的方法和属性的副本,这样就无法做到数据共享,而且也很浪费资源。这样操作多了,对性能上也是有一定的影响。javascript中的每个对象都有prototype属性,Javascript中对象的prototype属性的解释是:返回对象类型原型的引用。每一个构造函数都有一个属性叫做原型。这个属性非常有用:为一个特定类声明通用的变量或者函数。prototype 就像一把钥匙,只要拥有这把钥匙,你就可以享有 构造函数 这个房子里的一切。
下面这一小段代码就说明了先会在自身实例中查找一个属性或方法。
这里面有一个注意点,就是你更改一个实例对象的属性时,并不会影响到原型。
源码中,下面的代码可谓是jQuery的整个架构了。(这是我从jquery源码中抽离出来的)
1.jq 无 new 构建,就是因为 jq 为我们封装了 new。
每次调用$()方法等于是创建了一个新的实例,所以说为此为了减少多余空间,以后最好用var a = $();的方式让下文使用提供方便,避免多余的封装和空间的浪费。
- $(“xxx”) 这种实例化方式,其内部调用的是 “return new jQuery.fn.init( selector, context );” ,就是构造实例交给 jQuery.fn.init 方法去处理。
- init = jQuery.fn.init = function( selector, context, root ) {} ,jQuery.fn.init 的方法就与 jQuery.init方法像等同,
- jQuery.init.prototype 指向 jQuery.fn,然后 jQuery.fn 最终指向 jQuery.prototype。所有jq对象都有jQuery.fn的属性和方法。
到这里虽然弄明白了 jQuery 源码的逻辑。这时,俺就又不明白了,为个啥子非要弄得这么麻烦,直接像下面这种写法不就行了么。
看到这个结果,呵呵哒了吧,都像你想的那么简单,你早就是大神了。看没,无限的递归自己,所以造成死循环并溢出了。所以人家 jQuery采取的手段是把原型上的一个init方法作为构造器。
在上面的代码中再加上这句话:
通过原型传递解决问题,把jQuery的原型传递给jQuery.prototype.init.prototype,换句话说jQuery的原型对象覆盖了init构造器的原型对象。
对上面的代码还有一点需要说明一下,为什么要写 constructor: yjQuery 这句话:用 obj.prototype 去写一个东西的时候,constructor 的指向就不是构造函数,而是object ,所以这里要手动的把 constructor 设置为原构造函数。
还有在jQuery里面,你既可以用$.isArray()调用jq的静态方法,也可以通过$().css()获取对象执行实例方法。慕课网jq源码解析中也有解释说是静态与实例方法共享设计。
所谓静态方法是jQuery本身得公共方法,并不需要通过实例化来调用,一般也称为工具方法。反之,实例化方法就是需要实例化调用。来段代码瞧一瞧,看一看啦!
再说一下jq架构中的源码
jQuery的无new构建原理中在$()内部中首先保证是通过new创建,使得我们在函数调用中可以使用this来代表该jq实例,通过原型的共享而实现了静态方法与实例方法的共存。这样 this 的指向一直指的是 jQuery 的对象。
——————————————-华丽的分界线————————————————————–
加个分界线其实是有一点要补充一下:
通过prototype 或是 this 指针来绑定的实例方法,直接定义在实例上的变量的优先级要高于定义在“this”上的,而定义在“this”上的又高于 prototype定义的变量。即直接定义在实例上的变量会覆盖定义在“this”上和prototype定义的变量,定义在“this”上的会覆盖prototype定义的变量。
可以把上面的代码copy一下,直接进行测试。
总结:
通过new jQuery.fn.init() 构建一个新的对象,拥有init构造器的prototype原型对象的方法
通过改变prorotype指针的指向,让这个新的对象也指向了jQuery类的原型prototype
所以这样构建出来的对象就继续了jQuery.fn原型定义的所有方法了
2017-11-13