Home » Code » 菜鸟学习jQuery源码Part1——jQuery对象的创建

菜鸟学习jQuery源码Part1——jQuery对象的创建

既是菜鸟,当然得依靠巨人的肩膀来学习^~^。主要参考资料:妙味课堂杜鹏讲师的视频。另外还有这些资料可以参考:艾伦大神在博客园的文章阿里巴巴资深工程师高云所著《jQuery 技术内幕》

由于新版jQuery加入对AMD之类的支持,这个啥也不懂,故是以2.0.3版本为基础进行学习。这个版本的发布日期是2013年07月03日,哇,已经过去2年半了。

jQuery一上来就是一个匿名自执行函数,这个不用多说,这么做是为了不污染全局。

(function(window, undefined) {
	//jQuery code
}(window));

匿名自执行函数有多种写法,这里就不讨论哪种好坏的问题了。这里主要看为何要将window当参数传进去,又为何定义一个undefined的形参却没有传递实参?如果不传window进去,在里边一样能访问得到,之所以传,一说法是加快访问速度,二是为压缩提供方便,如在内部形参的window就可以写为a。定义一个形参undefined却又不传实参,这主要是为了防止undefined被外部更改,还有这事?真有这事:

var undefined = 10;
(function(window) {
	alert(undefined);
}(window));

例如上边的代码,在IE9正常弹出undefined,在IE8它就是10了,当然其他现代浏览器都是正常的。可见,jQuery是考虑得非常全面的。

接下来定义了一些变量,如core_strundefined,这个其实就是undefined的字符串形式”undefined”,注释上写”Support IE9 For `typeof xmlNode.method` instead of `xmlNode.method !== undefined`”。个人理解是它告诉我们,判断一个对象的属性或方法是否存在,【 使用typeof obj.method === “undefined”比obj.method === undefined 】 更通用靠谱一些。

在38行开始有这么两行,_jQuery = window.jQuery,_$ = window.$。这是干啥子用的?这是防冲突用的。jQuery对外一般为我们提供$(),jQuery()这两个方法(其实是一样的),如果先前这两个已经被使用了,就会被存到添加了下划线的变量中来,如果没有,则是undefined,后边讲防冲突部分会有讲解。

在第47行定义一个数组core_deletedIds,注释写存储删除了的数据的id,以便再次使用。然后通过这个数组又是一堆对数组方法的引用。这个数组跟数据缓存有关,但在2.x版本其实已经没什么用。

在第61行,终于见到jQuery的定义:

// Define a local copy of jQuery,定义jQuery的一个本地副本
jQuery = function( selector, context ) {
	// The jQuery object is actually just the init constructor 'enhanced'
	//jQuery对象实际上只是init constructor的“增强”
	return new jQuery.fn.init( selector, context, rootjQuery );
},

jQuery对象的创建方式,是一个难点。通过调用jQuery()函数,直接返回一个jQuery对象,它使用时无须new操作。按照一般的做法,是在使用对象之前先new出来,要不用new,可能会想到在构造函数中进行new:

function Person() {
	return new Person();
}

Person.prototype = {
	name: function() {},
	age: function() {},
}

var p = Person();

很明显,这样子是进入了死循环,行不通!如何正确返回一个类的实例呢?类的实例那就是this,this跟原型有关,在原型上添加一个init方法返回this,那就可以获得对象本身。

function Person() {
	return Person.prototype.init();
}

Person.prototype = {
	init: function() {
		return this;
	},
	name: function() {},
	age: 20,
}
var p = Person();
console.log(p === Person.prototype);//true
console.log(p.age);//20

jQuery_create_obj
这里没有new操作,明显这里的这个对象这个this就是Person.prototype。没经过new没有开辟新的作用域,所有对象就都是Person.prototype,后来的覆盖前边的。

function Person(age) {
	return Person.prototype.init(age);
}

Person.prototype = {
	init: function(age) {
		this.age = age;
		return this;
	},
	name: function() {},
	age: 20,
}
var p = Person(10);
var p2 = Person(30);
console.log(p);
console.log(p.age);
console.log(p2);
console.log(p2.age);

jQuery_create_obj_no_new
很显然的,对象每个对象,都得经过new来开辟新的作用域来隔离开。new操作只能对函数进行,我们就来new init这个函数,它返回的是Person.prorotype.init的实例,是作用域不同的对象。

function Person(age) {
	return new Person.prototype.init(age);
}

Person.prototype = {
	init: function(age) {
		this.age = age;
		return this;
	},
	name: function() {},
	age: 20,
}
var p = Person(10);
var p2 = Person(30);
console.log(p);
console.log(p.age);
console.log(p2);
console.log(p2.age);
console.log(p === p2);

jQuery_create_obj_new
那么还有一个问题,那就是这些对象是Person.prototype.init的实例,并不是Person的实例,没法共享原型链啊。有什么办法能使Person.prototype.init的实例能访问到Person.prorotype上的属性方法?重载Person.prototype.init的prorotype啊!!!

function Person(age) {
	return new Person.prototype.init(age);
}

Person.prototype = {
	init: function(age) {
		// this.age = age;
		return this;
	},
	name: function() {},
	age: 20,
}
Person.prototype.init.prototype = Person.prototype;
var p = Person();
var p2 = Person();
console.log(p);
console.log(p.age);
console.log(p2);
console.log(p2.age);
console.log(p === p2);

jQuery_create_obj_key_step
我们并没有在创建对象时添加age属性,但对象都得到了age属性,这是因为Person.prorotype指向了Person.prototype.init.prorotype,这一步,乃jQuery当中创建对象关键的一步!在jQuery当中,还为jQuery.prototype起了个别名jQuery.fn,说法是为了书写方便,写插件时,扩展jQuery.fn,也就是扩展jQuery.prorotype,也就是扩展jQuery.prorotype.init.prorotype,而jQuery对象,都是由jQuery.prorotype.init创建的。因此,插件的方法对所有jQuery对象都起作用。

知道了jQuery如何创建对象,那就来说一下它的整体骨架:

(function(window, undefined) {
	var jQuery = function(selector, context) {
		return new jQuery.fn.init(selector, context);
	}
	jQuery.fn = jQuery.prototype = {
		constructor: jQuery,
		init: function(selector, context, rootjQuery) {...},
		//一堆原型属性和方法
	}
	jQuery.fn.init.prorotype = jQuery.fn;
	jQuery.extend = jQuery.fn.extend = function() {...};
	jQuery.extend({
		//一堆静态属性和方法
	});
	window.jQuery = window.$ = jQuery;
})(window);

可以看到,它的整个结构是很明晰明了的。

Leave a Reply

Your email address will not be published. Required fields are marked *

*

Time limit is exhausted. Please reload CAPTCHA.