本人是一名.net程序员.....

你一个.net coder 看什么jQuery 源码啊?

原因吗,很简单。技多不压身吗(麻蛋,前端工作好高...羡慕)。

我一直都很喜欢JavaScript,废话不多说了,直接切入正题。

最近看了好几篇jQuery 源码的文章,对于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__"进行简单的介绍:

  • 对于所有的对象,都有__proto__属性,这个属性对应该对象的原型
  • 对于函数对象,除了__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();//
c();//
c();//

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 //

因为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 //
aQuery("b").age //

当我调用 传入"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 //
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 //
aQuery("b").age //
aQuery("a").name() //

最后在看一下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)也就是实例化方法存在这么一个关系链

  • jQuery.fn.init.prototype = jQuery.fn = jQuery.prototype ;
  • new jQuery.fn.init() 相当于 new jQuery() ;
  • jQuery() 返回的是 new jQuery.fn.init(),而 var obj = new jQuery(),所以这 2 者是相当的,所以我们可以无 new 实例化 jQuery 对象。

jquery源码学习笔记(一)jQuery的无new构建的更多相关文章

  1. jquery源码学习笔记三:jQuery工厂剖析

    jquery源码学习笔记二:jQuery工厂 jquery源码学习笔记一:总体结构 上两篇说过,query的核心是一个jQuery工厂.其代码如下 function( window, noGlobal ...

  2. 菜鸟的jQuery源码学习笔记(前言)

    前言 相信任何一名前端开发人员或者是前端爱好者都对jQuery不陌生.jQuery简单易用,功能强大,特别是拥有良好的浏览器兼容性,大大降低了前端开发的难度,使得前端开发变得“平易近人起来”.自从本人 ...

  3. jQuery源码学习笔记一

    学习jQuery源码,我主要是通过妙味视频上学习的.这里将所有的源码分析,还有一些自己弄懂过程中的方法及示例整理出来,供大家参考. 我用的jquery v2.0.3版本. var rootjQuery ...

  4. jquery源码学习笔记二:jQuery工厂

    笔记一里记录,jQuery的总体结构如下: (function( global, factory ) { //调用factory(工厂)生成jQuery实例 factory( global ); }( ...

  5. jquery源码学习笔记一:总体结构

    练武不练功,到老一场空.计算机也一样. 计算机的功,就是原理.如果程序员只会使用各种函数,各种框架,而不知其原理,顶多熟练工人而已.知其然,更要知其所以然. jquery我们用得很爽,但它究竟咋实现的 ...

  6. jQuery源码学习笔记二

    //添加实例属性和方法 jQuery.fn = jQuery.prototype = { // 版本,使用方式:$().jquery弹出当前引入的jquery的版本 jquery: core_vers ...

  7. jQuery 源码学习笔记

    //检测 window 中新增的对象 //first var oldMap = {}; for(var i in window) { oldMap[i] = 1; } //second for(var ...

  8. 菜鸟的jQuery源码学习笔记(二)

    jQuery对象是使用构造函数和原型模式相结合的方式创建的.现在来看看jQuery的原型对象jQuery.prototype: jQuery.fn = jQuery.prototype = { //成 ...

  9. 2016年11月2日——jQuery源码学习笔记

    1.jQuery()函数,即$().有四种不同的调用方式. (1)传递CSS选择器(字符串)给$()方法,返回当前文档中匹配该选择器的元素集.可选第二个参数,一个元素或jQuery对象,定义元素查询的 ...

随机推荐

  1. C# 对象引擎,以路径形式访问对象属性(data.Product[1].Name)

    对象引擎,以路径形式访问对象属性,例data.Product[1].Name. 在做excel模板引擎的时候,为了能方便的调用对象属性,找了一些模板引擎,不是太大就是不太适用于excel, 因为exc ...

  2. C# 成员变量和属性的区别

    之前一直在C#中使用这两者, 却一直不知道成员变量和属性还是不一样的两种概念. 不过说不一样, 也不是完全对. 简单举个例子: public class myclass { public string ...

  3. 【cocos2d-x 手游研发小技巧(7)图片资源加密,Lua文件加密】

    游戏开发中常遇到资源保护的问题. 目前游戏开发中常加密的文件类型有:图片,Lua文件,音频等文件,而其实加密也是一把双刃剑. 需要安全那就得耗费一定的资源去实现它.目前网上也有用TexturePack ...

  4. UCS2-little endian转码(utf16)

    public static void readFile(){ BufferedReader in = null; try { in = new BufferedReader(new InputStre ...

  5. 极大提高Web开发效率的8个工具和建议(含教程)

    面对复杂的 Web 应用的开发,良好的流程和工具支持是必不可少的,它们可以让日常的开发工作更加顺畅.更加高效.本文介绍了6个Web开发利器以及相关的教程,帮助你在开发.调试.集成和发布过程极大地提高效 ...

  6. tableView cell 设置圆角 响应链

    1.  tableView的样式必须为 UITableViewStyleGrouped 样式否则cell设置的圆角无效. 2. delegate用weak和assign的区别: A:用weak修饰是为 ...

  7. 简述项目中优化sql语句执行效率的方法,从哪些方面,sql语句性能如何分析?

    (1)尽量选择较小的列: (2)将where中用的比较频繁的字段建立索引: (3)select中避免使用*: (4)避免在索引列上使用计算.not in和<>等操作: (5)当只需要一行数 ...

  8. FunDA(5)- Reactive Streams:Play with Iteratees

    FunDA的设计目标就是把后台数据库中的数据搬到内存里,然后进行包括并行运算的数据处理,最后可能再对后台数据库进行更新.如果需要把数据搬到内存的话,那我们就必须考虑内存是否能一次性容纳所有的数据,有必 ...

  9. webpack快速入门——如何安装webpack及注意事项

    1.window+R键,输入cmd打开命令行工具,输入 mkdir XXXX(XX:文件夹名): 2.cd XXX 进入刚刚创建好的文件夹里,输入cnpm install -g webpack (安装 ...

  10. Class 和 MetaClass

    在 OC 中,类的一个实例定义如下: /// Represents an instance of a class. struct objc_object { Class _Nonnull isa OB ...