jQuery的实质, 自己写一个jQuery
2018-01-07 本文已影响102人
Apolo_Du
jQuery 的实质:
- 在了解了python的装饰器之后, 发现jQuery的本质就是一个装饰器, 只是它接受了不同的参数.
- python装饰器接受一个函数, 在装饰器内部用一个函数(wrapper)将其包装, 并返回处理后的结果.
- jQuery接受一个css选择器或dom对象作为参数, 返回一个包装后的对象(jQuery对象).
自定义DOM API
- 实现一个获取兄弟元素的API
- 通过定义 length属性, 让函数返回值可迭代, 成为一个类数组对象.
function getSiblings(node){
var allChildren = node.parentNode.children
var array = {length:0}
for (let i =0; i<allChildren.length; i++){
if(allChildren[i] !== item3){
array[array.length] = allChildren[i]
array.length += 1
}
}
return array
}
- 实现一个可批量增加/删除 类的API
我们可以使用点记法 obj.attr 或者 obj[attr] 来调用一个对象的成员
function addClass(node, classes){
for(let key in classes){
var value = classes[key]
var methodName = value?'add':'remove'
node.classList[methodName](key)
}
}
- 实现一个命名空间
- JS中并不提供原生的命名空间支持
- 我们可以创建一个简单对象字面量来打包所有的相关函数和变量。
- 这个简单对象字面量模拟了命名空间的作用。
- 避免覆盖全局变量
- 为我们自定的API提供了一个统一的入口
window.myDom ={}
myDom.addClass = addClass
myDom.getSiblings = getSiblings
-
让一个dom对象具有我们定义的API
- 方法1:
- 扩展 Node 接口, 在 Node.prototype上加函数.
- 这个方法是不好的
- 方法1:
-
方法2:
- 使用一个wrapper函数来包装这个对象并返回它, jQuery就是用这种的方式实现的
- 接收一个dom对象, 返回一个新的jQuery对象.
- jQuery 还能够区分传入的是字符串或是node节点
- 使用一个wrapper函数来包装这个对象并返回它, jQuery就是用这种的方式实现的
window.Node2 = function(nodeOrSelector) {
let node
if (typeof nodeOrSelector === 'string') {
node = document.querySelector(nodeOrSelector)
} else {
node = nodeOrSelector
}
return {
getSiblings: function() {
var allChildren = node.parentNode.children
var array = {
length: 0
}
for (let i = 0; i < allChildren.length; i++) {
if (allChildren[i] !== item3) {
array[array.length] = allChildren[i]
array.length += 1
}
}
return array
},
addClass: function(classes) {
for (let key in classes) {
var value = classes[key]
var methodName = value ? 'add' : 'remove'
node.classList[methodName](key)
}
}
}
}
- 实现一个jQuery选择器
- 我们要实现类似jQuery的功能, 每次都要能够获得一个jQuery对象这样的类数组对象.
- 如果参数是一个字符串, 我们就通过 querySelectorAll 获取一个类数组对象
- 迭代获得的对象, 将它的每一个元素放到新的对象中.
- 要保证自定义的类数组对象有index下标和length属性. 这样它才是可迭代的
- 如果参数是一个字符串, 我们就通过 querySelectorAll 获取一个类数组对象
- 如果参数是一个节点, 我们同样它放到一个具有 index 和 length的自定义类数组对象中.
- 我们要实现类似jQuery的功能, 每次都要能够获得一个jQuery对象这样的类数组对象.
window.jQuery = function(nodeOrSelector) {
let nodes = {}
if (typeof nodeOrSelector === 'string') {
let temp = document.querySelectorAll(nodeOrSelector)
//我们不要nodeList, 要一个自定的类数组元素
for (let i = 0; i < temp.length; i++) {
nodes[i] = temp[i]
}
nodes.length = temp.length
} else if (nodeOrSelector instanceof Node) {
nodes = {
0: nodeOrSelector,
length: 1
}
}
- 对选择器对象设置 getText 和 setText 方法
- getText方法要返回一个可迭代对象.
- 其中每个元素存储一个选择器元素的 textContent值
- setText 方法要对选择器进行统一设置
- 对选择器的内一个元素的 textContent 进行设置.
- getText方法要返回一个可迭代对象.
nodes.getText = function() {
let text = []
for (let i = 0; i < nodes.length; i++) {
text.push(nodes[i].textContent)
}
return text
}
nodes.setText = function(text) {
for (let i = 0; i < nodes.length; i++) {
nodes[i].textContent = text
}
return nodes
}
- jQuery 中的 getText 和 setText 方法
- 在jQuery中, 两种方法被合并了, 通过判断是否有传参数, 来执行不同的代码语句
nodes.text = function(text) {
if (text === undefined) {
let text = []
for (let i = 0; i < nodes.length; i++) {
text.push(nodes[i].textContent)
}
return text
} else {
for (let i = 0; i < nodes.length; i++) {
nodes[i].textContent = text
}
return nodes
}
}
-
这其实类似python中的 @property 和 @attr.setter, 为getter方法和setter方法 提供了一个统一的接口
- @property装饰器
-
nodes.text 函数和闭包
- nodes.text 函数内部没有声明 nodes变量, 而是引用了函数外部的变量, nodes.text 函数和 nodes变量就构成了一个闭包.
-
自定义jQuery的完整代码:
window.jQuery = function(nodeOrSelector) {
let nodes = {}
if (typeof nodeOrSelector === 'string') {
let temp = document.querySelectorAll(nodeOrSelector)
//我们不要nodeList, 要一个自定的类数组元素
for (let i = 0; i < temp.length; i++) {
nodes[i] = temp[i]
}
nodes.length = temp.length
} else if (nodeOrSelector instanceof Node) {
nodes = {
0: nodeOrSelector,
length: 1
}
}
nodes.addClass = function(classes) {
for (let key in classes) {
var value = classes[key]
var methodName = value ? 'add' : 'remove'
for (let i = 0; i < nodes.length; i++) {
nodes[i].classList[methodName](key)
}
}
}
nodes.text = function(text) {
if (text === undefined) {
let text = []
for (let i = 0; i < nodes.length; i++) {
text.push(nodes[i].textContent)
}
return text
} else {
for (let i = 0; i < nodes.length; i++) {
nodes[i].textContent = text
}
return nodes
}
}
return nodes
}