1.不用委托事件删除动态新增元素的前提 12.22的待办事项案例

必须发布成功了 新增成功了节点元素 才会有X按钮 否则在外发布点击事件外面 永远拿不到
// 一定要先有li,才可以获取删除按钮:
// doucument.querySelector():整个页面查询元素
// 父元素.querySelector():父元素中查找元素
image.png

1.1委托事件测试

  1. <div>点我</div>
  2. <ul>
  3. <input type="text">
  4. </ul>
  5. <script>
  6. let input = document.querySelector('input')
  7. input.onkeydown = function (e) {
  8. if (e.key == 'Enter') {
  9. console.log(e.key);
  10. console.log(input.value);
  11. //创建空标签
  12. let li = document.createElement('li')
  13. // 3.2为空标签新增内容
  14. li.innerHTML = `
  15. <input type="checkbox" name="" id="">
  16. <span>${input.value}</span>
  17. <button class = "delete">x</button>
  18. `
  19. // 3.添加到DOM树 父元素.appendChild(子元素)
  20. document.querySelector('ul').appendChild(li)
  21. }
  22. }
  23. // 委托事件 处理动态新增元素、节点
  24. document.querySelector('ul').onclick = function (e) {
  25. if (e.target.classList.contains('delete')) {
  26. this.removeChild(e.target.parentNode)
  27. }
  28. }
  29. document.querySelector('div').onclick = function () {
  30. this.style.backgroundColor = 'pink'
  31. this.style.textDecoration = "line-through"
  32. }

1.2删除对应勾选框测试:

image.png

  1. <ul>
  2. <li class="dis"><input type="checkbox" name="num" id="" checked>0</li>
  3. <li><input type="checkbox" name="num" id="">1</li>
  4. <li><input type="checkbox" name="num" id="">2</li>
  5. <li><input type="checkbox" name="num" id="">3</li>
  6. <li><input type="checkbox" name="num" id="">4</li>
  7. </ul>
  8. <button>删除</button>
  9. <script>
  10. let button = document.querySelector('button')
  11. let inputList = document.querySelectorAll('input')
  12. // 1.点击按钮
  13. button.onclick = function () {
  14. //遍历每个选择框
  15. for (let i = 0; i < inputList.length; i++) {
  16. console.log(inputList[i].checked);
  17. // 如果选择框被勾选了
  18. if (inputList[i].checked) {
  19. // 就删除 这个选择框
  20. let liList = document.querySelectorAll('li')
  21. liList[i].classList.add('dis')
  22. }
  23. }
  24. }
  25. </script>

12.22随机点名思路:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>Document</title>
  8. <style>
  9. * {
  10. margin: 0;
  11. padding: 0;
  12. }
  13. body {
  14. /* background-image: linear-gradient(rgb(255, 24, 24), rgb(37, 37, 255)); */
  15. background-attachment: fixed;
  16. transition: all 10s !important;
  17. }
  18. h1 {
  19. text-align: center;
  20. background-color: chartreuse;
  21. transition: all 1s !important;
  22. }
  23. .sn {
  24. margin: 10px;
  25. color: #c07ac8;
  26. }
  27. .btn {
  28. background-color: #9acc32;
  29. color: #fff;
  30. width: 50px;
  31. height: 50px;
  32. margin: 10px;
  33. }
  34. select {
  35. width: 80px;
  36. height: 26px;
  37. background-color: #feebcd;
  38. }
  39. .uname ul li {
  40. float: left;
  41. list-style: none;
  42. background-color: #000;
  43. font-size: 14px;
  44. color: #fff;
  45. width: 70px;
  46. height: 35px;
  47. margin: 3px;
  48. line-height: 35px;
  49. text-align: center;
  50. }
  51. .uname ul li.active {
  52. background-color: #acfe2f;
  53. color: #c07ac8;
  54. }
  55. .sn span {
  56. margin-left: 10px;
  57. }
  58. </style>
  59. </head>
  60. <body>
  61. <!-- ### 2:数据的基本添加和渲染
  62. - 目标
  63. - 能够收集页面元素的数据,并生成自己需要的数据
  64. - 理解页面操作与数据之前的关联,理解页面操作本质上就是数据操作,这对后期及至项目的学习都有重大的意义
  65. - 掌握页面元素数据的收集
  66. - 能够理解封装的需要,实现功能的封装
  67. - 效果:
  68. - 需要掌握的技能
  69. - 根据dom元素数据生成需要的数据对象
  70. - 数组操作api的使用
  71. - 添加数据之后的页面刷新--函数封装 -->
  72. <h1>点名系统</h1>
  73. <h2 class="sn">被选中的小伙伴: <span>彭雅娴</span></h2>
  74. <button class="btn">开始</button>
  75. 时间:
  76. <select name="" id="" class="timer">
  77. <option value="10">10</option>
  78. <option value="9">9</option>
  79. <option value="8">8</option>
  80. <option value="7">7</option>
  81. <option value="6">6</option>
  82. <option value="5">5</option>
  83. <option value="4">4</option>
  84. <option value="3">3</option>
  85. <option value="2">2</option>
  86. <option value="1">1</option>
  87. </select>
  88. <div class="uname">
  89. <ul>
  90. <li class="active">吕毕丹</li>
  91. <!-- <li>孙庆材</li> -->
  92. </ul>
  93. </div>
  94. <script>
  95. let unameArr = [
  96. '梦洁',
  97. '雅静',
  98. '韵寒',
  99. '莉姿',
  100. '沛玲',
  101. '欣妍',
  102. '歆瑶',
  103. '凌菲',
  104. '靖瑶',
  105. '阮秋帛',
  106. '赵同原',
  107. '邹月蕴',
  108. '马文松',
  109. '熊可渊',
  110. '唐飘遥',
  111. '严 华',
  112. '叶 霞',
  113. '韵寒',
  114. '莉姿',
  115. '沛玲',
  116. '欣妍',
  117. '歆瑶',
  118. '凌菲',
  119. '靖瑶',
  120. '阮秋帛',
  121. '赵同原',
  122. '邹月蕴',
  123. '马文松',
  124. '熊可渊',
  125. '唐飘遥',
  126. '严 华',
  127. '叶 霞',
  128. '韵寒',
  129. '莉姿',
  130. '沛玲',
  131. '欣妍',
  132. '歆瑶',
  133. '凌菲',
  134. '靖瑶',
  135. '阮秋帛',
  136. '赵同原',
  137. '邹月蕴',
  138. '马文松',
  139. '熊可渊',
  140. '唐飘遥',
  141. '严 华',
  142. '叶 霞',
  143. '封 英',
  144. '方顺清',
  145. '何 芹',
  146. '叶 霞',
  147. '封 英',
  148. '崔致敏',
  149. '邹月蕴',
  150. '马文松',
  151. '夏 良',
  152. '崔致敏',
  153. '封腾麟',
  154. '马文松',
  155. '熊可渊',
  156. '唐飘遥',
  157. ]
  158. // 新增节点
  159. // 循环得到每个li
  160. for (let i = 0; i < unameArr.length; i++) {
  161. // 1.创建空标签
  162. let li = document.createElement('li')
  163. // 2.将后台数据渲染到页面
  164. li.innerText = unameArr[i]
  165. // 3.添加到DOM树
  166. document.querySelector('.uname ul').appendChild(li)
  167. }
  168. // 1.获取元素
  169. let btn = document.querySelector('.btn')//按钮
  170. let liList = document.querySelectorAll('.uname li')
  171. // 2.事件注册 点击
  172. btn.onclick = function () {
  173. let timeID = setInterval(function () {
  174. // 2.1事件处理 姓名随机li
  175. // 2.2 得到随机下标
  176. let rondomIndex = Math.floor(Math.random() * (unameArr.length)) //含最大值,含最小值
  177. console.log(unameArr[rondomIndex]);
  178. // 得到每个li 这个循环好像可以省略
  179. for (let i = 0; i < liList.length; i++) {
  180. //排他思想 狙击排他思想 找到含有active类名的兄弟 移除他 不移除就可以依次显示在页面
  181. document.querySelector('.uname li.active').classList.remove('active')
  182. // 通过随机下标 为自己添加样式
  183. liList[rondomIndex].classList.add('active')
  184. }
  185. }, 200)
  186. // 3.开启定时器
  187. //3.1定义 自定义倒计时 取得时间框的对应下拉菜单value时间
  188. let timer = document.querySelector('.timer').value
  189. let timeID2 = setInterval(function () {
  190. // 3.2 对应的下拉菜单时间time--
  191. timer--
  192. console.log(timer);
  193. if (timer <= 0) {
  194. // 清除随机点名倒计时
  195. clearInterval(timeID)
  196. // 清除自身定时器
  197. clearInterval(timeID2)
  198. // 将被点到的人 显示在页面
  199. // document.querySelector('.sn span').innerText += document.querySelector('.uname li.active').innerText
  200. let span = document.createElement('span')
  201. span.innerText = document.querySelector('.uname li.active').innerText
  202. document.querySelector('.sn').appendChild(span)
  203. }
  204. }, 1000)
  205. }
  206. //3.背景颜色变化 随机颜色
  207. let timeID = setInterval(function () {
  208. let h1 = document.querySelector('h1')
  209. let randomColor1 = Math.floor(Math.random() * (255 - 0 + 1) + 0)
  210. let randomColor2 = Math.floor(Math.random() * (255 - 0 + 1) + 0)
  211. let randomColor3 = Math.floor(Math.random() * (255 - 0 + 1) + 0)
  212. // console.log("rgb" + "(" + randomColor + "," + randomColor1 + "," + randomColor2 + ")");
  213. h1.style.color = "rgb" + "(" + randomColor1 + "," + randomColor2 + "," + randomColor3 + ")"
  214. // document.body.style.backgroundImage = ""
  215. // console.log("linear - gradient" + "(" + "rgb" + "(" + randomColor + "," + randomColor1 + "," + randomColor2 + ")" + "," + "rgb" + "(" + randomColor3 + "," + randomColor2 + "," + randomColor1 + ")" + ")")
  216. }, 1000)
  217. // let bg = setInterval(function () {
  218. // let h1 = document.querySelector('h1')
  219. // let randomColor1 = Math.floor(Math.random() * (255 - 0 + 1) + 0)
  220. // let randomColor2 = Math.floor(Math.random() * (255 - 0 + 1) + 0)
  221. // let randomColor3 = Math.floor(Math.random() * (255 - 0 + 1) + 0)
  222. // document.body.style.backgroundImage = "linear-gradient" + "(" + "rgb" + "(" + randomColor1 + "," + randomColor2 + "," + randomColor3 + ")" + "," + "rgb" + "(" + randomColor3 + "," + randomColor2 + "," + randomColor1 + ")" + ")"
  223. // }, 1000)
  224. </script>
  225. </body>
  226. </html>

2.开关思想:

开关思想 : 用于解决一类问题的思路
判断数组中是否所有元素都满足条件
1.选择框是否全部勾选
2.表单是否全部填写
3.正则是否全部通过

let flag = true 全部成立
if(条件不满足){ //只要有一个条件不成立
flag =false //条件就不成立 后面就没有必要检查
break
}
全部条件 =flag
image.png
image.png

image.png
JS高级开关

3.localStorage: 本地存储 将数据存储到浏览器 (内存存储)(永久存储)

  1. 2.语法: <br /> 存: localStorage.setItem('属性名', '属性值')<br /> 取:let 变量名 = localStorage.getItem('属性名')<br /> 删: localStorage.removeItem('属性名')<br /> 清空: localStorage.clear()<br /> <br /> 3.注意点<br /> 3.0 存储没有返回值 读取需要返回值<br /> 3.1 存储的数据只能是字符串类型。如果是其他数据类型则会自动调用toString()方法转成字符串<br /> 3.2 永久存储。除非自己删,否则一直存在于浏览器
  1. */
  2. document.querySelector('.btn1').onclick = function () {
  3. localStorage.setItem('uname', '张三')//硬盘存储
  4. localStorage.setItem('password', '123456')
  5. localStorage.setItem('age', 18)
  6. localStorage.setItem('nickname', ['测试', '测试'])//存储的数据只能是字符串类型。如果是其他数据类型则会自动调用toString()方法转成字符串
  7. }
  8. 取数据:
  9. let 变量名= localStorage.getItem('age')//存储没有返回值 读取需要返回值
  10. console.log(变量名)
  11. sessionStorage.setItem('uname', '张三') // 临时存储---内存存储
  1. /4.将用户名和密码存入 localStorage
  2. let obj = {
  3. username : username.value,
  4. password : password.value
  5. }
  6. /* localStorage只能存储字符串类型,如果是对象需要转成JSON字符串 */
  7. localStorage.setItem('user', JSON.stringify(obj) )

4.正则判断

image.png

5.面向对象:

  • 面向对象不是一门技术,而是一种解决问题的思维方式
  • 面向对象的本质是对面向过程的一种封装

面向对象:注重的是结果
面向过程:注重的是过程

6.数组对象api常用:

image.png

7.字符串常用api

  1. <script>
  2. let str = '风止意难平'
  3. //1.字符串也可以像数组一样 通过下标获取某个字符串
  4. console.log(str[1]);//止
  5. //2.indexOf() 返回某个指定首字符串的位置下标
  6. //应用:检查一个字符串中是否有某个字符
  7. //有:则返回下标 没有:返回固定值 -1
  8. let a = str.indexOf('难平')
  9. console.log(a);//3 //有:则返回下标位置
  10. let b = str.indexOf('嗯')
  11. console.log(b) //-1 //没有:返回固定值 -1
  12. // 3.split('分隔符') :切割字符串
  13. //返回值一定是数组: 元素对应切割的每一个部分
  14. let url = 'https://www.yuque.com?name="张三",age="18"'
  15. let c = url.split('?')
  16. console.log(c);// ['https://www.yuque.com', 'name="张三",age="18"']
  17. d = c[1].split(',')
  18. console.log(d);//['name="张三"', 'age="18"']
  19. let f = d.join() //把数组元素拼接成一个字符串
  20. console.log(f); //name = "张三", age = "18"
  21. //4.substr(起始下标,截取长度) :截取部分字符串
  22. let str2 = "好好学习,天天向上"
  23. let l = str2.substr(5, 4)
  24. console.log(l)//天天向上
  25. // 5.replace("要替换的内容","替换成的文字")
  26. let str3 = "好好学习,天天向上"
  27. let m = str3.replace("好好学习,天天向上", "键盘敲烂,月薪过万")
  28. console.log(m)//键盘敲烂,月薪过万
  29. </script>

8.了解工厂函数

  1. <script>
  2. // 解决代码冗余方案: 封装函数
  3. // 什么是工厂函数 : 创建多个对象的函数
  4. function creatPerson(name, age, sex) {
  5. // 1.声明对象
  6. let p = {
  7. // 2.属性名:属性值
  8. namer: name,
  9. ager: age
  10. }
  11. //2.对象.属性名 = 属性值
  12. p.sex = sex
  13. // 3. 返回值
  14. return p
  15. }
  16. let zxy = creatPerson('张学友', 18, '男')
  17. let ldh = creatPerson('刘德华', 20, "男")
  18. console.log(zxy);
  19. console.log(ldh);
  20. </script>

9.(重点)构造函数

构造函数:作用于工厂函数一致,但是代码更少
构造函数:使用new来调用的函数
new 工作原理:
1.创建一个空对象{}
2.this指向这个对象 this = {}
3.对象赋值 this.属性名 = 属性值
4.返回这个对象
new 细节:
(1)构造函数首字母大写:为了提醒别人不要忘记new关键字
(2)构造函数里主动写了return:
如果是值类型:无效 继续返回new创建的对象
引用类型:有效 会覆盖new创建的对象

  1. function Person(name, age) {
  2. // 1.创建一个空对象{}
  3. // 2.this指向这个对象 this.属性名 = {}
  4. // 3.对象赋值
  5. this.name = name
  6. this.age = age
  7. //4.返回这个对象
  8. }
  9. let zs = new Person('张三', 18)
  10. let l4 = new Person('李四', 19)
  11. console.log(zs, l4);

10.(重点)原型对象

  • 1.原型对象:

    1. - 创建函数的时候,系统会自动帮我们创建一个与之对应的对象,称为原型对象
  • 2.原型对象作用:

    1. - **解决内存浪费+ 变量污染**
  • 3.构造函数 原型对象 实例对象的关系

    • 3.1 prototype:属于构造函数,指向原型对象

      1. 作用:解决内存浪费+变量污染
    • 3.2 proto:属于实例对象,指向原型对象

      1. 作用:实例对象 访问原型对象的成员
    • 3.3 constructor:属于原型对象,指向构造函数

      1. 作用让实例对象知道自己是谁创建的

先有构造函数 再有原型对象 再有实例对象
image.png
image.png

11.面向对象的三大特征

封装:

  • 将某个具体功能封装在对象中,只对外部暴露指定的接口,外界在使用的时候,只需考虑怎么用,不需要考虑内部怎么实现

    (重点)继承:

  • 一个对象拥有其他对象的属性和方法/行为

    多态:

  • 一个对象在不同情况下的多种状态

1.混入式继承

遍历 父对象 的所有属性值,添加给 子对象
特点:每继承一次,就要执行一次循环
应用场景:单个对象继承
image.png

  1. //继承:让一个对象拥有另一个对象的属性和方法
  2. let father = {
  3. house: {
  4. address: '北京一环',
  5. price: 100000000
  6. },
  7. car: {
  8. brand: '劳斯莱斯',
  9. price: 5000000
  10. }
  11. }
  12. let son = {
  13. name: 'ikun',
  14. age: 30
  15. }
  16. //1.混入式
  17. //解决方案:遍历父对象的所有属性值,添加给子对象
  18. //特点:每继承一次,就要执行一次循环
  19. //应用场景 : 单个对象继承
  20. for (let key in father) {
  21. son[key] = father[key]
  22. }
  23. console.log(son)

2.替换原型:

将父对象作为子对象构造函数的原型
特点:会丢失原型之前的成员变量
应用场景:多个对象继承
image.png

  1. let father = {
  2. house: {
  3. address: '北京一环',
  4. price: 100000000
  5. },
  6. car: {
  7. brand: '劳斯莱斯',
  8. price: 5000000
  9. }
  10. }
  11. function Son(name, age) {
  12. this.name = name
  13. this.age = age
  14. }
  15. Son.prototype.sayHi = function () {
  16. console.log('你好')
  17. }
  18. //让父对象成为子对象构造函数的原型
  19. Son.prototype = father
  20. console.log(Son.prototype);
  21. //实例化对象
  22. let son1 = new Son('ikun', 30)
  23. console.log(son1)
  24. let son2 = new Son('班长', 20)
  25. console.log(son2)

3. 混合式继承

混入式 + 替换原型
特点 :遍历 父对象 所有的属性值,添加给 子对象构造函数 的原型
应用场景:多个对象继承,但是不会覆盖原来的默认原型
image.png

  1. let father = {
  2. house:{
  3. address:'北京一环',
  4. price:100000000
  5. },
  6. car:{
  7. brand:'劳斯莱斯',
  8. price:5000000
  9. }
  10. }
  11. //1.构造函数
  12. function Son (name,age ) {
  13. this.name = name
  14. this.age = age
  15. }
  16. //2.默认原型对象
  17. Son.prototype.sayHi = function(){
  18. console.log('你好')
  19. }
  20. //4.(混合式继承)将父对象的所有属性添加到子对象构造函数的原型中
  21. for(let key in father){
  22. Son.prototype[key] = father[key];
  23. }
  24. //3.实例化对象
  25. let son1 = new Son('ikun',30)
  26. console.log ( son1 )
  27. let son2 = new Son('班长',20)
  28. console.log ( son2 )

12.(重点)原型链(框架底层基础)

1.原型链:

每一个对象,都有proto指向自身的原型。 而原型也是对象,也有自己的
proto指向原型的原型,以此类推形成链式结构,称之为原型链。

2.对象访问原型链中成员的规则 :就近原则

当访问对象的成员,先看自己有没有。有则访问,没有则看原型有没有。原型有则访问,
没有则访问原型的原型,没有则继续往上找。以此类推,一直找到原型链终点 :null .
如果还找不到, 如果是属性 : 则获取undefined 如果是方法 ,则报错xxx is not defined .
原型对象里的成员 实例对象可以直接使用 因为实例对象里面有proto属性指向原型对象

3.原型链作用 : 继承

js语言 通过 原型链 实现面向对象继承

只有对象才有原型,这里一定要把基本数据类型string、number、boolean,和基本包装类型(特殊的引用类型对象)String、Number、Boolean区分开来,不要搞混淆
image.png

  1. <script>
  2. /*
  3. 1.原型链 :每一个对象,都有__proto__指向自身的原型。 而原型也是对象,也有自己的
  4. __proto__指向原型的原型,以此类推形成链式结构,称之为原型链。
  5. 2.对象访问原型链中成员的规则 :就近原则
  6. 当访问对象的成员,先看自己有没有。有则访问,没有则看原型有没有。原型有则访问,
  7. 没有则访问原型的原型,没有则继续往上找。以此类推,一直找到原型链终点 : null.
  8. 还没有, 如果是属性 : 则获取undefined 如果是方法 ,则报错xxx is not defined
  9. */
  10. //1.构造函数
  11. function Person(name,age){
  12. this.name = name;
  13. this.age = age;
  14. };
  15. //2.原型对象
  16. Person.prototype.sayHi = function(){
  17. console.log('人生若只如初见,何事秋风悲画扇');
  18. };
  19. Person.prototype.type = '哺乳动物';
  20. //3.实例化对象
  21. let p1 = new Person('又又',18);
  22. console.log(p1);
  23. //请说出以下代码执行结果
  24. console.log(p1.name);//又又 p1自己有name属性
  25. console.log(p1.type);//哺乳动物 p1自己没有type,但是p1的原型有
  26. console.log(p1.hobby);//undefined p1自己没有hobby,原型也没有
  27. p1.sayHi();// 人生若只如初见,何事秋风悲画扇 p1自己没有这个方法,原型有
  28. // p1.eat();//报错 xxx is not defined p1自己没有这个方法,原型也没有
  29. //为什么不报错? p1自己没有这个方法,原型也没有这个方法,但是原型的原型有
  30. p1.toString();
  31. //查看p1的原型链
  32. console.log(p1.__proto__.constructor);//Person
  33. console.log(p1.__proto__ === Person.prototype);//true
  34. //查看p1的原型的原型
  35. console.log(p1.__proto__.__proto__.constructor);//Object
  36. console.log(p1.__proto__.__proto__ === Object.prototype);//true
  37. //查看p1的原型的原型的原型
  38. console.log(p1.__proto__.__proto__.__proto__);//null
  39. </script>

image.png

13.instanceof 运算符

instanceof运算符作用: 限制函数参数数据类型

例如: …… not of type …… 报错 函数参数数据类型错误
用于检测构造函数的prototype属性 是否出现在某个实例对象的原型链上

instanceof语法: 实例对象 instanceof 构造函数

  1. let arr = [10,20,30]
  2. //数组原型链 arr->Arr.prototype->Object.prototype->null
  3. console.log ( arr instanceof Array )//true
  4. console.log ( arr instanceof Object )//true
  5. //报错: 参数不是节点
  6. //instanceof关键字一般用在函数中,用于限制参数的数据类型
  7. //例如 appendChild()参数只能是node节点,如果你传了其他类型就会报错。底层就会用到instanceof来检测参数数据类型
  8. let h1 = document.createElement('h1')
  9. h1.innerText = '我是h1标签'
  10. document.body.appendChild(h1)

14.ES6类与继承

1.class关键字 :

作用: 声明一个类函数
相当于ES5的构造函数,只是代码的阅读性大大提高
1. 把构造函数和原型全部写在一个大括号里面,提高代码阅读性
2. 必须要使用new才可以调用class函数,否则会报错 提高代码规范
*class关键字语法:

class 构造函数名{
constructor(){
//(1)这里写构造函数代码
};
//(2)原型里面的方法写在下面
eat(){
};
play(){
};
};

class本质其实就是构造函数的另一种写法,本质还是给prototype添加成员
使用了class,一样可以继续使用ES5的prototype

左右两边的结果完全一致
image.png

2.extends关键字 : 作用:类继承

  1. 语法:<br />**class 子类函数 extends 父类函数 ** (extends不能脱离class单独使用)<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/25421229/1640421632791-e6e509c4-e7a3-44f4-93fb-fa64063a9303.png#clientId=u76ff8b62-4ba3-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=323&id=uf8a20314&margin=%5Bobject%20Object%5D&name=image.png&originHeight=336&originWidth=683&originalType=binary&ratio=1&rotation=0&showTitle=false&size=147749&status=done&style=none&taskId=u8a64225e-6d61-4c7a-9ba9-6a07c748a39&title=&width=657.4943237304688)<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/25421229/1640433870091-99d1d425-94f2-4f0f-8f8e-f9550698b84f.png#clientId=u76ff8b62-4ba3-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=364&id=uff4be596&margin=%5Bobject%20Object%5D&name=image.png&originHeight=624&originWidth=1227&originalType=binary&ratio=1&rotation=0&showTitle=false&size=231466&status=done&style=none&taskId=u54d85198-3314-4f9d-9c26-e86ad159b08&title=&width=715.4943237304688)
  1. <script>
  2. class Person {
  3. //1.构造函数
  4. constructor(name, age) {
  5. this.name = name
  6. this.age = age
  7. }
  8. //2.原型方法
  9. eat() {
  10. console.log('学习');
  11. }
  12. }
  13. //3.实例多选
  14. let p1 = new Person('李四', 20)
  15. console.log(p1);// Person { name: '李四', age: 20 }
  16. //extends关键字作用:类继承
  17. // 语法: class 子类函数 extends 父类函数 (extends不能脱离class单独使用)
  18. // 底层: 替换原型的原型继承(不会覆盖自构造器的原型)
  19. // Student.prototype.__proto__ = Person.prototype
  20. /*
  21. s1.__proto__ == Student.prototype //s1指向的原型对象 等于构造函数的prototype属性指向
  22. s1.__proto__.__proto__ == Student.prototype.__proto__ == Person.prototype
  23. */
  24. class Student extends Person {
  25. //子构造函数原型方法
  26. learn() {
  27. console.log('我今天学的很开心');
  28. }
  29. }
  30. let s1 = new Student('张三', 22)
  31. console.log(s1)//Student { name: '张三', age: 22 }
  32. s1.learn()//调用子类自己的方法 //我今天学的很开心
  33. s1.eat()//调用父类方法 //学习
  34. </script>

3.super关键字 : 子类中调用父类的方法

如果在子类中,也写了 constructor函数,则必须要调用 super()否则程序报错
语法: super(父类构造函数参数1,父类构造函数参数2)

class 子类函数 extends 父类函数{
//子类构造函数
constructor(父类构造函数参数1,父类构造函数参数2,子类参数){
super(父类构造函数参数1,父类构造函数参数2)
this className = className
}
}
image.png

  1. // 4.子类继承父类
  2. class Teacher extends Person {
  3. // 5.子类构造函数
  4. constructor(name, age, className) {
  5. //6.子类如果想拥有自己的额外属性,就需要加上constructor (父类构造函数参数1,父类构造函数参数2,自己的参数...)
  6. //此时必须要调用super(父类构造函数参数1,父类构造函数参数2),否则报错
  7. super(name, age)
  8. this.className = className
  9. }
  10. //7.子构造函数原型方法
  11. teacher() {
  12. console.log('看书');
  13. }
  14. }
  1. <script>
  2. // 1.class类
  3. class Person {
  4. // 2.父类构造函数
  5. constructor(name, age) {
  6. this.name = name
  7. this.age = age
  8. }
  9. //3.原型对象
  10. sayHi() {
  11. console.log('sayHI');
  12. }
  13. }
  14. // 4.子类继承父类
  15. class Teacher extends Person {
  16. // 5.子类构造函数
  17. constructor(name, age, className) {
  18. //6.子类不能有自己的constructor,
  19. // 如果要自己写constructor (父类构造函数参数1,父类构造函数参数2,自己的参数...)
  20. // 则必须要调用super(父类构造函数参数1,父类构造函数参数2)
  21. super(name, age)
  22. this.className = className
  23. }
  24. //7.子构造函数原型方法
  25. teacher() {
  26. console.log('看书');
  27. }
  28. }
  29. let t1 = new Teacher('张三', 20, '十一班')
  30. console.log(t1);
  31. </script>

14.函数补充内容

1.函数中的关键字: arguments

arguments 是一个伪数组arguments 的作用是动态获取函数的实参什么时候用arguments:参数不固定的时候用这个arguments注意:建议如果参数固定用形参,参数不固定用arguments

  1. 作用:arguments:获取函数所有的实参(伪数组)<br /> 场景: 例如 arr.push() 底层就是用arguments获取实参
  1. function fn(a,b){
  2. console.log(a,b)//10,20
  3. console.log( arguments )//获取函数所有实参
  4. }
  5. fn(10,20,30,40,50)

2.rest参数(剩余参数) : …形参名

  1. 作用:获取所有的剩余参数(真数组)<br /> 场景: 绝大多数情况下,剩余参数可以替代arguments
  1. 1.剩余参数(rest参数) 获取函数剩余的所有实参
  2. * 是一个真数组
  3. * 只能写在函数最后形参位置
  4. 2.场景:大多数情况下, rest参数可以替代arguments
  5. arguments是老语法,浏览器兼容性好
  6. rest参数:新语法,有些老的浏览器不支持
  7. */
  8. function fn(a, b, ...c) {
  9. console.log(arguments)//获取所有实参 10 20 30 40
  10. console.log(a, b)//10 20
  11. console.log(c)//[30,40] 获取剩余参数
  12. }
  13. fn(10, 20, 30, 40)

3.函数默认参数:

1.函数传参本质是:实参给形参赋值
2.函数形参和实参数量可以不一致,但是会按照顺序一一赋值
3.函数默认参数:
ES5 : 逻辑 或 中断
ES6 : 形参 = 默认值

  1. function fn(a=10,b=20){
  2. //找真 : 左边为真,返回左边的值。反之返回右边的值
  3. // a = a || 10
  4. // b = b || 20
  5. console.log(a)//5
  6. console.log(b)//20
  7. }
  8. fn(5)//底层 fn(5,undefined)

1.this指向

  • 函数三种执行模式 : 全局函数 、 对象方法 、 构造函数
  • this : 谁 调用 我,我就指向谁 (与函数的声明无关 取决于函数的调用)
    1. 全局函数 :函数名( ) —————————>this指向window
    2. 对象方法 :对象名.方法名( ) ———-> this指向对象
    3. 构造函数 :new 函数名( )——————>this指向new创建的空对象

分析this指向心得 : this指向取决于函数调用,而函数有三种调用方式。所以this指向也会有三种情况
(1)优先用排除法分析, 如果有new关键字,则指向new创建的对象。 否则不是window就是对象
(2)如何判断this是window还是对象,就看调用是: 函数名() 还是 对象名.方法名()

  1. let obj = {
  2. name: 'ikun',
  3. sayHi: function () {
  4. function fn() {
  5. console.log(this);
  6. }
  7. fn()// 普通函数调用 :函数名() fn调用了this 所以this指向window
  8. }
  9. }
  10. obj.sayHi()//window
  1. let obj1 = {
  2. name: 'ikun',
  3. sayHi: function () {
  4. console.log(this);
  5. }
  6. }
  7. obj1.sayHi() //对象方法调用 :对象名.方法名() 对象obj1调用了this 所以this指向obj1对象
  1. function Fn() {
  2. console.log(this);
  3. }
  4. new Fn()//构造函数调用 :new 函数名() 根据new执行流程/创建一个空对象-this指向这个对象-对象赋值-返回对象 这里的this指向new创建的空对象

共同的特点: this的指向无法动态修改

2.函数上下文执行模式(call/apply/bind)

2.1上下文模式注意点

  • a.函数上下文三个方法:call() apply() bind(),它们定义在Function构造函数的原型中
  • b.如果将this指向修改为值类型:(number,boolean,string),则函数会自动帮我们包装成对应的引用类型(基本包装类型)
    • 值类型: '123',1,true
    • 基本包装类型:String('123'),Number(1),Boolean(true)

2.2函数上下文作用:可以动态修改函数中的this指向

2.3 异同点:

  • 相同之处:都可以修改函数中this指向
  • 不同点:
    1.传参方式不同 : call是一一传参,apply是数组/伪数组传参
    2.执行机制不同 : call、apply会立即执行函数, bind不会立即执行函数

2.3 语法

有3种写法,作用都是一样改this,应用场景不同

  • call()语法: 函数名.call(this修改后的指向,参数1,参数2,...)———————适用于函数原本形参 <= 1
  • apply()语法:函数名.apply(this修改之后的指向,伪数组或者数组)——————-适用于函数原本形参 >= 2
  • bind()语法:函数名.bind(this修改后的指向)
    bind()语法并不会立即执行函数,而是返回一个修改指向后的新函数,常用于回调函数
    如果bind的时候传参,则参数也会绑定,之后无法传递实参
  1. <script>
  2. /*
  3. 1.函数三种执行方式 :
  4. 全局函数 : this指向window
  5. 对象方法: this指向对象
  6. 构造函数 : this指向new创建的对象
  7. 共同的特点: this的指向无法动态修改
  8. 2.函数上下文模式 :
  9. 2.1 作用: 可以动态修改函数中的this
  10. 2.2 语法: 有3种写法,作用都是一样改this,应用场景不同
  11. a. 函数名.call(修改的this,arg1,arg2…………)
  12. * 适用于函数原本形参 <= 1
  13. b. 函数名.apply(修改的this,[数组或伪数组])
  14. * 适用于函数原本形参 >= 2
  15. c. 函数名.bind(修改的this,arg1,arg2…………)
  16. * 特点:不会立即执行这个函数,而是返回修改this后的新函数
  17. * 适用于不会立即执行的函数 : 事件处理函数,定时器函数
  18. */
  19. // function fn(){
  20. // //三种执行模式this无法动态修改
  21. // //this = {age:18};
  22. // console.log(this);
  23. // };
  24. // fn();//this:window
  25. /* 上下文模式 */
  26. function fn(a, b) {
  27. console.log(this);
  28. console.log(a + b);
  29. };
  30. //a. 函数名.call(修改的this,arg1,arg2…………)
  31. //应用场景: 适用于函数原本形参 <= 1
  32. fn(10, 20);//this:window 30
  33. fn.call({ age: 18 }, 100, 200); //age:18 300
  34. //b. 函数名.apply(修改的this,[数组或伪数组])
  35. //应用场景: 适用于函数原本形参 >=2
  36. //apply传参的时候会自动的遍历这个数组,然后按照循序逐一传参
  37. fn.apply({ age: 88 }, [20, 30]);
  38. //c. 函数名.bind(修改的this)
  39. //特点:这个函数不会立即执行,而是返回一个修改this之后的新函数
  40. //应用场景 : 事件处理函数,定时器
  41. let newFn = fn.bind({ name: '坤坤' });
  42. console.log(newFn);
  43. newFn(50, 60);
  44. //4. 定时器中的this一定是指向window
  45. // 定时器:一段代码间隔事件执行 setTimeout(一段代码,间隔时间)
  46. //4.1 具名函数
  47. let test = function () {
  48. console.log('我是具名函数');
  49. console.log(this);
  50. };
  51. let newTest = test.bind({ name: '张三' })
  52. setTimeout(newTest, 3000);
  53. //4.2 匿名函数
  54. setTimeout(function () {
  55. console.log('我是定时器中的函数');
  56. console.log(this);
  57. }.bind({ name: '李四' }), 2000);
  58. </script>

call()上下文调用

  1. function fn(a,b){
  2. console.log( this )
  3. console.log(a+b)
  4. }
  5. fn(1,2)//this指向window
  6. //函数名.call(修改的this,参数1,参数2,....)
  7. fn.call({name:'张三'},10,20)

函数.call() 应用场景

Object.prototype.toString.call()万能检测数据类型
1.Object.prototype.toString() 得到固定格式字符串
2.检测数据类型固定格式语法:Object.prototype.toString.call()
得到一个固定格式字符串: [object 数据类型]**

  1. /* call场景:数据类型检测 */
  2. //1. typeof 数据 : 检测数据类型
  3. /* typeof有两种数据类型无法检测: null,array 都会得到object */
  4. console.log( typeof '123' )// 'string'
  5. console.log( typeof 123 )//'number'
  6. console.log( typeof true )//'boolean'
  7. console.log( typeof undefined )//'undefined'
  8. console.log( typeof null )//'object'
  9. console.log( typeof [10,20,30] )//'object'
  10. console.log( typeof function(){} )//'function'
  11. console.log( typeof {name:'123'} )//'object'
  12. //2. Object.prototype.toString() : 得到固定格式字符串
  13. //"[object type]",其中 type 是对象的类型
  14. //检测数据类型固定格式语法: Object.prototype.toString.call(数据)
  15. console.log( Object.prototype.toString.call('123') )//[object String]
  16. console.log( Object.prototype.toString.call(123) )//[object Number]
  17. console.log( Object.prototype.toString.call(true) )//[object Boolean]
  18. console.log( Object.prototype.toString.call(undefined) )//[object Undefined]
  19. console.log( Object.prototype.toString.call(null) )//[object Null]
  20. console.log( Object.prototype.toString.call([10,20,30]) )//[object Array]
  21. console.log( Object.prototype.toString.call(function(){}) )//[object Function]
  22. console.log( Object.prototype.toString.call({name:'123'}) )//[object Object]

apply()上下文调用

  1. function fn(a,b){
  2. console.log(this)
  3. console.log(a+b)
  4. }
  5. fn(1,2)//普通函数 this->window
  6. //(1)函数名.call(修改的this,参数1,参数2,....)
  7. fn.call({name:'张三'},10,20)
  8. //(2)函数名.apply(修改的this, 数组/伪数组 )
  9. //apply传参的时候会自动的遍历这个数组,然后按照循序逐一传参 a = [30,40][0] b=[30,40][1]
  10. fn.apply({name:'李四'},[30,40])
  11. </script>

arr.push.apply(arr,伪数组)的应用场景:伪数组转真数组

  1. <script>
  2. /*
  3. 伪数组: 有数组三要素(下标、元素、长度),但是不能使用数组的方法
  4. 伪数组 本质是对象
  5. 伪数组不能使用数组方法: 伪数组的原型指向对象的原型,而不是Array的原型
  6. */
  7. let obj = {
  8. 0: 10,
  9. 1: 20,
  10. 2: 30,
  11. 3: 40,
  12. length: 4,//length 省略不了
  13. }
  14. console.log(obj[0]);
  15. //将伪数组转成真数组 arr.push 往数组后面新增元素 返回的是数组的长度
  16. let newArr = []
  17. let a = newArr.push(obj[0], obj[1], obj[2], obj[3])
  18. console.log(newArr)//[10, 20, 30, 40]
  19. console.log(a)//数组长度 4
  20. // ES5:上下文
  21. let newArr1 = []
  22. newArr1.push.apply(newArr1, obj)
  23. //第一个参数:newArr1 本来this就是newArr 这里不需要修改this(相当于this不变)
  24. //第二个参数:obj 借助apply特点:自动遍历伪数组/数组,逐一传参(自己省去了for循环)
  25. console.log(newArr1);
  26. // ES6推荐: Arr.from(伪数组) 伪数组转成真数组
  27. let arr = Array.from(obj)
  28. console.log(arr);
  29. </script>

Math.max.apply(Math, arr)应用场景:求数组最大值

  1. <script>
  2. //求数组最大值
  3. let arr = [10, 13, 12, 3, 14, 23, 32]
  4. // 1.arr.sort() 数组排序 参数:回调函数 固定语法死记硬背
  5. // arr.sort(function (a, b) {
  6. // return a - b//从小到大排
  7. // return b - a//从大到小排序
  8. // })
  9. arr.sort(function (a, b) {
  10. return b - a
  11. })
  12. console.log(arr[0]);
  13. //2.擂台思想
  14. let max = 0
  15. for (let i = 0; i < arr.length; i++) {
  16. if (arr[i] > max) {
  17. max = arr[i]
  18. }
  19. }
  20. console.log(max)
  21. // ES5: Math.max.apply(Math, 数组名)
  22. //函数名.apply(修改的this,数组/伪数组)
  23. //第一个参数 Math: Math调用了方法 this指向的就是Math,这里就不需要修改this(传Math,相当于this不变)
  24. //第二个参数 arr :借助apply的特点.自动遍历数组/伪数组 逐一传参
  25. let max3 = Math.max.apply(Math, arr)
  26. console.log(max3);
  27. //ES6(推荐):
  28. let max4 = Math.max(...arr)
  29. console.log(max4)
  30. </script>

bind()上下文调用

  1. function fn(a,b){
  2. console.log(this)
  3. console.log(a+b)
  4. }
  5. fn(1,2)//普通函数 this->window
  6. //(1)函数名.call(修改的this,参数1,参数2,....)
  7. fn.call({name:'张三'},10,20)
  8. //(2)函数名.apply(修改的this, 数组/伪数组 )
  9. //apply传参的时候会自动的遍历这个数组,然后按照循序逐一传参 a = [30,40][0] b=[30,40][1]
  10. fn.apply({name:'李四'},[30,40])
  11. //(3)函数名.bind(修改this)
  12. //bind不会立即调用函数,而是得到一个修改this之后的新函数
  13. let newFn = fn.bind({name:'王五'})
  14. newFn(25,66)

bind()应用场景

bind场景主要是修改 ‘不需要立即执行的函数
//事件处理函数、定时器函数

  1. //bind场景主要是修改 '不需要立即执行的函数'
  2. // 事件处理函数、定时器函数
  3. //1.定时器中的this一定是window
  4. let fn = function(){
  5. console.log( this )
  6. }
  7. //使用bind修改this
  8. let newFn = fn.bind({name:'666'})
  9. // fn() : 调用函数, 运行结果是函数返回值
  10. // fn : 变量取值, 取出fn中存储的堆地址
  11. setTimeout( newFn ,3000)
  12. //下面这个写法和上面写法是一样的 : 函数是数据类型,也可以像其他数据一样直接使用语法
  13. setTimeout(function(){
  14. console.log(this)
  15. }.bind({name:'干饭'}),3000)

上下文模式注意点

  1. /*
  2. 1. 函数上下问执行模式 : 动态修改this
  3. 注意点 : 修改的this只能是引用类型
  4. 2.注意点: this一定是指向对象(引用类型)
  5. a.如果修改的this是 : string,number,boolean则编译器会自动转成对应的对象类型。
  6. 基本包装类型 : new String() new Number() new Boolean()
  7. b.如果修改的this是: undefined和null,则修改无效
  8. */
  9. function fn() {
  10. console.log(this);
  11. };
  12. fn.call('str');//String
  13. fn.call(1);//Number
  14. fn.call(true);//Boolean
  15. //如果传的是undefined和null,或者不传。代码不会报错,也不会帮我们修改this,还是原来的window
  16. fn.call(undefined);//Window
  17. fn.call(null);//Window
  18. fn.call();//Window
  19. fn.call(window);//Window

3.递归函数 :

/ 高阶函数: 自调用函数、回调函数、闭包、递归 /

什么是递归函数? : 函数内部调用自己
注意点: 需要满足条件才会递归,否则会导致死循环
递归函数和循环功能类似

  1. //一个函数递归
  2. // function fn(){
  3. // console.log('哈哈');
  4. // fn();
  5. // };
  6. // fn();
  7. //两个函数递归
  8. // function fn1(){
  9. // console.log('哈哈');
  10. // fn2();
  11. // };
  12. // function fn2(){
  13. // console.log('呵呵');
  14. // fn1();
  15. // };
  16. // fn2();
  17. //需求:写一个函数,打印三次 班长爱坤哥
  18. let i = 1;
  19. function fn(){
  20. console.log('好好学习');
  21. i++;
  22. if(i <= 3){
  23. fn();
  24. };
  25. //循环实现
  26. // for(let i = 1;i<=3;i++){
  27. // console.log('班长爱坤哥');
  28. // };
  29. };
  30. fn();

递归应用场景:

2.1 浅拷贝与深拷贝

1.浅拷贝: 拷贝的是地址, 修改拷贝后的数据对原数据有影响
  1. //声明一个对象
  2. //拷贝: 把对象中存储的数据 拷贝一份赋值给其他对象
  3. let obj = {
  4. name:'ikun',
  5. age:30,
  6. hobby:['讲课','学生','学习'],
  7. friend:{
  8. name:'朋友',
  9. sex:'男'
  10. }
  11. }
  12. //浅拷贝: 拷贝的是地址
  13. let obj1 = obj
  14. //由于浅拷贝,拷贝的是地址。 所以修改拷贝后的数据,原来的数据也会变化
  15. obj1.hobby = '美食'
  16. console.log(obj1,obj)

2.深拷贝:拷贝的是数据, 修改拷贝后的数据对原数据没有影响
  1. let obj = {
  2. name:'ikun',
  3. age:30,
  4. hobby:['讲课','学生','学习'],
  5. friend:{
  6. name:'朋友',
  7. sex:'男'
  8. }
  9. }
  10. /*
  11. (1)遍历obj,把所有的属性添加给newObj
  12. (2)如果obj[key]是引用类型(数组、对象),则不能直接拷贝地址
  13. (2.1)数组:给newObj声明一个空数组,然后遍历obj[key],把里面元素添加给newObj[key]
  14. (2.2)对象:给newObj声明一个空对象,然后遍历obj[key],把里面元素添加给newObj[key]
  15. (3)如果obj[key]不是引用类型,则直接赋值。结束递归
  16. */
  17. function kaobei(newObj,obj){
  18. for(let key in obj){
  19. //判断 obj[key] 是不是数组类型
  20. if( obj[key] instanceof Array ){
  21. //声明一个空数组,然后继续拷贝数组里面的数据
  22. newObj[key] = []
  23. kaobei(newObj[key],obj[key])
  24. }else if(obj[key] instanceof Object){
  25. //声明一个空对象,然后继续拷贝数组里面的数据
  26. newObj[key] = {}
  27. kaobei(newObj[key],obj[key])
  28. }else{
  29. newObj[key] = obj[key]
  30. }
  31. }
  32. }
  33. //调用深拷贝函数
  34. let newObj = {}
  35. kaobei(newObj,obj)
  36. //深拷贝:修改拷贝的数据,对原数据没有影响
  37. newObj.hobby[0] = '111'
  38. console.log( newObj,obj)

3.用JSON转换实现深拷贝
  1. /* 2.深拷贝有两种实现方式
  2. 2.1 推荐使用:json转换
  3. 2.2 递归 :
  4. */
  5. let obj = {
  6. name:'ikun',
  7. age:30,
  8. hobby:['讲课','学生','学习'],
  9. friend:{
  10. name:'朋友',
  11. sex:'男'
  12. }
  13. }
  14. //(1)先把js对象转成json格式字符串 : JSON.stringify(js对象)
  15. //json在把js转成json格式字符串的时候,底层会自动帮你深拷贝
  16. // let json = JSON.stringify(obj)
  17. // console.log( json )
  18. //(2)再把刚才的json字符串,转成js对象 : JSON.parse( json格式 )
  19. // let js = JSON.parse( json )
  20. // js.hobby = '学习'
  21. // console.log( js,obj )
  22. //以上两个流程,可以简写成一行代码
  23. let newObj = JSON.parse( JSON.stringify( obj ) )
  24. newObj.friend = '小狗'
  25. console.log(newObj,obj)

4.遍历dom树
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  6. <meta http-equiv="X-UA-Compatible" content="ie=edge" />
  7. <title>Document</title>
  8. <style>
  9. * {
  10. padding: 0;
  11. margin: 0;
  12. }
  13. .menu p {
  14. width: 100px;
  15. border: 3px solid;
  16. margin: 5px;
  17. }
  18. .menu > div p {
  19. margin-left: 10px;
  20. border-color: red;
  21. }
  22. .menu > div > div p {
  23. margin-left: 20px;
  24. border-color: green;
  25. }
  26. .menu > div > div > div p {
  27. margin-left: 30px;
  28. border-color: yellow;
  29. }
  30. </style>
  31. </head>
  32. <body>
  33. <div class="menu">
  34. <!-- <div>
  35. <p>第一级菜单</p>
  36. <div>
  37. <p>第二级菜单</p>
  38. <div>
  39. <p>第三级菜单</p>
  40. </div>
  41. </div>
  42. </div> -->
  43. </div>
  44. <script>
  45. //服务器返回一个不确定的数据结构,涉及到多重数组嵌套
  46. let arr = [
  47. {
  48. type: '电子产品',
  49. data: [
  50. {
  51. type: '手机',
  52. data: ['iPhone手机', '小米手机', '华为手机']
  53. },
  54. {
  55. type: '平板',
  56. data: ['iPad', '平板小米', '平板华为']
  57. },
  58. {
  59. type: '智能手表',
  60. data: []
  61. }
  62. ]
  63. },
  64. {
  65. type: '生活家居',
  66. data: [
  67. {
  68. type: '沙发',
  69. data: ['真皮沙发', '布沙发']
  70. },
  71. {
  72. type: '椅子',
  73. data: ['餐椅', '电脑椅', '办公椅', '休闲椅']
  74. },
  75. {
  76. type: '桌子',
  77. data: ['办公桌']
  78. }
  79. ]
  80. },
  81. {
  82. type: '零食',
  83. data: [
  84. {
  85. type: '水果',
  86. data: []
  87. },
  88. {
  89. type: '咖啡',
  90. data: ['雀巢咖啡']
  91. }
  92. ]
  93. }
  94. ]
  95. //arr:数据 father:父盒子
  96. function addElement(arr,father){
  97. //遍历数组,生成div>p添加到父元素中
  98. for(let i = 0;i<arr.length;i++){
  99. //(1)创建空标签
  100. let div = document.createElement('div')
  101. //(2)设置内容
  102. div.innerHTML = `<p>${arr[i].type || arr[i]}</p>`
  103. //(3)添加到父盒子
  104. father.appendChild(div)
  105. //如果菜单还有data,说明还有子菜单,则需要继续遍历添加
  106. if( arr[i].data ){
  107. addElement(arr[i].data,div)
  108. }
  109. }
  110. }
  111. let menu = document.querySelector('.menu')
  112. //调用函数
  113. addElement(arr,menu)
  114. </script>
  115. </body>
  116. </html>

image.png

4.闭包(closure):

  • 1.闭包是什么 : 闭包是一个可以访问其他函数内部变量的函数
    • 或者理解为 闭包 = 函数 + 上下文引用 的组合
  • 2.闭包的作用 : 解决变量污染

image.png
后期闭包出现较多的一般都会在回调函数里面

  1. function fn(){
  2. let a = 1
  3. //在fn1函数中, 访问了其他函数fn内部的变量。 fn1 + a 形成闭包
  4. function fn1(){
  5. console.log(a)
  6. }
  7. fn1()
  8. }
  9. fn()
  10. function test(){
  11. let num = 1
  12. setTimeout( function(){
  13. console.log(num)
  14. },2000)
  15. }
  16. test()

闭包场景:

  1. <script>
  2. /*
  3. 1.闭包(closure)是什么?
  4. 1.1 闭包是一个访问其他函数内部变量的函数
  5. 1.2 闭包 = 函数 + 上下文引用 的组合
  6. 总结:形成闭包要有两个条件,缺一不可。 (1)函数 (2)访问其他函数内部变量
  7. 2.闭包作用 : 变量污染
  8. * 后期闭包出现较多的一般都会在回调函数里面
  9. */
  10. //获取元素
  11. let input = document.querySelector('input')
  12. let button = document.querySelector('button')
  13. button.onclick = function(){
  14. //有一个变量存储搜索结果
  15. let str = '6666'
  16. //模拟网络请求:2秒钟之后获取搜索结果
  17. setTimeout(function(){
  18. alert(`搜索结束,本次搜索结果是${str}`)
  19. },2000)
  20. }
  21. </script>

5.总结梳理:

5.1this三种指向

  1. 全局函数 :函数名( ) —————————>this指向window
  2. 对象方法 :对象名.方法名( ) ———-> this指向对象
  3. 构造函数 :new 函数名( )——————>this指向new创建的空对象

5.2call、apply、bind区别

  • 相同点:都是修改函数this指向
  • 不同点
    • 传参方式不同: call用于单个参数,apply用于多个参数
    • 执行机制不同: call与apply会立即执行, bind不会立即执行
      • call、apply用一次修改一次
      • bind;一次修改,终生有效

5.3闭包

  • 什么是闭包:以下两种回答都可以
    • 闭包是一个访问其他函数内部变量的函数
    • 闭包是 函数 + 上下文代码组合
  • 闭包作用:解决变量污染

5.4递归

  • 什么是递归:函数内部调用自己
  • 递归场景
    • 深拷贝
    • 遍历dom树

Var的关键字特点:(预解析/没有块级)

预解析:js代码从上往下解析前 会现对变量声明进行提升
image.png

1.ES6新语法

1.1-变量声明let与const


重复定义 修改值 声明提升 块级作用域 循环
var 可以 可以 严格来说不支持 可以
let 不可以 可以 不会 支持 可以
const 不可以 不可以(除数组和对象) 不会 支持 不可以

1.没有预解析 : 变量必须要先声明,才能使用
2.有块级作用域 : if for 大括号里面声明的变量是局部变量
let 变量 : 可以修改变量中的数据
const 常量 : 只能在声明的时候赋值一次,不可修改

1.2-解构赋值语法

1.对象解构赋值

1.1 取出对象的值赋值给变量

ES6 解构赋值
let { name, age, sex } = obj

  1. //1:取出 对象的属性 赋值 给变量
  2. let obj = {
  3. name: '张三',
  4. age: 18,
  5. sex: '男'
  6. }
  7. /* ES5 */
  8. let name = obj.name;
  9. let age = obj.age;
  10. let sex = obj.sex;
  11. console.log(name,age,sex);
  12. /* ES6 */
  13. /* a.这行代码本质:声明三个变量 name,age,sex。取出右边obj对象对应的属性名赋值给左边的变量*/
  14. let {name,age,sex} = obj;
  15. console.log(name,age,sex);
  16. /* b.由于obj对象没有score属性,所以变量score的值为undefined。 */
  17. let {name,score} = obj;
  18. console.log(name,score);
  19. /* c. sex:gender 的含义 : let gender = obj.sex */
  20. let {name,sex:gender} = obj;
  21. console.log(name,gender);

1.2 取出变量的值 赋值给对象的属性

ES6 解构赋值
let obj = { name, age, sex, }

  1. //2:取出变量的值 赋值给对象的属性
  2. let name = '李四';
  3. let age = 20;
  4. let sex = '男';
  5. let sayHi = function(){
  6. console.log('你好');
  7. }
  8. /* ES5 */
  9. let person = {
  10. name : name,
  11. age : age,
  12. gender : sex,
  13. sayHi :sayHi
  14. }
  15. console.log(person);
  16. /* ES6 */
  17. let person = {
  18. name,//等价于 name:name
  19. age,
  20. gender:sex,//如果属性名和变量名不一致,还是按照以前ES5的写法来赋值
  21. sayHi,
  22. play(){//等价于:play:function(){}
  23. console.log('学习使我快乐');
  24. }
  25. };
  26. console.log(person);

2.数组解构赋值

  1. <script>
  2. //数组解构赋值
  3. let arr = [10, 20, 30]
  4. /* ES5 */
  5. // let n1 = arr[0];
  6. // let n2 = arr[1];
  7. // let n3 = arr[2];
  8. // console.log(n1,n2,n3);
  9. /* ES6 */
  10. let [n1, n2, n3 = 50, n4 = 100] = arr
  11. console.log(n1, n2, n3, n4) // 10 20 30 100
  12. </script>

3.函数参数解构赋值

  1. <script>
  2. /*
  3. 1.学习目标 : 掌握函数参数的解构赋值
  4. 2.学习路线
  5. (1)复习 函数 传参的本质是 实参给形参赋值的过程
  6. (2)使用解构赋值语法 给函数传参
  7. (3)使用解构赋值语法 设置 函数的默认参数
  8. */
  9. /* (1) ES5 :函数传参 */
  10. /**
  11. * @description:
  12. * @param {Object} obj:对象类型 {name:名字 age:年龄 sex:性别}
  13. * @return:
  14. */
  15. // function fn(obj){
  16. // console.log(obj);
  17. // //声明三个变量接收对象的参数值
  18. // let name = obj.name;
  19. // let age = obj.age;
  20. // let sex = obj.sex;
  21. // console.log(name,age,sex);
  22. // };
  23. // fn({
  24. // name:'黑马李宗盛',
  25. // age:32,
  26. // sex:'男'
  27. // });
  28. /* (2) ES6 :函数传参 */
  29. // function fn1({name,age,sex}){
  30. // //声明三个变量接收对象的参数值
  31. // console.log(name,age,sex);
  32. // };
  33. // fn1({
  34. // name:'黑马李宗盛',
  35. // age:32,
  36. // sex:'男'
  37. // });
  38. //添加解构语法默认值
  39. // function fn2({name = '班长',age = 38,sex:gender='女'}){
  40. // //声明三个变量接收对象的参数值
  41. // console.log(name,age,gender);
  42. // };
  43. // fn2({
  44. // name:'黑马李宗盛',
  45. // });
  46. /* (3)函数默认参数 */
  47. /* ES5:使用逻辑或短路运算实现 */
  48. // function fn(n1,n2,n3){
  49. // n1 = n1 || 10;
  50. // n2 = n2 || 20;
  51. // n3 = n3 || 30;
  52. // console.log(n1,n2,n3);
  53. // };
  54. // fn(5,8);
  55. // fn();
  56. /* ES6:使用解构赋值语法默认值实现 */
  57. function fn (n1 = 10, n2 = 20, n3 = 30) {
  58. console.log(n1, n2, n3)
  59. }
  60. fn(5, 8)
  61. fn()
  62. </script>

4.小结:

  1. 1.1 取出对象的属性值 赋值给 变量
  2. 语法:let { name, age, sex } = 对象名
  3. 场景:如果函数参数是对象,就可以进行解构
  4. 1.2 取出变量的值 赋值给 对象的属性
  5. let 对象名 = {name,age,sex}
  6. 场景:获取多个表单的数据,装入对象中
  7. 2.解构赋值的其他用法
  8. 2.1解构赋值的默认值
  9. let {name,age = 18,sex} = obj //if(obj.age==undefined){age =18}else{age = obj.age}
  10. 2.2自定义解构变量名
  11. let {sex:gender} = obj 相当于 let sex = obj.gender
  12. 1.3 取出数组的元素赋值给变量
  13. let [n1,n2,n3,n4] = arr
  14. 1.4 取出变量的值 赋值给数组的元素
  15. let arr = [n1,n2,n3,n4]

开发中,很多函数的参数是对象类型

  1. function fn({ uname, password }) {
  2. //函数参数进行解构赋值
  3. console.log(uname, password);
  4. }
  5. fn({ uname: 'admin', password: '123456' })

1.3 箭头函数

1.箭头函数语法:

就是function简写 匿名函数才能转换为箭头函数
把function单词 替换成 箭头=>
把形参()移到箭头的左边

2.箭头函数其他用法:

如果只有一个形参,则可以省略形参小括号
a=>{return a2}
如果函数体只有一行,则可以省略大括号 此时需要省略return
a => a
2

3.箭头函数的this指向:

  • (1)箭头函数没有this : 这意味着 call() apply() bind() 无法修改箭头函数中的this
  • (2)箭头函数中的this指向 :访问上一个作用域的this
    • 说人话:函数在哪个作用域声明,this就是谁 (本质是通过作用域链访问上一个作用域中的this)
  1. </script>
  2. //1.匿名函数 //函数表达式
  3. let fn = function () {
  4. console.log(11111);
  5. }
  6. fn()
  7. //2.箭头函数
  8. let fn1 = () => {
  9. console.log(2222);
  10. }
  11. fn1()
  12. //一个形参 省略小括号 一行函数体 省略大括号和return
  13. let fn2 = a => a * 2
  14. console.log(fn2(3));
  15. //多个形参
  16. let fn3 = (d, w, c) => d + w + c
  17. console.log(fn3(2, 3, 4))
  18. //对象的解构 配合箭头函数
  19. let { uname, pwd } = { uname: '张三', pwd: '123456' }
  20. let fn4 = ({ uname, pwd }) => console.log({ uname, pwd });
  21. fn4({ uname: '张三', pwd: '123456' })
  22. </script>

1.4 展开运算符

展开运算符: …
作用:相当于遍历对象简写
作用场景:
1. 链接数组: arr1.push(…arr2)
2. 求数组最大值 :Math.max(…arr)

  1. //1. 链接数组
  2. let arr1 = [1, 3, 5, 7]
  3. let arr2 = [2, 4, 6, 8, 'a']
  4. arr1.push(...arr2)
  5. console.log(arr1)//[1, 3, 5, 7, 2, 4, 6, 8, 'a']
  6. //2.求数组最大值
  7. let arr3 = [9, 3, 6, 8, 12]
  8. let max = Math.max(...arr3)
  9. console.log(max)

1.5数据类型set(集合)

  1. Set :集合。 ES6新增的数据类型,类似于ES5的数组
  2. Set与Array最大的区别 : Set不支持重复元素
  3. Set经典应用场景 : 数组去重
  1. // 应用:数组去重
  2. // 一行代码去重: let newArr = Array.from(new Set(数组))
  3. //一行代码去重 : let newArr = [...new Set(数组)]
  4. let arr = [12, 12, 34, 34, 42, 42]
  5. //1.声明Set
  6. let set = new Set(arr)
  7. console.log(set); //{ 12, 34, 42 }
  8. // 2.把set转成真数组
  9. let newArr = Array.from(set)
  10. console.log(newArr);// [12, 34, 42]
  11. /* 一行代码实现数组去重 */
  12. let newarr = Array.from(new Set(arr))
  13. console.log(newarr);
  14. </script>

2.数组迭代方法

2.1数组map遍历 : (映射数组)

  1. map场景 : 映射数组。 对数组每一个元素进行映射处理,得到一个全新数组
    映射关系: 每一个元素 0.8
    映射关系: 每一个元素
    2
  2. map方法特点
    (1)回调执行次数 === 数组长度
    (2)本身返回值 : 隐射之后的新数组
    (3)回调函数内部return
    return 新数组元素值
    如果不写return, 新数组每一个元素都变成undefined
  1. let arr = [88,100,90,66,50]
  2. //全场8折
  3. let newArr = arr.map( (value,index)=>{
  4. console.log(index,value)//下标 元素
  5. return value*2
  6. } )
  7. console.log( newArr )

2.2数组filter遍历 : (筛选数组)

数组filter遍历 筛选数组找出数组中符合条件的元素 返回一个新数组
return true : 满足筛选条件,当前元素放入新数组中
return false : 不满足筛选条件,当前元素不放入新数组
参数是 : 回调函数(元素, 下标)=> { return 新元素 }

image.png
筛选已勾选的 求和
image.png

  1. let arr = [20, 30, 40, 50, 60]
  2. /*
  3. let res = arr.filter(function (value, index) {
  4. if (value % 3 == 0) {
  5. return true
  6. } else {
  7. return false
  8. }
  9. })
  10. console.log(res);//[30, 60]
  11. */
  12. //ES6 简化后
  13. let res1 = arr.filter(value => value % 3 == 0)
  14. console.log(res1);//[30, 60]

2.2 filfer()应用场景

  1. <script>
  2. // 筛选4000-5000之间的产品
  3. let arr1 = [
  4. {
  5. name: '苹果电脑',
  6. price: 3000
  7. },
  8. {
  9. name: '小米',
  10. price: 4200
  11. },
  12. {
  13. name: '平板',
  14. price: 4800
  15. },
  16. {
  17. name: '空调',
  18. price: 2800
  19. },
  20. {
  21. name: '电视机',
  22. price: 3080
  23. }
  24. ]
  25. let newArr = arr1.filter(value => value.price >= 4000 && value.price <= 5000)
  26. console.log(newArr);
  27. </script>

2.3 数组forEach遍历 : (遍历数组)

1.forEach应用场景:用于遍历数组,相当于for循环另一种写法
2.注意点:
a. 回调函数执行次数 == 数组长度
数组中有多少个元素,回调函数就会执行几次
b. forEach函数没有返回值
c. 回调函数不需要return
就算手动return,也不会结束循环

  1. let arr = [10, 55, 60, 88, 99, 70]
  2. arr.forEach((value,index)=>{
  3. console.log(index,value)
  4. })

2.4 数组some遍历 : (判断是否存在满足条件的元素)

1.some应用场景:用于判断数组中是否存在满足条件的元素
2.注意点:
a. 回调函数执行次数 != 数组长度
some函数在遍历的时候可以中途结束
b. some函数返回一个布尔类型值
c. 回调函数返回布尔类型值用于结束遍历
return true; //遍历结束,且some函数返回值为true
(默认) return false; //遍历继续,且some函数返回值为false

  1. //判断数组中是否存在小于0的元素
  2. let arr = [10, 55, 60, 88, 99, 70]
  3. let res = arr.some((value, index) => value < 0)
  4. console.log(res)//false

2.5 数组every遍历(判断数组中是否所有元素都满足条件)

every应用场景:用于判断数组中是否所有元素都满足条件 (开关思想) (全选 反选)
every函数 返回一个布尔类型值
全部满足条件 返回true
有元素不满足条件 返回false
vue购物车案例 第2 - 5天
image.png
image.png
回调函数返回布尔类型值用于结束遍历
return true; //遍历继续,且every函数返回值为true
(默认)return false; //遍历结束,且every函数返回值为false

  1. //需求:判断数组中没有负数
  2. let arr = [23, 31, 60, -30, 90, 108, 260]
  3. let res = arr.every(value => value > 0)
  4. console.log(res)//false

2.6 数组findIndex方法 (获取符合条件的第一个元素(下标))

image.png

  • 数组findIndex方法
    作用: 获取符合条件的第一个元素位置(下标)
    语法 : arr.findIndex((item, index) => { return true / false })
  • 返回值:
    符合则返回元素下标,
    不符合则返回 - 1
  • 应用场景 :
    适用于数组中元素为对象类型,比传统for循环要高效
  1. let arr = [
  2. { name: '张三', age: 20 },
  3. { name: '李四', age: 30 },
  4. { name: '王五', age: 25 },
  5. { name: '赵六', age: 33 },
  6. { name: '小七', age: 10 },
  7. ]
  8. let res = arr.findIndex(value => value.name == '小七')
  9. console.log(res);//符合则返回元素下标, 不符合则返回 - 1

2.7数组reduce 方法

image.png

  • 作用 : 遍历数组元素,为每一个元素执行一次回调函数
  • 语法 : arr.reduce( (sum,value)=>{ return sum + value } ,0)
  • 返回值: 最终sum值
  • 应用场景 : 数组求和/平均值/最大值/最小值
  • 注意点:最好给一个初始值,避免空数组报错
  • image.png
  • image.png
    1. //求和数组
    2. let res = arr.reduce((sum, value, index) => sum + value, 0)//起始值加需要0 否则报错
    3. console.log(res);

总结:推荐使用字面量方式声明数组,而不是 Array 构造函数

实例方法 forEach 用于遍历数组,替代 for 循环
image.png

实例方法 filter 过滤数组单元值,生成新数组
image.png
image.png

实例方法 map 迭代原数组,生成新数组

实例方法 concat 合并两个数组,生成新数组
实例方法 sort 对原数组单元值排序
实例方法 splice 删除或替换原数组单元
image.png
image.png
实例方法 indexOf 检索数组单元值 if(value.indexOf(‘垃圾’))判断存不存在 ( !=-1) 代表存在 -1就代表不存在
image.png

实例方法 reverse 反转数组
静态方法 from 伪数组转成数组

总结:推荐使用字面量方式声明字符串,而不是 String 构造函数
实例属性 length 用来获取字符串的度长
实例方法 split( ) 用来将 字符串 拆分 成数组
实例方法 join( ) 将 数组元素 拼接成 字符串

实例方法 toUpperCase( ) 用于将字母转换成大写
实例方法 toLowerCase( ) 用于将字母转换成小写
image.png
实例方法 slice( ) 用于字符串截取 可提取字符串的某个部分
image.png
实例方法 substr( ) 用于字符串截取

实例方法 indexOf( ) 检测是否包含某字符
实例方法 startsWith 检测是否以某字符开头
实例方法 endsWith 检测是否以某字符结尾
实例方法 replace 用于替换字符串,支持正则匹配
image.png

日期处理: Vue 第二天 和 nodeJS笔记里有
//1.原生的 new Date() 获取时间
let date = new Date();
console.log(newDate());//Sun Feb 13 2022 16:30:38 GMT+0800 (中国标准时间)
console.log(newDate().toLocaleDateString()); //2022/2/13
console.log(newDate().toLocaleTimeString()); //16:29:35
console.log(newDate().toLocaleString()); //2022/2/13 16:29:35

//2.使用moment.js 需要导入
//2.1 基本使用
let date1 = moment().format(‘YYYY-MM-DD’);//日期格式
console.log(date1);//2019-11-02
//2.2 如果moment传入了时间,就表示用传入的时间帮你生成对应的格式
let date2 = moment(‘2018/10/25 23:24:25’).format(‘YYYY年MM月DD日 HH时mm分ss秒’);
console.log(date2);//2018年10月25日 23时24分25秒
moment(接收的是data对象,不能是字符串).format(‘转成指定格式’)
image.png
image.png

image.png

image.png

作用域链:

当代码在一个环境中执行时,会创建变量对象的一个作用域链
由子级作用域返回父级作用域中寻找变量,就叫做作用域链