一,重要的jQuery概念

1,jQuery的核心概念

1) jquery的核心函数: $ 这个是一个函数方法
2)jquery的核心对象:$() 所返回的对象
所以jQuery的两个特性是:
1】链式调用
2】读写合一:jquery作为函数调用和作为对象调用的不同的方法

  1. // 绑定文档监听
  2. $( function() {
  3. // 获取页面的元素,写交互的逻辑
  4. var $btn = $("#btn2") // 这个表示用的jquery的核心函数 $ 就可以拿到对应的dom元素
  5. // 这里可以看到,返回的不是dom的对象,因为dom对象没有后边的.方法,所以应该是jquery特有的对象,
  6. // 被jquery封装了一些方法
  7. $btn.click(function() {
  8. var username = $("#username").val()
  9. alert(username)
  10. })
  11. })

执行jquery函数,返回的就是jquery的对象

2,jQuery是什么

  1. // 这里是一个匿名的函数
  2. (function(window) {
  3. // 定义的jQuery,是一个函数
  4. var jQuery = function() {
  5. // 定义的这个jQuery返回的是jQuery对象
  6. return new jQuery
  7. }
  8. // 给window全局的对象绑定jQuery的方法
  9. window.$ = window.jQuery = jQuery
  10. }).(window) // 立即执行函数的调用,同时赋值了一个参数是window

3,jQuery里面的this是什么

this 依旧是 发生事件的那个dom元素

  1. $(function() {
  2. $("#btn2").click(function() {
  3. alert(this.innerHTML)
  4. })
  5. })

4,jQuery里面的$( ) 里面的参数可以是什么类型

1)可以是选择器
2)可以是dom元素
这个时候,里面得参数是this,而this指的是发生事件得那个dom元素,然后获取dom元素得html内容

  1. $(function() {
  2. $("#btn2").click(function() {
  3. // alert(this.innerHTML)
  4. alert($(this).html())
  5. })
  6. })

3)可以是一个函数,回调函数,就如同上面一样
4)

5,jQuery的遍历

1)语法的遍历,

  1. $(function() {
  2. var arr = [1,2,3]
  3. $.each(arr, function(index, item) {
  4. console.log(index,item )
  5. })
  6. })

2)遍历页面的元素

  1. <div>
  2. 用户名:<input type="text" id="username">
  3. <button> 使用jqury 获取input 的值 </button>
  4. <button> 使用jqury 获取input 的值 </button>
  5. <button> 使用jqury 获取input 的值 </button>
  6. <button> 使用jqury 获取input 的值 </button>
  7. </div>
  8. <script>
  9. // 遍历页面的button,这个时候,each里面的参数是每一个遍历的dom元素
  10. $('button').each(function(index, item) {
  11. console.log(index, item.innerHTML, this)
  12. })
  13. </script>

6,jQuery的dom伪数组

$ 有一个size和length的方法,这个表示选中的是对应dom元素的数组,这样就是一个伪数组

  1. // 查看$ 的dom 数组
  2. console.log($('button').length$('button').size() )

注意:伪数组没有数组相应的方法,伪数组是一个对象类型,只有length和index属性

7,使用index() 可以拿到其所在兄弟元素的下标

兄弟的dom元素

  1. <div>
  2. 用户名:<input type="text" id="username">
  3. <button id="btn1"> 使用jqury 获取input 的值 </button>
  4. <button> 使用jqury 获取input 的值 </button>
  5. <button id="btn"> 使用jqury 获取input 的值 </button>
  6. <button> 使用jqury 获取input 的值 </button>
  7. </div>
  8. <script>
  9. console.log($('#btn').index())
  10. console.log($('#btn1').index())
  11. console.log($('input').index())
  12. </script>

二,jQuery的选择器

jQuery 中文文档:http://jquery.cuishifeng.cn/

1,基本的选择器

  1. // id 选择器
  2. $('#btn-id')
  3. // class选择器
  4. $('.btn-class')
  5. // 元素 选择器
  6. $('input')
  7. // 并集选择器
  8. $('input', '#btn-id')
  9. // 交集选择器
  10. $('input''#btn-id')

2,层次选择器

用途:用来查找兄弟元素,子元素,后代元素的

  1. // 类似于css的选择器
  2. ancestor decendant // 找后代
  3. parent > child // 找孩子
  4. prev + next
  5. prev ~ siblings

3,过滤选择器

用途:在原有的选择器匹配的元素中进一步进行过滤的选择器
例子

  1. // 找到第一个div,设置为红色 —— API :first
  2. $('div:first').css('background', 'red')
  3. // 找到box元素的最后一个元素,设置为红色 —— API :last
  4. $('.box:last').css('background', 'red')
  5. // 选择div中没有box属性的元素(这个class中有没有box属性和没有class属性的box) —— API :not(selector)
  6. $('div:not(.box)').css('background', 'red')
  7. // 选取li元素的第2个和第3个元素 —— API :gt(index) :lt(index)
  8. // 多个过滤选择器是依次执行的,不是同时执行
  9. $('li:gt(0):lt(3)').css('background', 'red')
  10. // 选择li选择器里面内容为BBB的元素,标记为红色
  11. $('li:contains('BBB')').css('background', 'red')
  12. // 查找隐藏的元素
  13. $('li:hidden').css('background', 'red')
  14. // 选择有title属性的li元素
  15. $('li[title]').css('background', 'red')
  16. // 选择有title属性值为hello的li元素
  17. $('li[title=hello]').css('background', 'red')

例子:
给表格添加隔行变色的功能

  1. // 找到表格id=data的元素里面的奇数行
  2. $('#data>tbody>tr:odd').css('background', 'red')

4,表单选择器

  1. // 选择不可用的input为text的选择框
  2. $(':text:disable').css('background', 'red')
  3. // 显示已选择的爱好的个数
  4. $(':checkbox:checked').length

三,jQuery工具

  1. // 数组和对象操作
  2. $.each(object,[callback])
  3. $.inArray(val,arr,[from])
  4. $.type(obj)
  5. $.trim(str)

例子:
点击一个按键 显示对应的div内容
方法一:
1,给按钮都添加click事件
2,拿到点击的按键的下标
3,找到div的所有内容,设置所有的div的css为display:none
4,使用得到的下标去修改对应的div的dispaly:block
方法二:不一定需要把所有的div都去设置为display:none,可以把当前已经显示的修改为none,再把点击的显示为block,其他的元素可以不做操作

四,属性

操作元素的属性

五,css

  • 注意:jQuery的scrollTop的方法要获取页面的坐标方法对于chrome和ie浏览器有一个兼容问题
  • (一个获取页面的坐标需要使用html元素,另外一个需要body元素)
  • 解决方法

是需要整个页面的坐标会出现兼容,

  1. // 兼容 1
  2. $('html').scrollTop() + $('body').scrollTop()
  3. // 兼容2
  4. $(document.documentElement).scrollTop() + $(document.body).scrollTop

如果需要设置页面坐标为0(比如需求是回到顶部),就需要选择好两个选择器,使用交集选择器

  1. $('html,body').scrollTop(0)

需求:渐进式返回顶部(设置定时器不断的修改跳转的坐标)

  1. $(function () {
  2. // 需要渐进的滚动
  3. $('#to-top').click(function () {
  4. // console.log('点击')
  5. // 总的距离,需要兼容浏览器
  6. var distance = $('body').scrollTop() + $('html').scrollTop()
  7. console.log(distance)
  8. // 总的时间
  9. var time = 500
  10. // 间隔时间
  11. var intervalTime = 50
  12. // 间隔的距离
  13. var intervalDis = distance / (time / intervalTime)
  14. // 设置定时器
  15. var intervalId = setInterval(function () {
  16. // 不断修改坐标
  17. distance -= intervalDis
  18. // 当到达顶部的时候,就清除定时器
  19. if (distance <= 0) {
  20. distance = 0
  21. clearInterval(intervalId)
  22. }
  23. $('html, body').scrollTop(distance)
  24. }, intervalTime)
  25. })
  26. })

六,事件

mouseout([[data],fn]) —— 在进入这个元素的子元素的时候,也会调用out和over
mouseover([[data],fn])

mouseenter([[data],fn]) —— 在进入子元素的时候不会调用,只有进入这个元素会调用
mouseleave([[data],fn])
的区别

hover使用的是enter和leave

jQuery2 - 练习 - 选择爱好运动

需求:
功能说明:
1. 点击’全选’: 选中所有爱好
2. 点击’全不选’: 所有爱好都不勾选
3. 点击’反选’: 改变所有爱好的勾选状态
4. 点击’全选/全不选’: 选中所有爱好, 或者全不选中
5. 点击某个爱好时, 必要时更新’全选/全不选’的选中状态
6. 点击’提交’: 提示所有勾选的爱好
[ ] [ ] [ ] [ ] [ ]

  1. var $checkedAllBox = $('#checkedAllBox')
  2. var $items = $(':checkbox[name=items]')
  3. // 1. 点击'全选': 选中所有爱好
  4. $('#checkedAllBtn').click(function () {
  5. $items.prop('checked', true)
  6. $checkedAllBox.prop('checked', true)
  7. })
  8. // 2. 点击'全不选': 所有爱好都不勾选
  9. $('#checkedNoBtn').click(function () {
  10. // console.log('不选')
  11. $items.prop('checked', false)
  12. $checkedAllBox.prop('checked', false)
  13. })
  14. // 3. 点击'反选': 改变所有爱好的勾选状态
  15. $('#checkedRevBtn').click(function () {
  16. $items.each(function () {
  17. // dom对象的 checked属性
  18. this.checked = !this.checked
  19. })
  20. // 如果所有的items都选中,那就需要更新状态
  21. // 1,拿到里面没有选中的数量
  22. console.log($items.filter(':not(:checked)').length === 0)
  23. $checkedAllBox.prop('checked', $items.filter(':not(:checked)').length === 0)
  24. })
  25. // 4. 点击'全选/全不选': 选中所有爱好, 或者全不选中
  26. $('#checkedAllBox').click(function () {
  27. // console.log('点击')
  28. // console.log(this.checked)
  29. // dom的checkbox就是有一个checked的属性现在需要做的就是统一这个全选框和每一个爱好的选择情况
  30. $items.prop('checked', this.checked)
  31. })
  32. // 5. 点击某个爱好时, 必要时更新'全选/全不选'的选中状态
  33. // jQuery有一个隐式遍历,所以添加click事件是隐式遍历一个全部添加
  34. $items.click(function() {
  35. // 根据每一个item的点击状态,修改全选框的状态
  36. $checkedAllBox.prop('checked', $items.filter(':not(:checked)').length === 0)
  37. })
  38. // 6. 点击'提交': 提示所有勾选的爱好
  39. // 提交被选中的value
  40. $('#sendBtn').click(function() {
  41. // 需要遍历每一个选中的选项,然后alter
  42. $items.filter(':checked').each(function() {
  43. alert(this.value)
  44. })
  45. })

● 注意:
● 1,jQuery的对象和DOM的对象不一样,需要注意
● 2,jQuery的隐式遍历和需要给每一个对象遍历是不一样的,根据需求来做
● 3,设置属性的值,可以使用prop或者attr,prop是设置值为布尔值的属性
● 4,对于需要元素的筛选,可以使用filter,如果查找可以使用find

jQuery3 - 练习 - 文档的增删改查

需求:
image.png
功能说明:
1. 点击’Submit’, 根据输入的信息在表单中生成一行员工信息
2. 点击Delete链接, 提示删除当前行信息, 点击确定后删除信息
技术要点:
1. DOM查询
2. 绑定事件监听
3. DOM增删改
4. 取消事件的默认行为

  1. <table id="employeeTable">
  2. <tr>
  3. <th>name</th>
  4. <th>email</th>
  5. <th>salary</th>
  6. <th>&nbsp;</th>
  7. </tr>
  8. <tr>
  9. <td>Tom</td>
  10. <td>tom@qq.com</td>
  11. <td>5000</td>
  12. <td><a href='dddd'>删除</a></td>
  13. </tr>
  14. <tr>
  15. <td>jerry</td>
  16. <td>jerry@qq.com</td>
  17. <td>6000</td>
  18. <td><a href='dddd'>删除</a></td>
  19. </tr>
  20. <tr>
  21. <td>bob</td>
  22. <td>bob@qq.com</td>
  23. <td>7000</td>
  24. <td><a href='dddd'>删除</a></td>
  25. </tr>
  26. </table>
  27. <div id="formDiv">
  28. <h4>添加新员工</h4>
  29. <table>
  30. <tr>
  31. <td class="word">name:</td>
  32. <td class="inp">
  33. <input type="text" name="empName" id="empName">
  34. </td>
  35. </tr>
  36. <tr>
  37. <td class="word">email:</td>
  38. <td class="inp">
  39. <input type="text" name="email" id="email">
  40. </td>
  41. </tr>
  42. <tr>
  43. <td class="word">salary:</td>
  44. <td class="inp">
  45. <input type="text" name="salary" id="salary">
  46. </td>
  47. </tr>
  48. <tr>
  49. <td colspan="2" align="center">
  50. <button id="addEmpButton" value="abc">提交</button>
  51. </td>
  52. </tr>
  53. </table>
  54. </div>
  1. // 1. 点击'Submit', 根据输入的信息在表单中生成一行员工信息
  2. var $empName = $('#empName')
  3. var $email = $('#email')
  4. var $salary = $('#salary')
  5. console.log($empName)
  6. $('#addEmpButton').click(function () {
  7. // console.log('111', empName, email, salary)
  8. // 1,收集数据
  9. var empName = $empName.val()
  10. var email = $email.val()
  11. var salary = $salary.val()
  12. // 2,添加到页面
  13. // 2.1 ) 创建一个空的
  14. /*
  15. <tr>
  16. <td>jerry</td>
  17. <td>jerry@qq.com</td>
  18. <td>6000</td>
  19. <td><a href='dddd'>删除</a></td>
  20. </tr>
  21. */
  22. $('<tr></tr>')
  23. .append('<td>' + empName + '</td>')
  24. .append('<td>' + email + '</td>')
  25. .append('<td>' + salary + '</td>')
  26. .append('<td><a href="ddd">删除</a></td>')
  27. .appendTo('#employeeTable')
  28. // 给新添加的a标签添加事件
  29. .find('a')
  30. .click(deleteData)
  31. // 3,清空表格
  32. empName = $empName.val('')
  33. email = $email.val('')
  34. salary = $salary.val('')
  35. })
  36. // 2. 点击Delete链接, 提示删除当前行信息, 点击确定后删除信息
  37. $('a').click(deleteData)
  38. // 出现的问题:新增加的a标签还是会跳转,同时没有点击事件
  39. // 所以需要给a标签添加事件
  40. function deleteData() {
  41. // 阻止a的跳转,阻止默认行为
  42. event.preventDefault()
  43. // 找到当前点击的行
  44. // 可以使用$()包裹一个dom元素转换为jQuery对象,来使用jQuery的方法
  45. var $tr = $(this).parent().parent()
  46. var name = $tr.children('td:first').html()
  47. if (confirm('确定删除' + 'name' + '么?')) {
  48. $tr.remove()
  49. }
  50. }
  • 总结点:
  • 1,无法打印jQuery的值,是空,所以需要区分jQuery对象和dom对象,

必要的时候可以使用jQuery包裹DOM对象,来转换DOM对象,使其拥有jQuery对象的方法

  1. // 可以使用$()包裹一个dom元素转换为jQuery对象,来使用jQuery的方法
  2. var $tr = $(this).parent().parent()
  • 2,对于一个jQuery对象可以使用链式的方法,不断的给这个对象增加元素和给新的元素新增方法

    1. $('<tr></tr>')
    2. .append('<td>' + empName + '</td>')
    3. .append('<td>' + email + '</td>')
    4. .append('<td>' + salary + '</td>')
    5. .append('<td><a href="ddd">删除</a></td>')
    6. .appendTo('#employeeTable')
    7. // 给新添加的a标签添加事件
    8. .find('a')
    9. .click(deleteData)
  • 3,如果一个函数需要多次调用,可以单独定义一个函数,然后把这个函数传入需要调用的click事件中,这个时候不能给函数添加括号,添加括号表示函数的调用,不添加就表示传入的函数参数

    1. // 2. 点击Delete链接, 提示删除当前行信息, 点击确定后删除信息
    2. $('a').click(deleteData)
    3. function deleteData() {
    4. event.preventDefault()
    5. // 找到当前点击的行
    6. // 可以使用$()包裹一个dom元素转换为jQuery对象,来使用jQuery的方法
    7. var $tr = $(this).parent().parent()
    8. var name = $tr.children('td:first').html()
    9. if (confirm('确定删除' + 'name' + '么?')) {
    10. $tr.remove()
    11. }
    12. }
  • 4,在jQuery中,阻止元素的默认行为的方法和原生的js一样

    1. // 阻止a的跳转,阻止默认行为
    2. event.preventDefault()
  • 5,要获取点击的当前元素,可以使用this关键字

    1. // 找到当前点击的行
    2. var $tr = $(this).parent().parent()

jQuery4 - 练习 - 轮播图

实现功能:
点击上下可以平滑的移动

实现思路:
1,先完成简单功能,瞬间翻页,
1.1 拿到当前的list的left的值,
1.2 判断是上翻还是下翻
1.3 修改left的值,加上偏移量
2,设置定时器,一段距离一段距离的翻页,当达到目标距离的时候,就清除定时器
2.1 找到总的偏移量
2.2 通过总时间和间隔时间,拿到每一个单位距离
2.3 设置定时器,每次给当前的left的坐标添加一个单位距离,然后给list更新left的值
2.4 计算总的偏移量的目标坐标
2.5 当移动的坐标 === 目标坐标时,就清除定时器,不再翻页

代码

  1. <body>
  2. <div id="container">
  3. <div id="list">
  4. <img src="./img/1.jpg" alt="1">
  5. <img src="./img/2.jpg" alt="2">
  6. <img src="./img/3.jpg" alt="3">
  7. <img src="./img/4.jpg" alt="4">
  8. <img src="./img/5.jpg" alt="5">
  9. </div>
  10. <div id="printsDiv">
  11. <span index='1' class="on"></span>
  12. <span index='2'></span>
  13. <span index='3'></span>
  14. <span index='4'></span>
  15. <span index='5'></span>
  16. </div>
  17. <a href="javascript" id="prev" class="arrow">&lt;</a>
  18. <a href="javascript" id="next" class="arrow">&gt;</a>
  19. </div>
  20. <script src="./app.js"></script>
  21. </body>
  1. $(function () {
  2. /*
  3. // 1.1 找到下一页的图标,添加点击事件
  4. $('#next').click(function () {
  5. // 阻止a的默认行为
  6. event.preventDefault()
  7. // console.log('点击')
  8. // 1.2 每次点击的时候,就修改list的left的值
  9. $('#list').css('left', -600)
  10. })
  11. */
  12. // 1,定义需要的变量
  13. var $container = $('#container')
  14. var $list = $('#list')
  15. var $points = $('#printsDiv')
  16. var $prev = $('#prev')
  17. var $next = $('#next')
  18. // 定义图片长度,也就是偏移长度
  19. var page_width = 600
  20. var Time = 400
  21. var Item_Time = 20
  22. // 2,定义上下翻页
  23. $next.click(function () {
  24. // 阻止a标签的默认行为
  25. event.preventDefault()
  26. // 执行翻页函数,下一页
  27. nextPage(true)
  28. })
  29. $prev.click(function () {
  30. // 阻止a标签的默认行为
  31. event.preventDefault()
  32. // 执行翻页函数,上一页
  33. nextPage(false)
  34. })
  35. // 3,定义翻页函数
  36. function nextPage(next) {
  37. // console.log('----', currentLeft)
  38. /* 瞬间翻页的效果
  39. // 定义总的偏移量
  40. // 简单的条件判断可以使用三元表达式
  41. var offset = next ? -page_width : page_width
  42. // 设置新的left值,当前的left 加上 偏移的长度
  43. $list.css('left', currentLeft + offset)
  44. */
  45. // 4,实现平滑的翻页
  46. /*
  47. 1)总的偏移量 offset
  48. 2)总的时间 Time = 400 ??为什么要定义总的时间
  49. 3)移动的时间间隔:Item_Time = 20 ??
  50. 4)单位的偏移量:
  51. offset/(Time/Item_Time)
  52. ??直接定义一个间隔数,直接拿到间隔的时间和距离,不用定义总时间,间隔时间,再拿到间隔次数
  53. */
  54. // 4.1 定义总的偏移量
  55. var offset = 0
  56. offset = next ? -page_width : page_width
  57. // 4.2 定义总的时间
  58. // 4.3 计算单元移动的距离,然后不断的增加这个距离
  59. var ItemOffset = offset / (Time / Item_Time)
  60. // 拿到当前list的left值,根据参数判断是左翻还是右翻
  61. var currentLeft = $list.position().left
  62. // 4.4 设置定时器,每次增加单元的距离
  63. // 目标left距离
  64. var targetLeft = currentLeft + offset
  65. var intervalId = setInterval(function () {
  66. // 每次需要修改的left的值,要不断的变化
  67. currentLeft += ItemOffset
  68. // 4.5 在翻页left达到目标距离,就清除定时器
  69. if (currentLeft === targetLeft) {
  70. clearInterval(intervalId)
  71. }
  72. // 设置新的left值,当前的left 加上 偏移的长度
  73. $list.css('left', currentLeft)
  74. }, Item_Time)
  75. }
  76. })

总结:
1,首先可以考虑好实现的方案,一步一步的思考,不用太着急 —— 不要着急写
2,先分析需要调用的函数,再去定义函数 —— 这样可以把函数执行的函数抽离出来,简化逻辑
3,简单的条件判断可以直接简写为三元表达式 —— 简化代码
4,在过程中需要的变量可以集中定义在代码顶部 —— 方便代码解读
5,当实现功能的逻辑复杂的时候,可以一步一步的去做,一个变量一个变量的定义 —— 理清思路,慢慢来

jQuery5 - 源码解析 - 整体结构

整体架构

jQ的两大特色是
1,jQuery对象的构建方式:$(‘#id’)
2 ,jQueryl方法的调用方式$(‘#id’).css().html.hide()

1,实现jQ对象

1.1)一般都是使用js的类去构建,为了不使用new去创建实例,所以需要这个类返回一个实例

  1. var aQuery = function(selector, context) {
  2. return new aQuery();
  3. }
  4. aQuery.prototype = {
  5. name:function(){},
  6. age:function(){}
  7. }

问题:这样会构建的类,返回类的实例,就会造成陷入死循环

1.2)所以需要返回一个正确的实例

  1. var aQuery = function(selector, context) {
  2. return aQuery.prototype.init();
  3. }
  4. aQuery.prototype = {
  5. init:function(){
  6. return this;
  7. }
  8. name:function(){},
  9. age:function(){}
  10. }
  11. // 这样返回的就是一个类的拥有的三个方法

代码审查:构建的类返回类的一个实例,这个实例返回它自己this
问题如下:

  1. var aQuery = function(selector, context) {
  2. return aQuery.prototype.init();
  3. }
  4. aQuery.prototype = {
  5. init: function() {
  6. this.age = 18
  7. return this;
  8. },
  9. name: function() {},
  10. age: 20
  11. }
  12. aQuery().age //18

实例的方法访问不到类自己的属性,只能拿到这个实例的属性

1.3)需要独立类的属性和方法的属性,所以可以返回一个新类去区分单独的作用域

  1. var aQuery = function(selector, context) {
  2. return new aQuery.prototype.init();
  3. }
  4. aQuery.prototype = {
  5. init: function() {
  6. this.age = 18
  7. return this;
  8. },
  9. name: function() {},
  10. age: 20
  11. }
  12. //Uncaught TypeError: Object [object Object] has no method 'name'
  13. console.log(aQuery().name())

代码审查:每次使用new一个新的init实例,就可以独立不同实例的作用域
出现的问题:由于返回的是init的this,就不能拿到类的其他的方法

1.4)正确的建立实例,同时拿到类的属性和方法
实现的关键点:看jQ的处理

  1. // Give the init function the jQuery prototype for later instantiation
  2. jQuery.fn.init.prototype = jQuery.fn;

所以 我们可以写成

  1. var aQuery = function(selector, context) {
  2. return new aQuery.prototype.init();
  3. }
  4. aQuery.prototype = {
  5. init: function() {
  6. return this;
  7. },
  8. name: function() {
  9. return this.age
  10. },
  11. age: 20
  12. }
  13. // 关键点
  14. aQuery.prototype.init.prototype = aQuery.prototype;
  15. console.log(aQuery().name()) //20

代码审查:
init是作为实例化这个类,所以里面不要放其他的属性
关键点的解释:(不是很明白)

2,链式调用

DOM的链式调用的特点:
1,可以节约js的代码
2,返回的都是同一个对象

2.1)要让所有的方法都指向一个DOM对象,可以使用扩展原型的方法,然后return this来实现

  1. var aQuery = function(selector, context) {
  2. return new aQuery.prototype.init();
  3. }
  4. aQuery.prototype = {
  5. init: function() {
  6. return this;
  7. },
  8. name: function() {
  9. return this
  10. },
  11. age: 20
  12. }
  13. // 关键点
  14. aQuery.prototype.init.prototype = aQuery.prototype;
  15. // 链式调用
  16. aQuery().init().name()
  17. // 分解
  18. a = aQuery();
  19. a.init()
  20. a.name()

代码审查:由于每个方法都是返回this,所以又会回到当前的实例,然后访问到自己的原型

这些链式的调用都是同步链式,因为js是无阻塞的语言,所以都是同步链式操作,js的异步是需要调用一些接口来实现的,比如setTimeout类似的

3,jQ的插件接口

目的:jq已经做好了属性和方法的封装,但是也需要给开发者提供对方法的扩展(就是提供一个安装插件的接口,来扩展jq的功能),从封装的角度来说,不是直接去修改prototye的友好用户接口,而是需要提供一个对于jq函数的扩展接口 extend,jq一般提供jQuery.fn.extend( ) 来对对象添加方法

以下是extend的实现:
说明:
jQuery.extend和jQuery.fn.extend其实是同指向同一方法的不同引用
jQuery.extend 对jQuery本身的属性和方法进行了扩展
jQuery.fn.extend 对jQuery.fn的属性和方法进行了扩展

但是源码使用的语句是:jQuery.extend = jQuery.fn.extend = function() { }

  1. jQuery.extend = jQuery.fn.extend = function() {
  2. var src, copyIsArray, copy, name, options, clone,
  3. target = arguments[0] || {}, // 常见用法 jQuery.extend( obj1, obj2 ),此时,target为arguments[0]
  4. i = 1,
  5. length = arguments.length,
  6. deep = false;
  7. // Handle a deep copy situation
  8. if ( typeof target === "boolean" ) { // 如果第一个参数为true,即 jQuery.extend( true, obj1, obj2 ); 的情况
  9. deep = target; // 此时target是true
  10. target = arguments[1] || {}; // target改为 obj1
  11. // skip the boolean and the target
  12. i = 2;
  13. }
  14. // Handle case when target is a string or something (possible in deep copy)
  15. if ( typeof target !== "object" && !jQuery.isFunction(target) ) { // 处理奇怪的情况,比如 jQuery.extend( 'hello' , {nick: 'casper})~~
  16. target = {};
  17. }
  18. // extend jQuery itself if only one argument is passed
  19. if ( length === i ) { // 处理这种情况 jQuery.extend(obj),或 jQuery.fn.extend( obj )
  20. target = this; // jQuery.extend时,this指的是jQuery;jQuery.fn.extend时,this指的是jQuery.fn
  21. --i;
  22. }
  23. for ( ; i < length; i++ ) {
  24. // Only deal with non-null/undefined values
  25. if ( (options = arguments[ i ]) != null ) { // 比如 jQuery.extend( obj1, obj2, obj3, ojb4 ),options则为 obj2、obj3...
  26. // Extend the base object
  27. for ( name in options ) {
  28. src = target[ name ];
  29. copy = options[ name ];
  30. // Prevent never-ending loop
  31. if ( target === copy ) { // 防止自引用,不赘述
  32. continue;
  33. }
  34. // Recurse if we're merging plain objects or arrays
  35. // 如果是深拷贝,且被拷贝的属性值本身是个对象
  36. if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
  37. if ( copyIsArray ) { // 被拷贝的属性值是个数组
  38. copyIsArray = false;
  39. clone = src && jQuery.isArray(src) ? src : [];
  40. } else {
  41. // 被拷贝的属性值是个plainObject,比如{ nick: 'casper' }
  42. clone = src && jQuery.isPlainObject(src) ? src : {};
  43. }
  44. // Never move original objects, clone them
  45. target[ name ] = jQuery.extend( deep, clone, copy ); // 递归~
  46. // Don't bring in undefined values
  47. } else if ( copy !== undefined ) { // 浅拷贝,且属性值不为undefined
  48. target[ name ] = copy;
  49. }
  50. }
  51. }
  52. }
  53. // Return the modified object
  54. return target;
  55. }

注意1:
jQuery中利用上面实现的扩展机制,添加了许多方法,其中
1)直接添加在构造函数上,被称为工具方法

  1. jQuery.extend({
  2. isFunction: function() {},
  3. type: function() {},
  4. parseHTML: function() {},
  5. parseJSON: function() {},
  6. ajax: function() {}
  7. // ...
  8. })
  1. 2)添加到原型上
  1. jQuery.fn.extend({
  2. queue: function() {},
  3. promise: function() {},
  4. attr: function() {},
  5. prop: function() {},
  6. addClass: function() {},
  7. removeClass: function() {},
  8. val: function() {},
  9. css: function() {}
  10. // ...
  11. })

当我们直接使用$(‘#test’)创建一个对象时,实际上是创建了一个init的实例,这里的真正构造函数是原型中的init方法
所以涉及到的使用jQ的一个点:

  1. // 在使用jQuery时常常会毫无节制使用$(),比如对于同一个元素的不同操作:
  2. var width = parseInt($('#test').css('width'));
  3. if(width > 20) {
  4. $('#test').css('backgroundColor', 'red');
  5. }

从对于jq内部源码了解以后,发现执行$() 就构建一个init的实例,所以对于同一个对象重新去做$()的获取,是非常消耗内存的,所以建议对于一个对象的操作,进行一次命名赋值。

注意2:
区别:静态方法,工具方法,实例方法

工具方法:是jq放在构造函数中的方法,就是添加在 jQuery.extend里面的方法,这里面的方法不需要声明一个实例对象就可以直接使用,又叫静态方法,工具方法在使用的时候因为不用创建新的实例,因此相对效率会高很多,但是不节省内容。

实例方法:是放在原型中的方法,就是添加在jQuery.fn.extend里面的方法,在使用的时候需要创建一个新的实例对象才能访问,所以称为实例方法,由于需要创建实例才能使用,所以使用的成本比工具的方法高,但是节约内存。

汇总代码

jq的框架代码

  1. (function(ROOT) {
  2. // 构造函数
  3. var jQuery = function(selector) {
  4. // 在jQuery中直接返回new过的实例,这里的init是jQuery的真正构造函数
  5. return new jQuery.fn.init(selector)
  6. }
  7. jQuery.fn = jQuery.prototype = {
  8. constructor: jQuery,
  9. version: '1.0.0',
  10. init: function(selector) {
  11. // 在jquery中这里有一个复杂的判断,但是这里我做了简化
  12. var elem, selector;
  13. elem = document.querySelector(selector);
  14. this[0] = elem;
  15. // 在jquery中返回一个由所有原型属性方法组成的数组,我们这里简化,直接返回this即可
  16. // return jQuery.makeArray(selector, this);
  17. return this;
  18. },
  19. // 在原型上添加一堆方法
  20. toArray: function() {},
  21. get: function() {},
  22. each: function() {},
  23. ready: function() {},
  24. first: function() {},
  25. slice: function() {}
  26. // ... ...
  27. }
  28. jQuery.fn.init.prototype = jQuery.fn;
  29. // 实现jQuery的两种扩展方式
  30. jQuery.extend = jQuery.fn.extend = function(options) {
  31. // 在jquery源码中会根据参数不同进行很多判断,我们这里就直接走一种方式,所以就不用判断了
  32. var target = this;
  33. var copy;
  34. for(name in options) {
  35. copy = options[name];
  36. target[name] = copy;
  37. }
  38. return target;
  39. }
  40. // jQuery中利用上面实现的扩展机制,添加了许多方法,其中
  41. // 直接添加在构造函数上,被称为工具方法
  42. jQuery.extend({
  43. isFunction: function() {},
  44. type: function() {},
  45. parseHTML: function() {},
  46. parseJSON: function() {},
  47. ajax: function() {}
  48. // ...
  49. })
  50. // 添加到原型上
  51. jQuery.fn.extend({
  52. queue: function() {},
  53. promise: function() {},
  54. attr: function() {},
  55. prop: function() {},
  56. addClass: function() {},
  57. removeClass: function() {},
  58. val: function() {},
  59. css: function() {}
  60. // ...
  61. })
  62. // $符号的由来,实际上它就是jQuery,一个简化的写法,在这里我们还可以替换成其他可用字符
  63. ROOT.jQuery = ROOT.$ = jQuery;
  64. })(window);
  65. // 写一个Drag插件例子:
  66. (function() {
  67. // 构造
  68. function Drag(selector) {}
  69. // 原型
  70. Drag.prototype = {
  71. constructor: Drag,
  72. init: function() {
  73. // 初始时需要做些什么事情
  74. this.setDrag();
  75. },
  76. // 稍作改造,仅用于获取当前元素的属性,类似于getName
  77. getStyle: function(property) {},
  78. // 用来获取当前元素的位置信息,注意与之前的不同之处
  79. getPosition: function() {},
  80. // 用来设置当前元素的位置
  81. setPostion: function(pos) {},
  82. // 该方法用来绑定事件
  83. setDrag: function() {}
  84. }
  85. // 一种对外暴露的方式
  86. window.Drag = Drag;
  87. })();
  88. // 通过扩展方法将拖拽扩展为jQuery的一个实例方法
  89. (function ($) {
  90. $.fn.extend({
  91. becomeDrag: function () {
  92. new Drag(this[0]);
  93. return this; // 注意:为了保证jQuery所有的方法都能够链式访问,每一个方法的最后都需要返回this,即返回jQuery实例
  94. }
  95. })
  96. })(jQuery);

代码审查:
1,构建的jq插件也需要让他可以有jq的特性,所以需要按照jq的框架去构建新的插件
2,插件传入是一个键值对的形式

参考连接:
jQuery 2.0.3 源码分析core - 整体架构
详细图解jQuery对象,以及如何扩展jQuery插件