一. jQuery风格
- 原始:
window.jQuery = function(selector) {
const elements = document.querySelectorAll(selector);
const api = {
addClass(className) { //闭包,函数访问了外部的变量
for (let i = 0; i < elements.length; i++) {
elements[i].classList.add(className);
}
}
};
return api; //不返回得到的元素数组,返回的是可以操作这个数组的对象api
}
定义一个jQuery(),是一个全局函数。通过selector传递参数,用于获取对应的元素。但是,它却不返回这些元素,而是返回一个对象api,这个对象里有一些函数(如addClass)可以操作这些元素。此时我们可以这样调用jQuery():
let api = jQuery(".test");
api.addClass("red") //获取class是test的元素,并且为他们添加一个red类
- 链式操作
此时我们的addClass函数没有返回值,如果给它一个返回值,让它返回调用它的对象,也就是api,那么我执行api.addClass("red")时,这条语句的值就是api,那我就可以直接在后边再调用函数,这就是链式操作。
window.jQuery = function(selector) {
const elements = document.querySelectorAll(selector);
const api = {
addClass(className) { //闭包,函数访问了外部的变量
for (let i = 0; i < elements.length; i++) {
elements[i].classList.add(className);
}
return api;
}
};
return api; //不返回得到的元素数组,返回的是可以操作这个数组的对象api
};
此时就可以链式调用函数:
let api = jQuery(".test");
api.addClass("red").addClass("blue"); //链式操作
- this
当调用对象的函数时,obj.fn(),JS会自动把函数前边的对象传给this。所以在调用api.addClass时,api就是this。所以我们可以在函数里用this代替api。
window.jQuery = function(selector) {
const elements = document.querySelectorAll(selector);
return {
addClass(className) { //闭包,函数访问了外部的变量
for (let i = 0; i < elements.length; i++) {
elements[i].classList.add(className);
}
return this;
}
} //jQuery构造出来的对象
};
既然用this指代了api对象,那就可以直接return这个对象。在外部调用addClass函数时,哪个对象调用的,this就是那个对象。
let api = jQuery(".test"); //api可以是任何名字
api.addClass("red").addClass("blue"); //.前边的对象就是this
jQuery是构造函数吗?
-
是?因为jQuery函数确实构造并且返回了一个对象。
-
不是?因为不需要在jQuery前加new。
jQuery是一个不需要加new的构造函数,特殊的构造函数。我们约定:
jQuery对象指jQuery函数构造出来的对象。(类似Object对象)
二. jQuery返回不同的api对象
- jQuery('.xxx').find('.child') 查找.xxx里的.child元素
window.jQuery = function(selectorOrArray) {
let elements;
if (typeof selectorOrArray === "string") {
elements = document.querySelectorAll(selectorOrArray);
} else if (selectorOrArray instanceof Array) { //对象判断类型用instanceof
elements = selectorOrArray;
}
return {
addClass(className) { //闭包,函数访问了外部的变量
for (let i = 0; i < elements.length; i++) {
elements[i].classList.add(className);
}
return this;
},
find(selector1) {
let array = [];
for (let i = 0; i < elements.length; i++) {
array = array.concat(
Array.from(elements[i].querySelectorAll(selector1)) //伪数组
);
} //得到的array就是查找的.xxx里的.child元素
return jQuery(array);
}
};
};
如果我想找.test元素里的.child元素:jQuery('.test').find('.child')如果我在find函数里 return array,那得到的就是.child数组。
但是我想对查找到的.child元素添加类:jQuery('.test').find('.child').addClass('red'),如果我在find函数里返回得到的数组,数组不能调用函数;如果我在find函数里return this,像addClass函数一样,那么red类会被添加到.test上,因为find函数返回的是操作.test的对象。
所以,我需要用jQuery得到一个新的对象,这个对象是操作我用find函数得到的元素数组的,而不是操作第一次被传进去的.test元素的。所以find函数里:return jQuery(array); 返回一个操作数组的对象。
这样,我执行jQuery('.test').find('.child').addClass('red') 时,red类就会被添加到.child上.
jQuery是一个全局函数,这个函数接收什么参数,就会返回一个操作这个参数的对象。
三. 其他函数
each(fn) {
for (let i = 0; i < elements.length; i++) {
fn.call(null, elements[i], i);
}
return this; //this就是当前的api对象!
},
parent() {
const array = []; //容纳爸爸
this.each(node => {
if (array.indexOf(node.parentNode) === -1) {
array.push(node.parentNode);
} //防止一个爸爸里有三个同名的孩子,这种情况下,如果不做判断,会返回三个一样的爸爸
});
return jQuery(array); //返回一个可以操作这些爸爸的对象
},
children() {
const array = [];
this.each(node => {
array.push(...node.children); //node.children是一个数组,新语法在数组前边加...,把数组展开成元素的意思
});
return jQuery(array);
},
next() {
const array = [];
this.each(node => {
array.push(node.nextElementSibling);
});
return jQuery(array);
},
prev() {
const array = [];
this.each(node => {
array.push(node.previousElementSibling);
});
return jQuery(array);
},
print() {
console.log(elements); //elements就是当前对象操作的元素们
}
四. 关于$
-
如果你嫌jQuery太长,可以给jQuery取一个别名:
window.$=window.jQuery$就代表jQuery
-
怎么区分我获取到的对象是DOM对象还是jQuery对象?
const div1=document.querySelector('#test')
const div1=$('#test')
在我对div1这个对象继续操作时,如果是DOM对象,就可以使用appendChild/querySelector等函数;但是如果是$对象,就用find/each等函数。但是这个div1对象不知道是DOM对象还是jQuery对象。所以改成这样:
const $div1=$('#test') 如果是jQuery对象,就在变量前加一个$,用作区分。
约定:所有$开头的变量都是jQuery对象。
五. jQuery.prototype
根据不用的参数得到两个对象:
const $div1=$('#test1')
const $div2=$('#test2')
这两个对象都有find,each等函数,这些函数是他们的共有属性。那么为了节省内存,我就可以把这些对象的共有属性放到jQuery.prototype里,然后让对象的__proto__指向这块内存。同时,jQuery又给prototype起了一个别名,fn。