目的
- 用jQuery风格封装DOM库,实现简单的一些增删改查功能。理解jQuery的特点,体会jQuery的设计模式
全局函数

小知识点


- 两种写法等价,addClass是api对象里面的一个key,value是function,
遍历所有.test元素,加入red类名

- 初步思路:调用querySelectorAll,输入选择器参数筛选出所有元素
- 但是:jQuery返回的不是elements这个数组,return elements无效,返回的是对象
- 只能通过对象操作elements
- 声明对象,这个对象叫做api
- api里面声明一个函数addClass,用来操作elements(以下简称数组)
- 函数接收className,循环遍历数组、
- 数组的每一项的类名里面添加一个参数类名(DOM指令的含义)
- 返回api
- 结构图:
全局函数{
得到数组 elements;
api对象{ 对elements数组进行一系列的操作}
返回api
}

- 获取api,操作对象是(类名为test的元素组成的数组)
- 操作数组,往数组里面所有元素添加red类名
变化


- addClass函数里面添加
return api
- 于是api每调用一次自己的内在函数,就返回这个对象,再次调用内在函数,继续调用,即操作一次数组,再一次操作数组
- 如图二,api调用一次addClass,对数组添加red类名,返回api,再次调用addClass,再一次添加blue类名
以上操作叫做 链式操作,实现方法是:对象的函数添加返回这个对象的功能,从而实现连续调用函数的功能(类似套娃)
变化,this操作
- 重温this

- 两个式子等价,obj.fn.call(this,p1),这里的this就是obj
- 同理: api.addClass(className)等价于api.addClass.call(this,className)
- 函数里面的this就是对象api本身
- 所以api的执行函数里面返回值直接return this,效果等价,如下图:

为什么使用this?直接api不是更直接吗? 因为这个api对象没有名字,只是一开始就赋值给了叫api的变量,用this可以避免声明变量
模块分析

- 代码总体分两步
- 1.筛选出elements,2.返回这个可以操作elements的对象
- const api 只是过渡,因为这个对象没有名字
变化第一步:使用this,对象没有名字无所谓

- 两步走:筛选条件,得到elements数组
- 返回对象,这个对象能操作elements数组,对象没有名字
- 对象里面有个属性,key为addClass,value为function(className){}
- 可写成
addClass:function(className){...}
- 这个属性可以对数组进行操作,并且自带返回对象功能,实现链式操作

- 调用指令简化
- jQuery筛选出elements,返回对象
- 返回的对象无名,直接.addClass,调用属性,进行操作
得出jQuery第一个精髓:用闭包维持elements,实现操作;返回对象自己(this),实现链式操作
- 用选择器筛选出元素,组成elements(数组)
- 不直接返回elements,返回对象
- 对象能对数组里面的元素进行一系列操作
- 对象一直调用外部的elements,用来维持这个elements不被删除
- 对象的函数返回自己,链式操作
jQuery是构造函数吗?
是:因为jQuery函数确实构造了一个对象
不是: 因为不需要写newjQuery()就能构造一个对象
结论:jQuery是一个不需要加new的构造函数,不是常规意义上的构造函数,这是因为jQuery用了一些技巧
查找.test里的 .child元素(父元素test里面所有的。child元素)

- 写的位置,return对象内,与addClass()并列
- 为什么?因为是先筛选父元素,再筛选子元素,结合jQuery返回对象思考
- 定义find()函数,参数为选择器,声明空数组array
- 循环条件,以elements.length为准,遍历数组(结合以下调用指令理解)
- 声明变量elements2,用来接收筛选出的元素组成的数组
- 对elements数组进行操作
- elements数组的第i项,筛选出来元素,由于组成了伪数组,所以转化成数组即添加了Array.from在前
- array空数组与elements2建立连接,类似于将里面的元素填进去
- 返回arry数组,得到结果
这个代码完美了吗?链式操作能实现吗?(return this?)
- 假设函数find函数reurn this,调用指令,如下图

但是最终加到了test里面,因为find()函数是在对象里面的(return后面的{})
- 找test里面的find,分成了两步
- 传入参数test,全局函数筛选出了test,返回了针对test操作的对象
- 传入参数child,确实是找到了child,但是返回了外面的对象,即它是针对test的
- find的返回值其实是把步骤回退了,this根本没有加在child上面
- 思考解决方法:不能为了链式操作强行将elements=array,因为调用指令涉及覆盖问题,只能正向操作,不能返回,不然出错
$div.find 返回新的 api 对象(重新封装jQuery)
思路:既然之前那只能接受选择器,想办法将元素递给jQuery,find()接收数组,封装一个新的api
不返回操作test的对象,返回操作child的对象

- 问题的根源,每当找到了子元素,返回的却是操作父元素的对象,加.red属性加在了父元素身上
- 那不返回操作父级元素的对象,重新声明对象(newapi),这个对象是什么
- 将array传到Jquery,返回的就是针对array操作的对象,再加熟悉就加到了array上
- 比如.test里面有.children。传入test,筛选出涉及test的数组,返回操作test数组的对象,再找child
- 调用find()函数,得到child的数组,传到全局函数中,返回针对child数组的对象,那么再加.red就加到了child身上

- 全局函数(jQuery筛选元素)
- 外面声明elements,作用域问题,所以在外面,elemengts是变化的,要用let
- 如果传入的参数是字符串(其实就是指.test这样的参数),照旧,选出所有这个参数对应的对象,组成数组,后面返回的就是针对test的操作
- 如果传入的是字符串,那么elements就是这个字符串,后面返回的就是这对这个字符串的操作(比如。child返回到了这句话,接下来就是这对child的操作)
- 小结:if其实是针对选择器初步筛选的操作,比较常规,else则是针对父元素的子元素的操作
以上操作利用了JQurey传什么,操作什么的特点
- 根源就是find函数的返回值
return jQuery(array),实现后面针对筛选出的子元素数组的操作
如果想回到test怎么办? end函数

- 1.return的对象添加一个属性,叫OldApi,值为参数的OldApi
- 单独的属性是没有意义的,只是表明,oldApi属性,属性就是selectorOrarray的...
- slectorOrarray指的其实就是数组,只不过当它是字符串的时候是用不到这个属性的
- 当find调用了这个属性,才表明,array的这个属性是this(最外面的test对象),才有实际意义上的意义
- 2.find函数添加 新数组的旧api就是这个对象(child的数组的旧对象就是test对象)
- 3.当对child添加一些属性后(调用find或者addClass),返回的是child对象,调用end函数,这里的this就是child对象,加上oldApi,指的是test对象
结合以下调用指令理解

- 输入test指令,找到child,addclass不影响亮
- find()函数,array数组是child数组,旧的api是this,就是test(这里的this指的就是最外面return的对象)
- child对象调用end,返回它的就对象(test),然后旧对象添加yellow,即test里面添加yellow
遍历

- 最外面的对象(api)添加函数each
- each传一个参数fn,fn做什么先不考虑
- 遍历数组,fn传参,传数组的第i项目,因为jq第一步已经筛选好了结果组成了elementts数组,第i项就是元素
- fn.call()里面的this是空,第二个参数是elements[i],第三个参数是i
- 返回this,就是api
- 到此为止只是遍历了数组里面的元素,至于怎么做看指令

- 找到test里面的child,打印出来
- 声明对象x
- 先找到child
- 全局找test,返回操作test数组对象,这个对象再调用find,传参child,得到child数组,返回操作child数组对象(find函数最后一句回顾)
- 整天指令的结果,即操作child的对象赋值给x
- x.each就是操作child数组的对象调用each
- each函数调用fn临时函数,并循环,fn的参数就是数组的每一个元素和i,this取空(call函数回顾)
- fn就是为了打印每一个元素,这里的div是形式参数,其实就是指的元素
- 小结:全局找test,find找child,调用each,循环遍历,调用fn,fn传参遍历的数组元素,fn干什么?看指令,
找到父元素

- parent函数
- 声明空数组array,用于接收元素
- this是api,调用上面的each函数,遍历数组
- 如果空数组的父元素不在里面,将父元素放进空数组,当大于零时停止,防止重复
- 返回 操作array的对象
- 操作array的对象添加print函数,用来打印元素
找到子元素

- 注意:这里不需要再排除重复了,只存在拥有同一个父元素,不存在拥有同一个子元素
- this.each遍历数组(test的)
- 空数组array逐个放入 test的子元素(... )
- 具体说明后面的...

- 调试,发现node.children是几个不同的数组,不是一个数组
- 前面加...是新语法,将两个数组所有元素拆开,逐个放入array里面
- 使用了console.log法
jQUerr代码美化

- 让window.$=window.jQuery=function()
- 调用指令直接:$('.test')
命名风格
代码产生误解
const div=$('div#test')
- 我们会误认为div是一个dom
- 实际上div是jQuery构造出来的api对象
避免误解
const $div=$('div#test')
- 变量前面以$开头,表明是jQuery构造的对象
- 以el开头,表明是DOM
- 是jQ,只能调用jQ,是DOM只能调用DOM
增


- 全局函数改写,如果是字符串,判断识别'<',识别标签
- DOM指令创建div标签
- 返回div对象调用appendTo函数
删
$div.remove() 直接删除
$div.empty() 找到子元素然后删除
改
$div.text(?)读写文本
$div.html(?)读写HTML内容
$div.attr('title',?)读属性
$div.css({color:'red'})读写style,是操作style内联元素的,不是操作css的
$div.addClass('blue')增加类名
$div.on('click',fn)
$div.off('click,fn')
注意点
- $div大部分情况下对应着多个div,需要有个遍历操作
使用原型思想
场景

- 同时得到api1,api2
- 两个api使用了相同的函数,但是函数的地址不同,造成内存浪费
- 方案:将公用的函数、属性放到jQuery的原型里面
- api1,api2是由JQuery构造出来的,所以
api1.__proto__==jQuery.prototype
- 那么api1,api2调用函数的时候就从同一个地址调用,做到内存的节约
- 操作如下

- 把函数放到jQuery的prototype中
- api的特有属性不动
- api是jQuery构造出来的,所以它的__proto__自带函数地址
- 那么不同的api调用函数的时候都是从同一个地址调,节省内存