实现一个jQuery的API

jQuery API

1
2
3
4
5
6
window.jQuery = ???
window.$ = jQuery

var $div = $('div')
$div.addClass('red') // 可将所有 div 的 class 添加一个 red
$div.setText('hi') // 可将所有 div 的 textContent 变为 hi

实现上述API

分析: 显而易见,$ 是一个函数,它可以接收一个 CSS 选择器字符串参数,然后返回一个包含了选择器对应元素的对象(按照CSS选择器规则所能选中的元素),这个对象具有addClasssetText方法。

  1. jQuery是全局可调用的,可以将其声明为全局变量或直接将其作为 window对象的属性,并赋值为一个函数,这个函数接收一个 CSS 选择器字符串参数。

    1
    2
    3
    window.jQuery = function (selector){
    // 函数体...
    }
  2. 在函数体中,首先要实现的功能就是根据选择器找到对应的元素,可以使用window.querySelectorAll 方法实现。

    1
    let elements = windown.querySelectorAll(selector)  //根据传入的CSS选择器找到对应元素
  3. 由于我们要返回的是一个包含了选择器选中的各个元素的对象,接下来先声明一个空对象,然后遍历elements NodeList,将每一个元素依次放入这个空对象。

    1
    2
    3
    4
    5
    6
    7
    let temp = {}
    let length = elements.length

    for (let i=0;i<length;i++){
    temp[i] = elements[i]
    }
    temp.length = length
  4. 现在我们已经得到了一个包含了选择器对应的各个元素的对象temp,是时候给它添加addClasssetText方法了。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // addClass 方法,给temp对象中包含的每一个元素对象都增加一个class
    temp.addClass = function (className){
    for (let i=0;i<temp.length;i++){
    temp[i].classList.add(className) //遍历temp对象,得到其中的各个元素后使用 DOM API 添加类
    }
    }
    // setText 方法,给temp对象中包含的每一个元素对象都设置一段文本
    temp.setText = function (text){
    for (let i=0;i<temp.length;i++){
    temp[i].textContent = text
    }
    }
  5. 至此,temp对象中已经包含了选择器对应的各个元素,并且拥有了两个方法可以对它拥有的各个元素进行相应操作,只需把temp返回即可。

    1
    return temp
  6. 完整的代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    windown.jQuery = function (selector){
    //根据传入的CSS选择器找到对应元素
    let elements = windown.querySelectorAll(selector)
    //创建用于存放各个元素的空对象
    let temp = {}
    let length = elements.length
    //遍历NodeList,将各个元素放入temp对象
    for (let i=0;i<length;i++){
    temp[i] = elements[i]
    }
    temp.length = length
    // addClass 方法,给temp对象中包含的每一个元素对象都增加一个class
    temp.addClass = function (className){
    for (let i=0;i<temp.length;i++){
    temp[i].classList.add(className) //遍历temp对象,得到其中的各个元素后使用 DOM API 添加类
    }
    }
    // setText 方法,给temp对象中包含的每一个元素对象都设置一段文本
    temp.setText = function (text){
    for (let i=0;i<temp.length;i++){
    temp[i].textContent = text
    }
    }
    //将temp对象返回
    return temp
    }
    // 将jQuery函数赋值给$,就可以使用简称"$"。
    window.$ = windown.jQuery

总结改进

        通过这样的一系列封装,实现了类似jQuery选择器的功能,初步了解了jQuery背后的原理。同时发现这种实现的方法每次返回的temp对象的那两个方法都是temp的实例方法,都会各自占用内存而没有共享方法。我觉得可以定义一个Temp构造函数,这两个方法都定义在Temp函数的原型上,然后每次调用$时不是新建一个空对象temp,而是实例化一个Temp对象,再将各个元素依次放入。

改进后的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
windown.jQuery = function (selector){
//根据传入的CSS选择器找到对应元素
let elements = windown.querySelectorAll(selector)
let length = elements.length
//实例化一个空的Temp对象用于盛放各个被选中的元素
let temp = new Temp()
temp.length = length
//遍历NodeList,将各个元素放入temp对象
for (let i=0;i<length;i++){
temp[i] = elements[i]
}
//将temp对象返回
return temp
}

// 将jQuery函数赋值给$,就可以使用简称"$"。
window.$ = windown.jQuery

//Temp的原型上添加方法
function Temp(){}
Temp.prototype.addClass = function (className){
for (let i=0;i<this.length;i++){
this[i].classList.add(className)
}
}

Temp.prototype.setText = function (text){
for (let i=0;i<this.length;i++){
this[i].textContent = text
}
}