jQuery是一个快速、简洁的JavaScript框架,是继Prototype之后又一个优秀的JavaScript代码库(或JavaScript框架)。jQuery设计的宗旨是“write Less,Do More”,即倡导写更少的代码,做更多的事情。它封装JavaScript常用的功能代码,提供一种简便的JavaScript设计模式,优化HTML文档操作、事件处理、动画设计和Ajax交互。
如此优秀的一个代码库,即便没有以前流行,也是有很多地方值得学习的。
本文并非讲解jquery
源码,而是记录从源码学习到的思想、技术等
构造函数与继承
JavaScript
是一门面向对象的编程语言,但是与传统面向对象语言不同的是,JavaScript
没有提供class
关键字,并且JavaScript
的是基于原型继承的面向对象语言。那么,在JavaScript
中编写类的过程,必定与传统的面向对象语言有所区别。
传统的类与继承
在ES6
之前,都是通过创建构造函数的方式来创建一个类,比如:
// Book类的原型
const BookPrototype = {
getName () {
return this.name
}
}
// Book类
function Book (name, author, content) {
this.name = name
this.author = author
this.content = content
}
// Book类继承自 BookPrototype
Book.prototype = BookPrototype
const book = new Book('金瓶梅', '佚名', '此处省略999999999字')
console.log(book.getName())
BookPrototype
为Book
类的原型(基类),使用new
关键字创建的Book
实例,都会包含BookPrototype
里面的属性和方法。这里刚开始然我有些迷惑,参考经典的面向对象语言,基类应该也是一个类,这里却是一个对象字面量。首先,需要深刻明白一点: JavaScript中,一切皆是对象。比如一个函数,我们可以给它加上任何的属性
function bar () {}
bar.sayHi = function () {
console.log('hi~')
}
bar.sayHi()
// hi~
其次,也是这里的迷惑点,JavaScript
是基于原型的继承,意思就是,一个构造函数要继承某些属性方法,只需要将这些方法放到一个容器(对象)里,再赋值给prototype
属性就可以在实例中访问。当我们需要创建静态方法/变量的时候,可以直接在构造函数上绑定(一切皆对象)。在JavaScript中,静态方法不会被继承。
function Book (name, author, content) {
this.name = name
this.author = author
this.content = content
}
Book.prototype.getName = function () {
return this.name
}
// 静态方法
Book.foo = function () {
console.log('foo')
}
Book.foo()
ES6中的类与继承
在ES6
标准中,提出了class
关键字,我们可以使用类似于Java
创建类的语法来创建一个JavaScript
类。
class Book {
constructor(title, author, content) {
this.title = title
this.author = author
this.content = content
}
getName () {
return this.title
}
}
const book = new Book('金瓶梅', '佚名', '此处省略999999999字')
console.log(book.getName()) // 金瓶梅
我们可以直接通过extends
关键字进行类的继承
class StoryBook extends Book {
constructor(title, author, content, storyType) {
super(title, author, content)
this.storyType = storyType
}
getStoryType () {
return this.storyType
}
}
book = new StoryBook('金瓶梅', '佚名', '此处省略999999999字', 'fairyTale')
book.getName() // 金瓶梅
book.getStoryType() // fairyTale
ES6
中的class
和extends
实际上都是语法糖,根本上还是通过前一节中的方法来创建类和继承类。JavaScript
基于原型的继承方式,赋予了更多的灵活性,jQuery
的构造函数就是一个非常好的实例。
jQuery入口
从jQuery
最基本的使用出发,会发现jQuery
好像不是一个构造函数。确实是这样,当我们获取一个元素const box = $('.box1')
,将box打印出来看看。
从打印结果可以看出几个东西:
box
对象是jQuery.fn.init
的一个实例- 改实例是一个包含类数组集合对象
- 改对象有一个
prevObject
属性,指向了两一个对象的引用
接下来就分别从这几点出发来分析jQuery
源码。
jQuery构造函数
通常情况下,我们会以函数调用方式($('selector'))获取一个jQuery
对象,同时,我们也可以使用new $('selector')
来获取jQuery
实例,我们先来思考这一个问题:如何同时满足函数调用和构造函数调用。实际上不需要太多思考就能想到如下代码:
function myQuery (selector) {
if (!(this instanceof myQuery)) {
return new myQuery(selector)
}
// ...
return this
}
但是我们发现jQuery
并没有这样写,通过观察发现我们得到的是一个jQuery.fn.init
的实例,下面就开始进入源码看看jQuery.fn
和jQuery.fn.init
是个什么东西。
// 源码
jQuery = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
// Need init if jQuery is called (just allow error to be thrown if not included)
return new jQuery.fn.init( selector, context );
};
这是jQuery
的大门,所有故事从这里开始,至此我们知道了jQuery
构造函数实际上是个工厂函数,它返回了另一个类的实例。接下来继续看看jQuery.fn
jQuery.fn = jQuery.prototype = {
// The current version of jQuery being used
jquery: version,
constructor: jQuery,
// The default length of a jQuery object is 0
length: 0,
// ...
};
init = jQuery.fn.init = function( selector, context, root ) {
// ...
return this
};
// Give the init function the jQuery prototype for later instantiation
init.prototype = jQuery.fn;
可以看出jQuery.fn
就是jQuery.prototype
的一个引用,而init
是原型上的一个构造函数。里面有非常核心的一行代码init.prototype = jQuery.fn
。这行代码将jQuery
的原型共享给了init
构造函数,jQuery
和init
的原型是同一个,这样jQuery
对象就能访问jQuery
原型上的方法和变量。
到这里基本清楚了jQuery
创建实例到底是怎么一回事,至于和我们刚开始想的不一样,主要原因是更方便的进行对象的初始化操作。
jQuery插件
说到了原型顺便再看一下jQuery
的插件,jQuery
的插件开发基本上使用过jQuery
的人都知道怎么操作,分为两种方式,一种方式是直接在$.fn
上面添加方法,另一种是调用$.fn.extend()
方法。前面这两种方法都是对jQuery
的原型进行扩展,在其实例中才能使用,还有一个$.extend()
方法,是用来扩展jQuery
的静态方法。
// 源码
jQuery.extend = jQuery.fn.extend = function() {}
源码中,jQuery.extend
和fn.extend
引用的是同一个方法,但是作用的上下文却不同。这里是非常巧妙的一个点,当我们直接使用$.extend()
时添加的是静态方法,而是用来$.fn.extend()
时添加的是实例方法。
小结
jQuery
并没有非常高级的技术,内部原理都是非常简单的逻辑封装,但是整个内部架构设计的非常好,有许多值得学习的巧妙设计。