JS学习笔记

ps: 网站首页默认名称为 index.html 首页 浏览器默认 解析 index.html文件

JS简介

java和javascript

​ 1.两种语言都是同一个人写的

​ 2.java 是强类型语言 使用变量一定要定义类型 运行在服务器端

​ 3.javascript 是弱类型语言 没有要定义类型的需求 运行在客户端

前端

​ 特点:前端更新技术非常迅速

​ 要学的内容:js+jQuery+ES6+框架vue/react/angular+TS(typescript) 趋近强语言

​ 写后端:node.js 运行在服务端

​ 数据库:mongoose/mysql

js的引用方法

​ 1.内部写法:写在script标签内部

​ 2.外部写法:

​ 3.js文件的后缀名为:.js

ps: 内存:堆内存/栈内存

js的组成

​ BOM 浏览器对象 DOM 文档对象 ECMAscript js标准

js基础内容

1.js输出语句

​ 1.在body文档内部输出 document.write(‘我是index页面1’) 内部必须用引号包起来

​ 2.控制台输出 console.log(‘我是控制台输出’)

​ 3.弹窗输出 alert(‘我是弹窗’)

​ ps: window三大弹窗 alert警告框 confirm信息框 prompt输入框

​ 弹窗会产生拦截的效果 阻碍后面js的运行

2.js变量命名规范

严格区分大小

​ 变量:储存在内存中

​ 1.声明变量的时候 不允许使用关键字

​ 2.不允许使用中文

​ 3.不允许使用非"_“,”-"的标点符号

​ 4.不允许使用数字开头

​ 5.驼峰命名(被链接的单词 首字母大写) 不允许超过250字节

3.变量赋值

​ var 变量名称 = 数据值

​ var:变量的关键字 声明变量 指代关系

​ =:赋值

​ 对于变量来说 声明变量就相当于给当前变量赋值了一个undefined

4.数据类型

基础数据类型:栈(先进后出)

​ 1.字符串 String 被引号包裹的都是字符串 不分单双引号

​ 2.数字 Number 纯数字

​ 3.布尔 Boolean 判断对错 对true/错false

​ 4.对空 null 空 (占位符)

​ 5.未定义 undefined 声明变量但没有赋值的情况下为该值

​ 6.唯一标识 Symbol es6新增

引用数据类型:堆

​ 7.数组 Array

​ 8.函数 Function

​ 9.对象 Object

js中 万物皆是对象 字符串也可以是 对象类型

具备迭代器的数据类型:Array String Set Map NodeList arguments Symbol

5.运算符

​ 运算符是将数字进行运算 数字:整数 浮点数 NaN非数值的数字类型

等号类型不属于运算符

​ 1.= 赋值 将变量赋值成值 var a = 1

​ 2.== 判断是否相等

​ 3.=== 全等 不光判断值是否相等 还需要判断数据类型是否相等

强调:一个等号 一定是赋值 两个或三个等号一定是判断

隐式转换:纯数字形式的字符串 在判断值的时候 会将字符串进行隐式转换为数字类型 再做判断。 0 1 由于二进制的原因 在判断时 0为false 1为true

纯数字字符串在判断、运算(除+以外)会进行隐式转换

​ 4.- 减

​ 5.* 乘

​ 6./ 除

​ 7.% 取模(余数)

​ 8.++ 自增 ++a 先运算后赋值 a++ 先赋值后运算

​ 9.-- 自减 --a 先运算后赋值 a-- 先赋值后运算

ps:NaN不等于NaN isNaN 判断是否为非数字

运算符只针对数字类型,非数字类型会进行运算但是会自动转译成数字/NaN

6.字符串

​ 基础数据类型:字符串 数字 布尔 空 未定义

判断基础数据类型的语法:typeof a

1.字符串的拼接

​ 字符串的拼接符: + 对于字符串而言 + 是拼接符号

​ 拼接规则:遇到变量 变量两端加"+"号并且引号 引号之间是区分单双引号的 会互相寻找闭合 所以要成对书写 注意层级

​ 拼接抽出来的内容的数据类型均为字符串类型(字符串和任何数据类型拼接结果都为字符串类型)

2.字符串方法

​ 索引:所有字符串都有长度 下标(所有下标从0开始算)

​ 1.charAt() 返回指定索引位置的字符串

​ 2.concat() 字符串拼接

​ 3.indexOf() 返回字符串中检索指定字符第一次出现的位置(下标),如未找到,返回-1

​ 4.replace(a,b) 替换与正则表达式匹配的子串(字符串的替换) a表示指定被替换的字符串 b表示替换成的字符串

​ 匹配查找到的第一个所匹配的字符串(单个匹配)

​ 全部匹配 利用正则(/被替换的字符串/g,替换成的字符串)

​ 5.toString() 通用 将数据类型转换为字符串类型(不改变原先变量的数据类型)

​ 字符串切割 均不影响原字符串

​ 6.split(‘a’) 切割字符串 a表示指定切割的字符串

​ 将字符串切割成数组,返回的是带有字符串内容的数组

​ 切割空字符串 split(‘’)得到的是每一个字符切割成单个的数组的子项

​ a = ‘123456789’ a.split(‘4’) 得到的值为[‘123’,‘56789’]

​ 7.slice(a,b) 切割字符串 a表示切割的起始下标 b表示切割的结束下标

​ 取值范围为[a,b) a,b均可以为负(从后往前数下标)

​ 切割指定下标范围内的字符串,返回被切割的字符串

​ a = ‘123456789’ a.slice(‘4,7’) 得到的值为 567

​ 8.substr(a,b) 切割字符串 a表示切割的起始下标 b表示切割的长度

​ [a,length] a可以为负值

​ 切割从指定下标开始计算指定长度的字符串,返回被切割的字符串

​ a = ‘123456789’ a.substr(2,6) 得到的值为345678

​ 9.substring(a,b) 切割字符串 a表示切割的起始下标 b表示切割的结束下标

​ [a,b) a不可以为负数(但凡为负数,则从0开始) b不可以为负数

​ 切割指顶下标范围内的字符串,返回被切割的字符串

​ a = ‘123456789’ a.substring(‘4,7’) 得到的值为 567

3.链式写法

​ a.split().slice().substr().substring()

7.循环语句

1.for循环
1.结构

​ for(var i = 1; i < 11; i++){

​ console.log(i) // 循环体

​ }

2.语法

​ 第一句话 i = 1 声明变量进行赋值 循环的初始值

​ 第二句话 i < 11 循环体的终止条件

​ 第三句话 i++ 循环语句 一般来说是 自增/自减

3.执行顺序

​ 先执行第一句话赋值后代入第二句话进行判断,符合条件则执行循环内的语句/不符合条件则结束循环,执行完毕后代入执行第三句话进行运算,随后将运算完的值代入第二句话进行判断,符合条件则继续循环往下代入/不符合条件则结束循环。

2.while循环

​ while(条件){执行语句} 先判断后执行

​ while 循环会在指定条件为真时循环执行代码块

var a = 1;
while(a < 2) {
  a++;
  console.log(++a);
}
3.do…while循环

​ do{执行语句}while(条件) 先执行后判断

​ do…while循环语句是一定执行一次的

var a = 1;
do{
    a++;
    console.log(a);
}while(a < 5)

8.条件语句

1.if语句

​ if (条件) {

​ // 执行语句

​ }

​ if条件如果成立,则执行

2.if…else…语句

​ if的分支语句 if(如果)…else(否则)…

​ if(条件){执行语句}

​ else{执行语句}

​ if条件如果满足则执行;否则执行else里面的代码

3.多分支语句

​ else if() 但凡if不成立 则查找else if()里的条件 直到找到else{};

if () {
    
    }else if () {
              
              }else if () {
                        
                        }else {
    throw Error('代码报错')   //都查找不到时可以使用代码报错
}
4.switch case语句

​ switch (变量) {

​ case 变量: 执行语句; break;

​ case 变量: 执行语句; break;

​ default: 值;

​ }

语法:

​ 1.case 跟条件 之间必须加空格;

​ 2.case 变量后面必须加冒号

​ 3.执行语句之后 必须加 break

​ break 打断 强制跳出执行语句

​ default 默认值

5.三目运算符

​ 条件 ? 成立 执行语句 : 不成立 执行语句

​ 官方:条件 ? 真 : 假

// 三目运算符的嵌套使用
var a = 1;
a == 5 ? console.log(a) : 
     a == 4 ? console.log(a):
          a == 3 ? console.log(a):
               a == 2 ? console.log(a):
                    a == 1 ? console.log(a):
                         console.log('查无此数')

9.数字

​ Number 数字 1234567890 NaN和infinity为特殊值

​ 类型:整数 浮点数 长数 短数 非数值数字类型NaN(not a number)

​ 但凡整数的长度过长或过短,则会转换成指数形式显示

数值过大或者过小 亦或者是除以0 会显示为infinity或者-infinity;

​ 浮点运算并不总是100%准确

1.属性方法

​ 1.Number.toFixed(n) n为需要保留的小数位数 会将数据类型转换成String

​ 2.Number.parseInt() 将数据类型转换成整数 (只保留小数点前的数,不会进行四舍五入)

​ 3.Number.parseFloat() 将数据类型转换成浮点数 不会进行四舍五入

​ 转换类型的时候 如果需要转换成数字,必须从头部开始,但凡碰到非数字,停止解析,也不解析非数字,如果首位解析的值为非数字类型的数据类型,进行转换之后会解析成NaN

2.内置Math

​ Math 数学对象

1.属性

​ 1.Math.round() 四舍五入 取整值

​ 2.Math.ceil() 上舍入 小数点后为非零值的情况下向前一位进一

​ 3.Math.floor() 下舍入 取整数位 无论小数点后为任何值都舍弃

​ 4.Math.random() 0~1随机数 [0,1) ()内不能写值

2.取随机数范围

​ 需求范围为 [min,max]

​ 1.Number.parseInt(Math.random() * (max - min + 1)) + min;

​ 2.Math.floor(Math.random() * (max - min + 1)) + min;

​ 3.Math.ceil(Math.random() * (max - min)) + min;

​ 4.Math.round(Math.random() * (max - min)) + min;

10.数组

1.写法

数组内的子项可以为任何数据类型

​ 1.var a = [‘123’]

​ console.log(a); 输出的数据类型为数组

​ 2.var b = new Array(‘123’)

​ console.log(b); 输出的数据类型为对象

2.判断数据类型

​ 1.typeof 判断基础数据类型

typeof 能够判断的数据类型为 String Number Boolean undefined Symbol Function null会被判断为object

​ 2.instanceof 无法判断当前数据类型是否为数组还是对象

console.log(a instanceof Array)

​ 3.Object.prototype.toString.call() 判断具体数据类型

console.log(Object.prototype.toString.call(a))
3.概念

​ 数组是有长度的 length

​ 数组内的每一个子集称之为 子项

var a = [];   //空数组
var b = [123,'456',false,null,undefined,'1',2,4]  //一维数组
var c = [[[[...]]]] n维数组 n为嵌套几层的中括号
4.方法

以下方法均不会改变原数组

​ 1.concat 数组拼接 根据数组顺序进行子项的拼接

var a = [1,2,3,4,5]
var b = [[6]]
console.log(a.concat(b))  //[1,2,3,4,5,[6]]

2.filter 过滤筛选满足条件的子项,并返回新的数组,未找到则返回[]

Array.filter(function(currentValue,index,arr){})
数组中的每一项都会执行这个函数
var a = [1,2,3,4,5,6,7,8,9]
var b = a.filter(function(x){
    return x > 3
})
console.log(b)
currentValue-----当前数组的子项     必须写
index       -----当前子项的下标     可选
arr         -----当前子项属于的数组  可选
return      -----返回

3.forEach 专属遍历数组,返回所有的子项 空数组子项是不解析的

var a = [[1,2,3],[4,5,6],[7,8,9]]
a.forEach(function(i){
    i.forEach(function(j){
        console.log(j)
    })
})
多维数组用多层嵌套forEach循环进行遍历

​ 4.find() 查找第一次满足条件的子项,并返回子项,未找到则返回undefined

var a = [1, 2, 3, 4, 5, 10, 7]
var b = a.find(function (i, index) {
    return i > 6
})
console.log(b)  // 10

​ 5.findIndex() 查找第一次满足条件的子项,并返回子项下标,未找到则返回-1

var a = [1, 2, 3, 4, 5, 10, 7]
var b = a.findIndex(function (i, index) {
    return i > 6
})
console.log(b)  // 5

6.indexOf() 返回数组中检索指定子项第一次出现的位置(下标),如未找到,返回-1

​ 7.includes() 查找数组内是否含有当前指定的子项 查找其是否存在 返回布尔值

var a = [1,2,3,4,5,6]
console.log(a.includes('2'))  //  false
虽然返回的是布尔值 但是查找不进行判断所以不会进行隐式转换

​ 8.isArray() 判断当前对象是否为数组 返回布尔值

9.join(‘分隔符号’) 将数组内所有内容都转换成字符串 包括标点符号 join可以将对象的分隔符号进行替换 但凡不想保留分隔符号 则使用join(‘’);

​ 10.toString() 将任何数据类型转换成字符串 包括标点符号全部转换

​ 11.map() 映射 将数组内所有调用过的函数复制出来 与原数组不是同一个数组

以下方法均会改变原数组(除slice())

​ 12.sort(function () {return a-b/b-a}) 排序 数字从大到小排 b-a 从小到大排a-b 英文会按照首字母顺序排序 中文比较的是UTF-16值进行排序

​ 13.reverse() 将数组子项的顺序反转排列

14.push(n) 尾部添加内容n 返回新数组的长度

15.unshift(n) 头部添加内容n 返回新数组的长度

16.pop() 尾部删除 ()内不写内容 写了也没用 返回的是被删除的子项

17.shift() 头部删除 ()内不写内容 写了也没用 返回的是被删除的子项

18.slice() 切割数组 返回的是被切割的子项 -------------不会改变原数组

19.splice(a,b,c) 增删改查 a—指定切割的下标 b—切割的长度 c—切割替换的子项

​ 当数组被切割的时候,返回被切割的数组

20.some() 返回的是一个布尔值,只要数组中有一项符合条件(或者成立)即返回true,否则为false

21.every() 返回的是一个布尔值,只要数组中有一项不符合条件(或者不成立)即返回false,否则为true

5.补充扩散

1.将一个多个子项的数组转换成一个子项的数组

​ 第一种:

var cc = [
  [
    [1, 2, 3], [4, 5, 6], [7, 8, 9]
  ],
  [
    [11, 12, 13], [14, 15, 16], [17, 18, 19]
  ],
  [
    [21, 22, 23], [24, 25, 26], [27, 28, 29]
  ],
]
var aa = []
cc.forEach(function (a,b){
    a.forEach(function (c,d) {
        c.forEach(function (e,f) {
            aa[aa.length] = e
        })
    })
})
console.log(aa) // [1,2,3,4,5,6,7,8,9,11,12,13,14,15,16,17,18,19,21,22,23,24,25,26,27,28,29]

​ 第二种:

var cc = [
  [
    [1, 2, 3], [4, 5, 6], [7, 8, 9]
  ],
  [
    [11, 12, 13], [14, 15, 16], [17, 18, 19]
  ],
  [
    [21, 22, 23], [24, 25, 26], [27, 28, 29]
  ],
]
var aa = []
cc.forEach(function (a,b){
    a.forEach(function (c,d) {
        c.forEach(function (e,f) {
            aa.push(e)
        })
    })
})
console.log(aa)

2.将数组转换成数字

var bb = aa.join('')
console.log(Number.parseInt(bb))

3.将一维数组转换成多维数组

​ 第一种:用一个空数组接收新数组 用for循环去创造子项 配合splice切割放入数组

var a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 24, 25, 26, 27, 28, 29]
var c = a.map(function(y){
    return y
})
var b = []
for (i = 0;i < 3;i++){
    b[i] = a.splice(0,0)
    for (j = 0;j < 3;j++){
        b[i][j] = c.splice(0,3)
    }
}
console.log(b)

​ 第二种:先用一个空数组将映射出来的数组切割成要的最小子项,再用一个空数组接收二次切割的想要的子项即可,多层再嵌套

var a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 24, 25, 26, 27, 28, 29]
var c = a.map(function(y){
    return y
})
var b = []
for ( i = 0;i < a.length/3;i++) {
    b.push(c.splice(0,3))
}
var d = []
for ( i = 0;i < 3;i++){
    d.push(b.splice(0,3))
}
console.log(d)

11.onload

​ window.onload = function () {} 用于在网页加载完毕后执行操作 写js必写

​ window: 浏览器 所有的方法都在window内查找

​ onload: 事件 页面加载完毕

​ 作用:(同步,异步) 确保页面加载完成,不受js报错影响,确保dom节点能够获取到

获取id:document.getElementById(‘a’) a为id

12.变量

1.语法

​ 1.变量一定要先声明,后引用

2.变量存在变量提升,会将声明的变量往前放,但是赋值操作不会提升上去

2.类别

​ 变量目前所学区分为两种:函数(局部)变量/全局变量

​ 对于当前执行的函数来说,当前函数以外的变量称之为全局变量,当前函数以内的变量称之为函数(局部)变量,全局或是局部变量都不是绝对的 是相对而言的。

函数作用域:

​ 全局变量整个函数内部都可以调用,函数(局部)变量只有当前执行函数才能调用,外部无法调用。

​ 全局变量容易受内部调用产生污染改变原变量值,变量在内部调用发生改变后外部变量一样发生改变,反之亦然。

3.执行顺序

​ 优先查找内部是否有函数(局部)变量;再查找外部全局变量;直至最外层全局;

当变量名和函数名重名时,优先查找变量,找不到再匹配函数名,尽量不要让名字重复

13.函数

​ 函数但凡要执行,必须先产生调用 function函数名()

1.写法
1.命名函数

​ 1.var a = function () {} 调用查找a返回 function () {} a() 表示执行函数

​ 2.function a() {} 调用查找a返回 function a() {} a() 表示执行函数

a() 优先查找变量 但凡变量不是一个函数 则报错 变量如果是一个函数直接输出该函数

2.匿名函数

​ 必须通过特定的方式去调用

​ window.onload = function () {}

2.函数执行

​ 只要函数执行 不管函数在什么地方进行调用 只要全局能够找到就可以调用执行当前函数

​ 当函数产生调用时,只要调用的函数已经声明了函数,即使没有变量,也会直接执行函数

声明变量语句如果在函数后面,也不会影响到函数的执行(变量提升)

var i = 10
var b = 5
function a() {
    for (var i = 0;i < b;i++){
        d()
    }
    b++
}
function d() {
    console.log(i)
}
a()
//  输出为输出五次10
//  虽然d()函数调用是在for循环内部 但是d()函数是不会随着调用改变自身位置的,所以输出i的时候还是先在自身内部寻找,再往外寻找全局变量。
3.注意

​ 函数本身带有返回值 return 如果没有写该语句去定义 则默认值返回undefined

函数如果仅仅只是执行 返回也是存在的 只是并没有被输出/获取

f()() 表示执行f函数一次 再执行f()即f函数的返回值一次 而后f()执行完的返回值必须是函数 否则会报错

4.参数

​ 参数的作用:方便封装 封装:提供一种功能单一的方法 只需要改变写入的参数值即可使用 简单易懂

// 1.封装取值范围
function math(min,max) { // 函数本身的括号内写入的min,max等参数为形参 起存放作用 类似于定义了一个变量名称 需要规避关键字起名
                         // 函数内部调用的参数名称必须与形参名称一一对应 否则寻找变量
    var a = Number.parseInt((Math.random() * (max - min + 1)) + min)
    console.log(a)
}
math(50,100) // 函数调用内的括号写入的是具体的值 是实参 类似于给变量赋值 如果给的不是具体值则优先查找变量 随后查找函数 找不到则报错

​ 如果设置的函数的形参比实参少,多余的实参不会被接收。

​ 如果设置的函数的形参比实参多,多余的形参没有接收值,则默认为undefined。

// 2.封装从n加到m的值
(function (n,m) {
  var a = 0
  for (;n <= m;n++){
    a += n
  }
  console.log(a)
})()

arguments 只有函数才有的参数 函数的内置对象 接收所有形参的值 返回一个类数组 类数组是一个集合 不是单个的元素

var sum = 0
function a(a,b,c,d,e,f) {
    console.log(arguments)  // 输出所有形参的值形成一个类数组
    console.log(arguments.callee)  // 通过arguments.callee 指向函数本身
    for (i = 0;i < arguments.length;i++) {
        sum += arguments[i]
    }
    console.log(sum) // 将所有类数组内的子项相加的结果输出
}
a(1,2,3,4,5,6,7,8,9)
5.立即执行函数

​ 语法:运算符 + 函数() // ()也是运算符 将函数包裹起来即可

​ 规则:当代码执行到这一行时 直接立即执行该函数 执行完后直接销毁 无法二次调用

​ ps:立即执行函数也是匿名函数的一种

(function (形参) {
    执行语句
})(实参)

14.this

谁调用指向谁,最终指向为最终调用

​ 在对象内部,属性值中的this 一般情况下指向window

​ 属性值中的函数中的this 一般情况下指向对象本身

​ 属性值中的函数中的函数的this 一般情况下指向window

​ 改变this指向的方法:

​ 1.call

​ 2.apply

​ 3.bind

15.自行调试

​ 1.window.onload = function () {} 只能写一个 写多个只会执行最外层最下面的那一个

​ 2.当定义多个同名函数时 只会执行最后一个函数

​ 3.数组比较是将子项转换成数字逐一比较,全部子项满足条件则返回ture 只要有一个不满足即返回false

​ 4.一个函数只能有一个返回值return 当写多个return时只执行第一个

16.跳出关键字

​ 1.break 跳出 将循环打断 直接终止 后面代码不执行 跳出循环

​ 2.continue 跳出 将本次循环跳出,立即执行下一次循环

​ 3.return 跳出 专属于函数的跳出return,只作用于函数,跳出函数

17.DOM

​ DOM就是document

​ dom 文档节点(文档对象)

​ dom树

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-maZcH3jC-1660745683917)(JS.assets/Snipaste_2021-08-03_23-31-10.png)]

18.获取/选中元素

​ 1.document.getElementById() 通过获取id来得到元素

2.document.getElementsByClassName() 通过获取类名来得到元素的集合 类数组

​ 可以命名一个变量来接收这个类数组 如果要获取里面的元素 一定要使用下标获取

​ 集合:由一个或多个元素组成的类数组

​ 元素:类数组内的单个子项 也就是dom的一个节点叫做元素

​ 3.document.getElementsByTagName() 通过获取标签名称来得到元素的集合

​ 4.document.getElementsByName() 通过获取元素的name属性值来获取元素的集合

​ 之所以会是元素的集合 是因为类名 标签名 以及name属性值 可以同时有多个同名元素

​ 而ID值是唯一的 所以获取id得到的就是单个元素了

​ 5.document.querySelector() 匹配第一个符合条件的元素

​ 6.document.querySelectorAll() 匹配所有符合条件的元素

在下方的案例中 并未使用document.getElementById()去获取id值得到元素 而是直接给id名绑定点击事件

虽然也能实现效果 是因为这相当于给window写了一个同名变量去使用 但是这种写法并不推荐 单纯个人想偷懒

<body>
    <input type="checkbox" id="all_check">全选
    <input type="checkbox" id="no_check">全不选
    <input type="checkbox" id="reverse_check">反选
    <p>兴趣:</p>
    // 给每一个选项写上同一个点击事件
    <input type="checkbox" class="check" onclick="check()">打王者<br>
    <input type="checkbox" class="check" onclick="check()">吃饭<br>
    <input type="checkbox" class="check" onclick="check()">睡觉<br>
    <input type="checkbox" class="check" onclick="check()">打豆豆<br>
    <input type="checkbox" class="check" onclick="check()">看老蔡
</body>
<script>
    // 1.获取元素
    var all_checkbox = document.getElementsByClassName('check')
    // 2.1给全选绑定点击事件
    all_check.onclick = function () {
        // 3.for循环将类数组内的元素都选中
        for (i = 0; i < all_checkbox.length; i++) {
            // 4.让所有选项的选中状态与全选框的选中状态保持一致
            all_checkbox[i].checked = this.checked
            no_check.checked = false
            reverse_check.checked = false             
        }
        traverse(check_all)                   // 全选按钮功能内也能调用全选判断函数
    }
    // 2.2给全不选绑定点击事件
    no_check.onclick = function () {
        // 3.for循环将类数组内的元素都选中
        for (i = 0; i < all_checkbox.length; i++) {
            // 4.让所有选项的选中状态在点击全不选框后为未选中状态 false
            all_checkbox[i].checked = false
            all_check.checked = false
            reverse_check.checked = false
        }
        traverse(check_all)                   // 全不选点击事件内也能调用全不选判断函数
    }
    // 2.3给反选绑定点击事件
    reverse_check.onclick = function () {
        // 3.for循环将类数组内的元素都选中
        for (i = 0; i < all_checkbox.length; i++) {
            // 4.判断选项的选中状态是否为已选中
            if (all_checkbox[i].checked == true) {
                // 5.1 已选中的选项则改为未选中
                all_checkbox[i].checked = false
            } else {
                // 5.2 未选中的选项则改为已选中
                all_checkbox[i].checked = true
            }
        }
        all_check.checked = false
        no_check.checked = false
        traverse(check_all)                   // 反选按钮功能内也能调用全选判断函数
    }
    // 2.4给选项绑定点击事件
    function check() {
        // 3.for循环将类数组内的元素都选中 负责全选状态的for
        for (i = 0; i < all_checkbox.length; i++) {
            // 4.判断选项的选中状态是否为未选中
            if (all_checkbox[i].checked == false) {
                // 5.1 只要有选项的选中状态是未选中则将全选按钮的选中状态改为未选中
                all_check.checked = false
                // 5.2 break跳出 只要执行了改为未选中则跳出循环不再执行 可以实现判断所有选项的选中状态是否都为已选中状态
                break;
            } else {
                // 6.只有在所有选项的选中状态都为已选中时才会执行else 将全选按钮的选中状态改为已选中
                all_check.checked = true
            }
        }
        // 3.for循环将类数组内的元素都选中 负责全不选状态的for
        for (i = 0; i < all_checkbox.length; i++) {
            // 4.判断选项的选中状态是否为已选中
            if (all_checkbox[i].checked == true) {
                // 5.1 只要有选项的选中状态是已选中则将全不选按钮的选中状态改为未选中
                no_check.checked = false
                // 5.2也可以写与上方一样的代码实现当所有选项都未选中时全不选按钮的选中状态改为已选中 但是我觉得这个功能比较鸡肋
            }
        }
    }
    traverse(check_all)            // 当页面打开时就能执行选项全选/全不选判断函数
    // 函数封装
    function traverse(x) {
        // 遍历所有选项按钮
        for (var i = 0; i < all_checkbox.length; i++) {
            // 给选项绑定点击事件
            all_checkbox[i].onclick = function () {
                x()     // 函数调用
            }
        }
    }
    // 判断选项是否全选
    function check_all() {
        for (var j = 0; j < all_checkbox.length; j++) {
            if (all_checkbox[j].checked == false) {
                all_check.checked = false;
                break;
            } else {
                all_check.checked = true;
            }
        }
    }
    // 判断选项是否全不选
    function check_no() {
        console.log(1)
        for (var j = 0; j < all_checkbox.length; j++) {
            if (all_checkbox[j].checked == true) {
                no_check.checked = false
            }
        }
    }
</script>

19.获取/改变元素内容

​ ’.‘ 方法的拼接符号 表示**“的”**的意思

​ 1.innerHTML 当前元素中所有内容(包含标签)

​ 2.innerText 当前元素中所有文本(不包含标签,但是包含空格/换行)

​ 3.value 表单元素专属的属性

console.log(a[0].innerHTML = 修改内容)
console.log(a[0].innerText = 修改内容)
console.log(inp[0].value = 修改值)
// 改变元素内容 通过 = 来进行重新赋值改变

20.获取/改变元素属性

​ 1.attributes 针对元素的属性,获取元素的属性集合(类数组),通过下标/遍历 获取属性

​ 2.dom.getAttribute(a) a 需要查找的属性名称 返回对应的属性值

​ 3.dom.setAttribute(a,b) a 需要查找的属性名称 b 改变的值 返回值默认为undefined

​ 4.dom.style.属性名称 = 属性值 通过style方法设置的属性是 行内样式 获取的属性也是行内样式 如果元素并没有这条样式,则为空字符串

js 没有同时设置多条样式的方法 多条样式可以使用循环封装使用 jq可以

​ 5.dom.className 直接获取元素的类名

21.开关变量

​ var a = true/false 表示开关 暂时不会用

22.选项卡

<style>
    * {
        margin: 0;
        padding: 0;
    }

    .nav_box {
        width: 400px;
        display: flex;
    }

    .nav {
        border: 1px solid #000;
        flex: 1;
        text-align: center;
    }

    .content {
        width: 400px;
        height: 400px;
    }

    .content:nth-child(1) {
        background: blue;
        display: none;
    }

    .content:nth-child(2) {
        background: red;
        display: none;
    }

    .content:nth-child(3) {
        background: yellow;
        display: none;
    }

    .content:nth-child(4) {
        background: green;
        display: none;
    }
    .nav_box>.active{
        color: red;
    }
    .contents>.active{
        display: block;
    }
</style>
<body>
    <div class="nav_box">
        <div class="nav active">导航1</div>
        <div class="nav">导航2</div>
        <div class="nav">导航3</div>
        <div class="nav">导航4</div>
    </div>
    <div class="contents">
        <div class="content active"></div>
        <div class="content"></div>
        <div class="content"></div>
        <div class="content"></div>
    </div>
</body>
<script>
    // 获取所有类名为nav的元素
    var a = document.getElementsByClassName('nav')
    // 获取所有类名为content的元素
    var b = document.getElementsByClassName('content')
    // 遍历导航栏
    for (i = 0; i < a.length; i++) {
        // 存放下标!!!
        a[i].index = i
        // 给所有导航按钮绑定点击事件
        a[i].onclick = function () {
            // 再次遍历做清空样式(排他操作)作用
            for (j = 0; j < a.length; j++) {
                // a[j].className = 'nav'
                // 所有子项类名改回nav
                a[j].setAttribute('class','nav')
                // b[j].className = 'content'
                // 所有子项类名改回content
                b[j].setAttribute('class','content')
            }
            // this.className = 'nav active'
            // this表示当前点击元素 将当前点击元素的类名改为nav active
            this.setAttribute('class','nav active')
            // b[this.index].className = 'content active'
            // [this.index]当前点击元素的下标 让需要展示内容的下标与当前点击元素的下标保持一致 类名改为content active
            b[this.index].setAttribute('class','content active')
        }
    }
</script>

23.三级联动

<body>
    <select name="" id="pro"></select>
    <select name="" id="cit"></select>
    <select name="" id="are"></select>
</body>
<script>
    // 获取省元素
    var pro = document.getElementById('pro')
    // 获取市元素
    var cit = document.getElementById('cit')
    // 获取区元素
    var are = document.getElementById('are')
    // 定义省数组
    var a = ['江西省', '湖南省', '湖北省']
    // 定义市数组
    var b = [
        ['南昌市', '吉安市', '上饶市'],
        ['长沙市', '株洲市', 'c市'],
        ['武汉市', '宜昌市', 'c市']
    ]
    // 定义区数组
    var c = [
        [
            ['西湖区', '东湖区', '青山湖区'],
            ['吉安1', '吉安2', '吉安3'],
            ['上饶1', '上饶2', '上饶3']
        ],
        [
            ['a区', 'b区', 'c区'],
            ['a区', 'b区', 'c区'],
            ['a区', 'b区', 'c区']
        ],
        [
            ['d区', 'e区', 'f区'],
            ['d区', 'e区', 'f区'],
            ['d区', 'e区', 'f区']
        ]
    ]
    // 省市区调用
    lick(a, pro)
    lick(b[0], cit)
    lick(c[0][0], are)
    // 封装默认选项框函数
    function lick(x, y) {
        for (i = 0; i < x.length; i++) {
            // 创建生成option元素
            var aa = document.createElement('option')
            // 添加内容
            aa.innerHTML = x[i]
            // 添加value值 表单元素特有的 传给服务器(后端)的数据
            aa.value = x[i]
            // 添加dom节点
            y.appendChild(aa)
        }
    }
    // 获取省select元素下标   selectedIndex  专属获取表单下标
    var pro_index = pro.selectedIndex
    // 获取市select元素下标
    var cit_index = cit.selectedIndex
    // 省内容改变时调用函数(省市区联动)   onchange 当内容发生非JS改变时触发事件
    pro.onchange = function () {
        // 获取当前省内容元素下标
        pro_index = this.selectedIndex
        // 清空市下拉框内容
        cit.innerHTML = ''
        // 通过省元素下标对市元素产生联动
        lick(b[pro_index], cit)
        // 清空区下拉框内容
        are.innerHTML = ''
        // 通过省元素下标对区元素产生联动 默认为第0项
        lick(c[pro_index][0],are)
    }
    // 市内容改变时调用函数(市区联动)
    cit.onchange = function () {
        // 获取当前市内容元素下标
        cit_index = this.selectedIndex
        // 清空区下拉框内容
        are.innerHTML = ''
        // 通过省元素下标和市元素下标对区元素产生联动
        lick(c[pro_index][cit_index], are)
    }
</script>

dom生成方法:1.b.innerHTML += ' ’ + a[i] + ‘’

​ 2.var c = document.createElement(‘option’) // 生成option元素

​ c.innerHTML = a[i] // 添加内容

​ c.value = a[i] // value赋值

​ b.appendChild© // dom.appendChild(节点) ()内部必须是元素节点

​ 3.option专属 创建option对象 new Option(a,b) a—文本内容 b—value值

​ select.add© select专属添加option方法

24.Date

​ 内置对象 Date 日期 var a = new Date() 获取当前系统日期

​ 1.定时器 setInterval(function () {},time) 每间隔time时间,执行一次函数(循环执行)

​ setTimeout(function () {},time) 间隔time时间,执行一次函数(只执行一次)

​ time 单位ms 时间不写时 默认为0

setInterval(function () {            // 每隔1000ms执行一次函数
    var a = new Date()               // 获取当前系统日期,年月日时分秒星期
    var year = a.getFullYear()       // 获取日期中的年
    var month = a.getMonth() + 1     // 获取日期中的月  0~11 对应1~12 所以用+1来矫正
    var date = a.getDate()           // 获取日期中的日
    var day = a.getDay()             // 获取日期中的星期 0~6 从星期日开始计算第0天
    var hours = a.getHours()         // 获取日期中的时
    var minutes = a.getMinutes()     // 获取日期中的分
    var seconds = a.getSeconds()     // 获取日期中的秒
    var week = ['日', '壹', '贰', '叁', '肆', '伍', '陆']  // 设置一个星期数组用于转换星期几的中文
    var chinese = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'] // 设置0~9的繁体中文用于修改定时器内所有数字改成繁体中文
    function conversion(x) {   // 封装数字转换繁体中文的函数
        x = x.toString()       // 先将数字改为字符串
        var conver = ''        // 再声明一个空字符串用于接收转换的值
        for (i = 0; i < x.length; i++) {  // 循环遍历字符串
            conver += chinese[x[i]]       // 利用字符串下标逐个转换后叠加拼接而成
        }
        return conver                     // 返回转换好的值
    }
    var year_s = conversion(year)         // 声明变量接收调用函数后的返回值
    var month_s = conversion(month)
    var date_s = conversion(date)
    var hours_s = conversion(hours)
    var minutes_s = conversion(minutes)
    var seconds_s = conversion(seconds)
    day = week[day]                       // 将星期的数字转换为中文
    minutes = minutes >= 10 ? minutes : '0' + minutes   // 判断分的值是否大于等于10,小于10时进行补0操作
    seconds = seconds >= 10 ? seconds : '0' + seconds   // 判断秒的值是否大于等于10,小于10时进行补0操作
    var time = year_s + '年' + month_s + '月' + date_s + '日' + '<br>' + hours_s + ':' + minutes_s + ':' + seconds_s + '<br>' + '星期' + day                               // 将所有值拼接成想要的结果
    document.body.innerHTML = time        // 在body内添加拼接好的内容
}, 1000)                                  // 间隔1000ms

​ 2.时钟案例

<div class="box clearfix"></div>
* {
    margin: 0;
    padding: 0;
}

.box {
    width: 356px;
    height: 356px;
    border-radius: 50%;
    border: 10px solid black;
    position: relative;
    margin: 100px auto;
}

.box::after {
    display: block;
    content: "";
    width: 10px;
    height: 10px;
    background: red;
    border-radius: 50%;
    transform: translate(173px, 175px);
}

.box1 {
    position: absolute;
    left: 175px;
    top: 40px;
    width: 6px;
    height: 160px;
    background: #000000;
    transform-origin: 3px 140px;
}

.box2 {
    position: absolute;
    left: 174px;
    top: 60px;
    width: 8px;
    height: 140px;
    background: pink;
    transform-origin: 4px 120px;
}

.box3 {
    position: absolute;
    left: 173px;
    top: 80px;
    width: 10px;
    height: 120px;
    background: yellow;
    transform-origin: 5px 100px;
}

.li {
    position: absolute;
    top: 0;
    left: 177px;
    width: 2px;
    height: 6px;
    background: #000;
    transform-origin: 1px 178px;
}

.clearfix::after {
    content: "";
    display: block;
    clear: both;
}

.clearfix {
    zoom: 1;
}
window.onload = function () {                                  // 网页加载完毕执行事件
    var boxs = document.getElementsByClassName('box')          // 通过类名获取表盘元素集合
    boxs = boxs[0]                                             // 获取表盘元素
    for (i = 0; i < 60; i++) {                                 // 生成刻度循环
        var lis = document.createElement('div')                // 生成div
        lis.setAttribute('class', 'li')                        // 给生成的div赋予类名li
        boxs.appendChild(lis)                                  // 给表盘元素添加新生成的div
        lis.style.transform = 'rotate(' + i * 6 + 'deg)'       // 让每一次新生成的div旋转6度
    }
    var box1 = document.createElement('div')                   // 生成秒针
    box1.setAttribute('class','box1')
    boxs.appendChild(box1)
    var box2 = document.createElement('div')                   // 生成分针
    box2.setAttribute('class','box2')
    boxs.appendChild(box2)
    var box3 = document.createElement('div')                   // 生成时针
    box3.setAttribute('class','box3')
    boxs.appendChild(box3)
                                                               // 写在外面一个解决页面刚打开的时间问题
    var a = new Date()                                         // 获取时间戳
    var hours = a.getHours()                                   // 获取时
    var minutes = a.getMinutes()                               // 获取分
    var seconds = a.getSeconds()                               // 获取秒
    box3.style.transform = "rotate(" + (hours * 30 + (minutes / 60 * 30 + (seconds / 3600 * 30))) + "deg)"
                                                               // 时针旋转度数 受分针度数和秒针度数影响
    box2.style.transform = "rotate(" + (minutes * 6 + (seconds / 60 * 6)) + "deg)"
                                                               // 分针旋转度数 受秒针度数影响
    box1.style.transform = "rotate(" + seconds * 6 + "deg)"    // 秒针每秒旋转6度
    setInterval(function () {                                  // 写在定时器内部让时间每一秒都实时获取变化
        var a = new Date()
        var hours = a.getHours()
        var minutes = a.getMinutes()
        var seconds = a.getSeconds()
        box3.style.transform = "rotate(" + (hours * 30 + (minutes / 60 * 30 + (seconds / 3600 * 30))) + "deg)"
        box2.style.transform = "rotate(" + (minutes * 6 + (seconds / 60 * 6)) + "deg)"
        box1.style.transform = "rotate(" + seconds * 6 + "deg)"
    }, 1000)
}

​ 3.倒计时

var inp = document.getElementById('input')   // 通过ID获取input元素
inp.value = '点击获取验证码'                    // 设置value初始值
var i = 5                                    // 设置倒计时初始值
inp.onclick = function () {                  // 对按钮绑定点击事件
    inp.value = i + 's后重新获取'              // 设置刚点击时的转换值
    inp.setAttribute('disabled','true')      // 设置点击后启用disabled属性禁用用户操作
    var inp_s = setInterval(function () {    // 设置点击后启用的定时器
        i--                                  // 倒计时计算
        inp.value = i + 's后重新获取'          // 每秒都重新获取赋值
        if (i<1) {                           // 判断当时间小于1s后执行新样式
            clearInterval(inp_s)             // 定时器的暂停 当小于1s后停止
            inp.value = '点击重新获取'         // 小于1s后 按钮显示值
            inp.removeAttribute('disabled')  // 删除disabled属性 将按钮改回能够点击操作的样式
            i = 5                            // 对倒计时的值重新赋值 使得再次点击时又从开始值计算
        }
    },1000)                                  // 每间隔1s执行一次
}

​ 4.定时器的嵌套

​ 由于第1s产生的i会在第二秒进入内定时器再次计算 而并不是消失 所以每一秒都产生一个新的i进行计算 而旧的一直存在 所以输出值的个数会递增

​ 所以 定义了定时器之后一定要清空定时器 否则定时器会一直运算下去 增加浏览器负担

var i = 10
setInterval(function () {
  setInterval(function () {
    i--
    console.log(i);    // 第一次输出一个值 第二次输出两个值 一次递增 运行会越来越快
  }, 1000)
}, 1000)

​ 5.递归(自调用函数)

var i = 10
function a() {
  var b = setTimeout(function () {
    console.log(i);
    i--
    a()    // 每间隔100ms都会自己再次调用自身函数 实现递归操作
  }, 100)
  if (i < 1) clearTimeout(b)  // 设置好判断值停止掉自调用 清空定时器
}
a()

​ 6.循环语句内的定时器

var s = 0
for (var i = 10; i > 5; i--) {
  s += i
  setTimeout(function () {
    console.log(s); // 5个40   先执行完循环语句后 输出最后的值 循环几次输出几次
  }, 0);
}

25.同步与异步

​ 1.js 单线程 只能先处理一个任务,然后才能处理下一个任务 js代码是从上到下执行逐行进行读取的

​ 优点:执行精准,浏览器压力小。缺点:容易造成代码阻塞(报错/弹窗) 解决:使用异步代码

​ JavaScript之所以设计为单线程,这与它的用途有关。它作为浏览器脚本语言,主要用途是负责与页面的交互,以及操作DOM(添加,删除等),它只能是单线程的,否则它就会带来很复杂的同步问题。

​ 2.java 多线程 同一时间可以同时处理多个任务 一个线程挂掉并不会影响其他线程的执行

​ 优点:效率高,耗时短,有效避免代码阻塞

​ 3.同步:生活当中的异步 宏任务(主线程)

​ 4.异步:生活当中的同步 微任务(子线程) 必须等待所有的宏任务执行完,再依次执行微任务

​ 5.异步代码:回调函数 函数执行的返回值 事件循环 循环/定时器/onload

​ 优先级:同步>onload>异步 暂时先这么记 并不是绝对的

setTimeout(function () {
  console.log(1);
  setTimeout(function () {
    console.log(2);
  }, 0)
}, 1000)
window.onload = function () {
  setTimeout(function () {
    console.log(3);
  })
  console.log(4);
}
setTimeout(function () {
  console.log(5);
})     // 45312

26.对象

​ Object 万物皆对象

1.写法

​ 1.var a = {} 直接定义一个变量等于{}即可

var a = {
    age: 18,       // 键值对写法  属性: 属性值  每条属性之间用 , 隔开
    name: '小蔡',
    height: 185,
    width: 185,
}
  var a = function () {
    console.log(this);   // window
    console.log(b.b);    // undefined  对象并没有生成完 b.b中的this.c在window中创建了 但是并没有赋值 所以b.b为undefined
  }
  var b = {
    a: c,       // 找变量c  window中没有c变量 报错err
    b: this.c,  // 找window中的c  this.c在查找的同时在window中创建了c变量 所以是undefined
    e: b.c,     // 找对象的c    此时的c还未生成 找不到 报错err  只有当整个对象生成完毕后才能找到相对应的值
    c: function () {
      console.log(this);  // 对象中函数的this 此时指向对象本身
      v()
      function v() {
        console.log(this);   // 对象中函数的函数的this 此时的函数是在window中生成并由window.v()产生调用 所以指向window
      }
    }
  }
  b.c()

​ 2.var a = new Object() new 关键字生成的全部都是对象类型

​ 3.var a = function () {} 构造函数写法 这时a函数为对象

​ var aa = new a() 将函数实例化为对象 没有这一句 上面的就只是函数

2.获取值方法

​ 1.a.age 对象名.属性 获取相对应的属性值

​ 2.a[‘age’] 对象名[属性] 针对属性的数据类型是字符串时的方法

​ 3.如果想获得对象内所有属性的值 则使用for in循环 for (var x in a) 此时的x为所有的属性名 都转换为字符串类型了

​ 所以想要获得相对应的值时 需要使用 a[x]的写法获取 当属性值为函数时只能单独调用获取

​ 4.对象属性的增删查改 = 赋值操作就是直接添加/修改属性 a.age获取属性的操作就是查询属性

​ delete a.age 为删除相对应的属性

3.面向对象/面向过程

​ 1.面向过程 通过编写步骤 一步步逐步进行功能的实现 这整个流程就是面向过程编码

​ 2.面向对象 通过this 将对象实例化之后进行调用 不光可以实现对于单一功能的编写 也可以对多个功能进行组装 例如 拖拽功能

27.原型/原型链

​ 原型:prototype 显示原型 原型链:proto 隐式原型

​ 1.构造函数具有原型但没有原型链 函数的原型链指向Function的原型prototype

​ 实例化对象有原型链但没有原型

​ 2.构造函数的原型与实例化对象的原型链互相指向

​ 3.构造函数的原型的constructor指向函数本身 只有原型具备constructor 原型链不具备

​ 4.构造函数的原型的原型链与实例化对象的原型链的原型链互相指向

​ 5.Object的原型指向构造函数的原型的原型链同时指向实例化对象的原型链的原型链 Object是内置的构造函数 Object的原型的constructor指向Object函数本身

​ 6.Object的实例化对象的原型链指向构造函数的原型的原型链同时指向实例化对象的原型链的原型链

​ 7.Object的原型的原型链指向Object的实例化对象的原型链的原型链同时指向构造函数的原型的原型链的原型链同时指向实例化对象的原型链的原型链的原型链

​ 他们同时指向null

​ 8.原型和原型链中都没有原型,但是都有原型链

​ 9.Function函数具有原型和原型链 与Object函数的原型链以及构造函数的原型链 都指向Function的原型 且都相等

​ 10.Function的原型的原型链指向Object的原型[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bXAeU9b2-1660745683919)(JS.assets/Inkedparse构造函数_LI.jpg)]

28.查找dom节点

​ 1.parentNode 查找父级节点

​ 2.nextSibling 查找下一个同级节点 (空格 换行 都是节点)

​ 3.nextElementSibling 查找下一个同级元素节点

​ 4.previousElementSibling 查找上一个同级元素节点

​ ps: 查找所有的同级节点 jq有 js能通过循环实现

​ 案例1:群组及个人的点击添加及删除功能

    * {
        margin: 0;
        padding: 0;
    }

    ul,
    li,
    ol {
        list-style: none;
    }

    .box {
        width: 600px;
        height: 600px;
        display: flex;
        margin: 100px auto;
    }

    .box>div {
        flex: 1;
        border: 1px solid black;
    }

    ul {
        display: block;
    }
    <div class="box">
        <div class="left">
            <div class="zu">组1<span>+</span></div>
            <ul>
                <li>1</li>
                <li>2</li>
                <li>3</li>
                <li>4</li>
            </ul>
            <div class="zu">组2<span>+</span></div>
            <ul>
                <li>1</li>
                <li>2</li>
                <li>3</li>
                <li>4</li>
            </ul>
        </div>
        <div class="right"></div>
    </div>
    var left = document.getElementsByClassName('left')[0]                        // 通过类名获取左边大盒子
    var right = document.getElementsByClassName('right')[0]                      // 通过类名获取右边大盒子
    var zu = document.getElementsByClassName('zu')                               // 通过类名获取类名为zu的元素集合
    var span = document.getElementsByTagName('span')                             // 通过标签名获取span元素集合
    var left_li = left.getElementsByTagName('li')                                // 获取左边大盒子所有的li
    var right_li = right.getElementsByTagName('li')                              // 获取右边大盒子所有的li
    for (i = 0; i < left_li.length; i++) {                                       // for循环遍历所有左边的li  左边点击给右边添加功能
        left_li[i].setAttribute('rel', 1)                                        // 给左边的li添加开关rel 值为1代表开启
        left_li[i].setAttribute('xb', i)                                         // 给左边的li添加下标xb 值为i一一对应
        left_li[i].onclick = function () {                                       // 给左边的li绑定点击事件
            if (this.getAttribute('rel') == 1) {                                 // 判断当前点击元素的开关是否为开 是则执行以下代码
                var li = document.createElement('li')                            // 生成一个li标签
                li.setAttribute('xb', this.getAttribute('xb'))                   // 给新生成的li添加下标xb为当前点击元素的下标产生对应
                li.innerHTML = this.innerHTML                                    // 新生成的li的内容为当前点击元素的内容
                right.appendChild(li)                                            // 给右边的大盒子添加新生成的li
                this.setAttribute('rel', 0)                                      // 将当前点击元素的开关rel 关闭为0
            }
            for (j = 0; j < right_li.length; j++) {                              // for循环遍历所有右边的li  右边点击消失功能
                right_li[j].onclick = function () {                              // 给右边的li绑定点击事件
                    this.remove()                                                // 删除当前点击元素 remove()删除
                    left_li[this.getAttribute('xb')].setAttribute('rel', 1)      // 通过获取当前点击元素的下标与左边的li下标产生对应
                }                                                                // 然后将开关打开
            }
        }
    }
    for (i = 0; i < zu.length; i++) {                                            // for循环遍历所有类名为zu的元素  点击展开/收回操作
        zu[i].onclick = function () {                                            // 给所有类名为zu的元素绑定点击事件
            if (this.nextElementSibling.style.display == 'none') {               // 判断当前点击元素的下一个同级元素的display是否为none
                this.nextElementSibling.style.display = 'block'                  // 是则改为block
            } else {                                                             // 如果判断是否为block 点击的第一下判断没有效果
                this.nextElementSibling.style.display = 'none'                   // 不是则改为none
            }
        }
    }
    for (i = 0; i < span.length; i++) {                                          // for循环遍历所有的span 选中所有的+号  群组添加操作
        span[i].onclick = function (event) {                                     // 给所有的+号绑定点击事件
            event = event || window.event                                        // event对象事件兼容性处理
            event.stopPropagation()                                              // 阻止点击+号后的冒泡
            var a = this.parentNode.nextElementSibling.getElementsByTagName('li')// 将当前点击元素的父级的下一个同级内的所有的li赋值给变量
            for (j = 0; j < a.length; j++) {                                     // for循环遍历所有选中的li
                if (a[j].getAttribute('rel') == 1) {                             // 判断每一个li的开关rel是否为1开启状态 是则执行以下代码
                    var li = document.createElement('li')                        // 生成一个li标签
                    li.setAttribute('xb', a[j].getAttribute('xb'))               // 给新生成的li添加下标xb为所有选中元素对应的下标产生对应
                    li.innerHTML = a[j].innerHTML                                // 新生成的li的内容为所有选中元素对应的内容
                    right.appendChild(li)                                        // 给右边的大盒子添加新生成的li
                    a[j].setAttribute('rel', 0)                                  // 将所有选中元素的开关rel 关闭为0
                }
                for (x = 0; x < right_li.length; x++) {                          // for循环遍历所有右边的li 让点击+号以后也有点击删除操作
                    right_li[x].onclick = function () {                          // 给右边的li绑定点击事件
                        this.remove()                                            // 点击删除当前点击元素
                        left_li[this.getAttribute('xb')].setAttribute('rel', 1)  // 通过获取当前点击元素的下标与左边的li下标产生对应 
                    }
                }
            }
        }
    }

29.封装/多态/继承

1.多态

​ 概念:通过对传递的参数进行判断来执行逻辑语句,即可实现一种方法多态处理机制 使用对象的形式,通过不同的方法调用 实现不同的功能机制

  function add() {                          // 构造函数
    function a() {                          // 没有参数的方法
      return 10
    }
    function b(num) {                       // 一个参数的方法
      return 20 + num
    }
    function c(x, y) {                      // 两个参数的方法
      return x + y
    }
    this.sdd = function () {                // sdd方法用于判断参数个数决定执行的逻辑方法语句
      var arg = arguments,                  // arguments内置对象将所有的参数拿到
          len = arg.length                  // 再将拿到的所有参数的长度赋值给自定义变量
      switch (len) {                        // 判断函数个数执行语句
        case 0: return a();                 // 当case后面的执行语句为return时不需要break打断 return本身也是打断
        case 1: return b(arg[0]);
        case 2: return c(arg[0], arg[1])
      }
    }
  }

  var dd = new add()                        // 将函数进行实例化
  console.log(dd.sdd());                    
  console.log(dd.sdd(1));
  console.log(dd.sdd(1, 2));
2.封装
  function a(name,age) {
      var sex = 'man'           // 在函数内部重新定义的变量是私有化的变量  私有化的变量和方法只允许构造函数内部调用
      function run() {          // 在函数内部建立的命名函数是私有化的方法
          console.log('飞')
      }
      this.name = name          // 通过this将方法进行公有化
      this.age = age     
      this.say = function () {
          console.log(sex);     // 公有化的方法在函数内部自身调用私有化的变量是可以输出的
      }
      this.aa = function () {
          return run()          // 同样的私有化的方法也是可以找到的
      }
  }
  var aa = new a('小红',18)      // 可以对构造函数进行多次实例化
  var bb = new a('小明',14)      // 多个功能共有的属性 通过公有变量/方法执行   不同的地方则使用私有化变量/方法
  var cc = new a('小君',15)

1.闭包

  function f1() {
    var n = 999;
    nAdd = function () { n += 1 }
    function f2() {
      alert(n);
    }
    return f2;                        // 将内部的值通过return返回出去
  }

  var result = f1();                  // 外部接收return值
  result(); // 999                    // 从而达到外部调用函数内部的值
  nAdd();                             // 因为在外部进行调用后 才能执行函数内部的nAdd方法
  result(); // 1000
  result()

2.闭包实现封装

  function Cat(name, color) {
    this.name = name;
    this.color = color;
  }
  Cat.prototype.type = "猫科动物";                         // 在构造函数的原型上进行挂载变量/方法 也是私有化变量/方法的写法
  Cat.prototype.eat = function () { alert("吃老鼠") };    


  function peo(name, sex) {
    this.name = name
    this.sex = sex
  }
  peo.prototype.type = '哺乳动物';
  peo.prototype.eat = function () { alert("干饭") };

  var c = new peo('流星', '男')
  console.log(c);
3.继承

​ 子类继承父类

​ 当子类没有时才会继承父类的 如果有则直接调用子类直接的方法

  function Animal(name) {                       // 定义一个动物类 父类
    this.name = name || 'Animal';
    this.sleep = function () {
      console.log(this.name + '正在睡觉!');
    }
  }
  Animal.prototype.eat = function (food) {      // 原型方法  ---- 只允许在子类的原型上 去查找父类的方法
    console.log(this.name + '正在吃:' + food);
  };
  cat.prototype = new Animal('猫')              // 通过在子类的原型上对父类进行实例化挂载 可以将父类的原型上的所有方法继承在子类的原型上
  function cat() {
    Animal.call(this);                          // 通过父类的call改变this指向 将父类的私有/共有方法继承给子类
  }                                             // 同时使用原型实例化挂载和改变this指向 将整个父类完整的继承给子类
  var v = new cat()                             // 对子类进行实例化  不要将父类进行实例化
  console.log(v);

30.拖拽

1.获取拖拽需要的值

​ 1.event 事件对象 例如获取元素内鼠标的位置状态 需要使用兼容性代码 event = event || window.event

​ 2.event.clientX 获取鼠标距离页面左边的距离

​ 3.event.clientY 获取鼠标距离页面顶部的距离

​ 4.event.screenX 获取鼠标距离屏幕左边的距离

​ 5.event.screenY 获取鼠标距离屏幕顶部的距离

​ 6.clientTop 元素顶部的边框宽度

​ 7.clientLeft 元素左边的边框宽度

​ 8.clientWidth 获取元素内容的宽度(不包含边框)

​ 9.clientHeight 获取元素内容的高度(不包含边框)

​ 10.offsetTop 元素距离页面顶部的距离

​ 11.offsetLeft 元素距离页面左边的距离

​ 12.offsetWidth 获取整个元素的宽度(包含边框)

​ 13.offsetHeight 获取整个元素的高度(包含边框)

​ 14.offsetX 获取鼠标距离元素内容层左边的距离

​ 15.offsetY 获取鼠标距离元素内容层顶部的距离

​ 16.screenTop 浏览器窗口距离设备(屏幕)顶部的距离 screen都是window的属性 window.screenTop

​ 17.screenLeft 浏览器窗口距离设备(屏幕)左边的距离

​ 18.screen.width 设备分辨率的宽

​ 19.screen.height 设备分辨率的高

​ 20.scrollTop 元素距离顶部滚动的距离

​ 21.window.innerWidth 获取当前页面的宽度

​ 22.window.innerHeight 获取当前页面的高度

2.事件

​ 1.dom.onmousedown = function () {} 鼠标按压(点击下去)事件

​ 2.dom.onmousemove = function () {} 鼠标移动事件 拖拽主要由该事件实现效果

​ 3.dom.onmouseup = function () {} 鼠标抬起(松开)事件

​ 4.window.onscroll = function () {} 当滚动条滚动时触发事件

​ 5.window.onresize = function () {} 当浏览器窗口大小放生改变时触发事件

​ 6.dom.onmouseover = function () {} 鼠标移入元素时触发事件

​ 7.dom.onmouseout = function () {} 鼠标移出元素时触发事件

​ ps:事件分级为 写在行内的HTML事件 onclick这种直接操作DOM的0级事件 事件监听的2级事件

3.事件监听

​ 1.dom.addEventListener(‘mousedown’,function () {},false) 添加事件监听

​ 第一个参数为需要添加的事件名称 去掉’on’ 第二个参数为需要添加执行的函数(也可以是函数名) 第三个参数值为true(捕获)或false(冒泡)—默认

​ 2.dom.removeEventListener(‘mousedown’,function () {},false) 删除事件监听

​ 第一个参数为需要删除的事件名称 去掉’on’ 第二个参数为需要删除执行的函数(也可以是函数名) 第三个参数值为true(捕获)或false(冒泡)—默认

​ ps: 如果需要删除同一个事件同一个函数 则将函数写成共有的进行命名 然后在事件监听内写上该函数的名称

4.事件流

​ 1.冒泡 由内到外依次同时触发相同的事件机制

​ 2.捕获 由外到内依次同时触发相同的事件机制

​ 3.执行顺序 先进行捕获后进行冒泡

​ 4.阻止冒泡 event.stopPropagation()

​ 5.阻止默认行为 event.preventDefault()

5.拖拽案例
1.简单拖拽并判断临界值
    * {
        margin: 0;
        padding: 0;
        user-select: none;             /*禁止用户选中元素内容*/
    }

    .box {
        width: 100px;
        height: 100px;
        background: black;
        /* margin: 20px 0 0 50px;       使用定位产生位移进行拖拽操作后 将外边距及平移关掉 避免bug */  
        position: absolute;            /*绝对定位相对于首屏产生位移 方便计算 避免使用相对定位的bug*/
        top: 0;
        left: 0;
    }

    .box:nth-child(2) {
        background: pink;
    }

    .box:nth-child(3) {
        background: red;
    }
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>

​ 1.事件监听写法

    var box = document.getElementsByClassName('box')        // 通过类名获取所有盒子
    window.onresize = function () {                         // 监听浏览器窗口改变 当浏览器窗口改变时重新执行代码防止页面缩小后临界值判断有误bug
        for (var i = 0; i < box.length; i++) {              // 通过for循环给每个盒子都设置拖拽功能
            move(box[i])                                    // 每循环一次执行一次拖拽函数进行绑定  实参设置要绑定的元素
        }
    }
    function move(bb) {                                     // 实现拖拽的函数
        var aa = bb                                         // 声明变量获取传进来的元素
        var dx = 0                                          // 声明变量的初始值 方便调用 以免查找不到报错
        var dy = 0
        var bx = aa.offsetLeft                              // 获取元素距离页面左边的距离
        var by = aa.offsetTop                               // 获取元素距离页面顶部的距离
        aa.addEventListener('mousedown', function (event) { // 添加鼠标按压事件监听
            event = event || window.event                   // event事件对象的兼容性处理
            dx = event.clientX                              // 获取鼠标距离页面左边的距离
            dy = event.clientY                              // 获取鼠标距离页面顶部的距离
            bx = aa.offsetLeft                              // 对要获取的变量重新赋值  防止元素再次点击会返回原位的bug
            by = aa.offsetTop
            document.addEventListener('mousemove', a)       // 在鼠标按压后 添加鼠标移动事件监听 
        })                                                  // 将事件监听挂载到document 防止鼠标移动过快丢失元素的bug
        document.addEventListener('mouseup', function () {  // 添加鼠标抬起事件监听  任何时候都可以有抬起所以放在外面
            document.removeEventListener('mousemove', a)    // 在鼠标抬起后 删除鼠标移动事件监听
        })
        function a(event) {                                 // 要添加和删除的事件监听函数为同一个所以写成共有方法
            event = event || window.event
            var ndx = event.clientX                         // 获取鼠标移动过后 鼠标距离页面左边的距离
            var ndy = event.clientY                         // 获取鼠标移动过后 鼠标距离页面顶部的距离
            aa.style.left = ndx - dx + bx + 'px'            // 用移动后的鼠标位置 减去移动前的鼠标位置 得到鼠标到底移动了多少距离的值
            aa.style.top = ndy - dy + by + 'px'             // 再加上元素此时距离页面的位置 添加上单位 即可使元素每次点击都是从新位置拖拽
        }
    }

​ 2.面向对象写法

​ a — 所有的方法触发都挂载在原型上,同时调用则可以同时触发

​ b — 功能的单一化 一个函数只做一件事情

​ c — 功能的选择多元化 不需要重复触发 降低代码耦合性

    function move(a) {                                                   // 构造move函数 传入参数a
        var _this = this                                                 // 存this 使得调用方法时从move函数上调用
        this.box = document.getElementsByClassName(a)[0]                 // 获取传入参数值的元素
        this.dx = 0                                                      // 声明变量初始值 防止调用时查不到报错
        this.dy = 0
        this.bx = this.box.offsetLeft                                    // 获取元素距离页面左边的距离
        this.by = this.box.offsetTop                                     // 获取元素距离页面顶部的距离
        this.box.onmousedown = function (event) {                        // 添加当前元素的鼠标按压事件
            event = event || window.event                                // event对象事件的兼容性处理
            _this.down()                                                 // 调用move函数上的down方法
        }
        document.onmouseup = function () {                               // 给document添加鼠标抬起事件
            _this.up()                                                   // 调用move函数上的up方法
        }
    }
    move.prototype.down = function (event) {                             // 将down方法挂载在函数的原型上
        event = event || window.event                                    // event对象事件的兼容性处理
        var _this = this                                                 // 重新声明保存this指向move函数
        this.dx = event.clientX                                          // 获取鼠标按压时鼠标距离页面左边的距离
        this.dy = event.clientY                                          // 获取鼠标按压时鼠标距离页面顶部的距离
        this.bx = this.box.offsetLeft                                    // 对元素当前位置进行重新赋值 防止再次点击回归原点的bug
        this.by = this.box.offsetTop
        this.x = this.dx - this.bx                                       // 计算鼠标按压时鼠标距离元素左边的距离
        this.y = this.dy - this.by                                       // 计算鼠标按压时鼠标距离元素顶部的距离
        document.onmousemove = function () {                             // 给document添加鼠标移动事件 防止鼠标移动过快丢失元素的bug
            _this.move()                                                 // 调用move函数上的move方法
        }
    }
    move.prototype.move = function (event) {                             // 将move方法挂载在函数的原型上
        event = event || window.event                                    // event对象事件的兼容性处理
        var ndx = event.clientX                                          // 获取鼠标移动过后 鼠标距离页面左边的距离
        var ndy = event.clientY                                          // 获取鼠标移动过后 鼠标距离页面顶部的距离
        this.nx = ndx - this.x                                           // 计算鼠标移动后元素距离页面左边的距离
        this.ny = ndy - this.y                                           // 计算鼠标移动后元素距离页面顶部的距离
        if (this.nx <= 0) {                                              // 判断移动后元素距离页面左边距离是否小于等于0
            this.nx = 0                                                  // 是则将元素距离页面左边的距离一直保持为0
        } else if (this.nx > window.innerWidth - this.box.offsetWidth) { // 判断移动后元素距离页面左边距离是否大于页面宽度-元素宽度
            this.nx = window.innerWidth - this.box.offsetWidth           // 是则将元素距离页面左边的距离一直保持为页面宽度-元素宽度
        }                                                                // 用于判断元素是否到达左右临界
        if (this.ny <= 0) {                                              // 判断移动后元素距离页面顶部距离是否小于等于0
            this.ny = 0                                                  // 是则将元素距离页面顶部的距离一直保持为0
        }else if (this.ny > window.innerHeight - this.box.offsetHeight) {// 判断移动后元素距离页面顶部距离是否大于页面高度-元素高度
            this.ny = window.innerHeight - this.box.offsetHeight         // 是则将元素距离页面顶部的距离一直保持为页面高度-元素高度
        }                                                                // 用于判断元素是否到达上下临界
        this.box.style.left = this.nx + 'px'                             // 将计算好的元素距离页面左边的距离赋予给绝对定位的left
        this.box.style.top = this.ny + 'px'                              // 将计算好的元素距离页面顶部的距离赋予给绝对定位的top
    }                                                                    // 这样每次拖拽都是新的定位
    move.prototype.up = function () {                                    // 将up方法挂载在函数的原型上
        document.onmousemove = null                                      // 鼠标抬起时将document的鼠标移动事件清除掉 让鼠标抬起后放下元素
    }
    new move('box')                                                      // 将move构造函数进行实例化 传入参数'box'
2.正方形碰撞
    * {
        margin: 0;
        padding: 0;
        user-select: none;        /* 禁止用户选中 */
    }

    .box {
        width: 100px;
        height: 100px;
        background: black;
        position: absolute;
        top: 0;
        left: 0;
    }

    .box:nth-child(2) {
        background: black;
        top: 400px;
        left: 600px;
    }
    <div class="box"></div>
    <div class="box"></div>
    var box = document.getElementsByClassName('box')               // 通过类名获取所有盒子
    move(box[0],box[1])                                            // 第一个盒子移动碰撞第二个盒子的调用
    move(box[1],box[0])                                            // 第二个盒子移动碰撞第一个盒子的调用
    function move(x,y) {                                           // 将碰撞函数封装成move函数
        var aa = x                                                 // 获取移动盒子元素
        var bb = y                                                 // 获取参照盒子元素
        var dx = 0                                                 // 声明变量初始值 防止调用时查不到报错
        var dy = 0
        var bx = aa.offsetLeft                                     // 获取移动盒子距离页面左边的距离
        var by = aa.offsetTop                                      // 获取移动盒子距离页面顶部的距离
        aa.addEventListener('mousedown', function (event) {        // 给移动盒子添加鼠标按压事件监听
            event = event || window.event                          // event对象事件兼容性处理
            dx = event.clientX                                     // 获取鼠标按压时鼠标距离页面左边的距离
            dy = event.clientY                                     // 获取鼠标按压时鼠标距离页面顶部的距离
            bx = aa.offsetLeft                                     // 对变量进行重新赋值 防止再次点击时元素回到原点的bug
            by = aa.offsetTop
            document.addEventListener('mousemove', a)              // 对document添加鼠标移动事件监听 a为移动时执行的函数

        })                                                         // 任何时候都可以有鼠标抬起事件 所以与按压同级书写
        document.addEventListener('mouseup', function () {         // 对document添加鼠标抬起事件
            document.removeEventListener('mousemove', a)           // 当鼠标抬起后将document的鼠标移动事件监听清除掉 将元素放下
        })                                                         // 添加和删除事件执行同一个函数达到删除的目的
        function a(event) {                                        // 移动时执行的a函数
            event = event || window.event                          // event对象事件的兼容性处理
            var ndx = event.clientX                                // 获取移动后鼠标距离页面左边的距离
            var ndy = event.clientY                                // 获取移动后鼠标距离页面顶部的距离
            var x = ndx - dx + bx                                  // 计算鼠标移动后元素距离页面左边的距离
            var y = ndy - dy + by                                  // 计算鼠标移动后元素距离页面顶部的距离
            if (x <= 0) {                                          // 元素移动的临界值判断 与上一案例相同
                x = 0
            } else if (x >= window.innerWidth - aa.offsetWidth) {
                x = window.innerWidth - aa.offsetWidth
            }
            if (y <= 0) {
                y = 0
            } else if (y >= window.innerHeight - aa.offsetHeight) {
                y = window.innerHeight - aa.offsetHeight
            }
            aa.style.left = x + 'px'                                // 移动盒子的定位
            aa.style.top = y + 'px'
            b_t = bb.offsetTop                                      // 获取参照盒子距离页面顶部的距离
            b_l = bb.offsetLeft                                     // 获取参照盒子距离页面左边的距离
            b_b = b_t + bb.offsetHeight                             // 加上参照盒子的高度则为参照盒子底部距离页面顶部的距离
            b_r = b_l + bb.offsetWidth                              // 加上参照盒子的宽度则为参照盒子右边距离页面左边的距离
            if (aa.offsetTop + aa.offsetHeight <= b_t || aa.offsetLeft + aa.offsetWidth <= b_l || aa.offsetTop >= b_b || aa.offsetLeft >= b_r) { // 判断移动盒子底部<=参照盒子顶部||移动盒子右边<=参照盒子左边||移动盒子顶部>=参照盒子底部||移动盒子左边>=参照盒子右边
                aa.style.backgroundColor = 'black'                  // 满足其中一个则为不碰撞  将移动盒子的颜色改为黑色
                bb.style.backgroundColor = 'black'                  // 将参照盒子的颜色改为黑色
            } else {                                                // 四个条件都不满足则为已碰撞 则执行以下代码
                aa.style.backgroundColor = 'red'                    // 将移动盒子的颜色改为红色
                bb.style.backgroundColor = 'yellow'                 // 将参照盒子的颜色改为黄色
            }
        }
    }
3.球体碰撞
    var box = document.getElementsByClassName('box')
    move(box[0], box[1])
    move(box[1], box[0])
    function move(x, y) {
        var aa = x
        var bb = y
        var dx = 0
        var dy = 0
        var xy = 0
        var bx = aa.offsetLeft
        var by = aa.offsetTop
        var bx2 = bb.offsetLeft
        var by2 = bb.offsetTop
        var br = aa.offsetWidth / 2                                // 移动球体的半径
        var dr = bb.offsetWidth / 2                                // 参照球体的半径
        aa.addEventListener('mousedown', function (event) {
            event = event || window.event
            dx = event.clientX
            dy = event.clientY
            bx = aa.offsetLeft
            by = aa.offsetTop
            dx2 = bx + br                                          // 移动球体圆心距离页面左边的距离
            dy2 = by + br                                          // 移动球体圆心距离页面顶部的距离
            document.addEventListener('mousemove', a)
        })
        document.addEventListener('mouseup', function () {
            document.removeEventListener('mousemove', a)
        })
        function a(event) {
            event = event || window.event
            var ndx = event.clientX
            var ndx2 = bx2 + dr                                    // 参照球体圆心距离页面左边的距离
            var ndy = event.clientY
            var ndy2 = by2 + dr                                    // 参照球体圆心距离页面顶部的距离
            var x = ndx - dx + bx
            var y = ndy - dy + by
            var x2 = (ndx2 - dx2) ** 2                             // 获取两个圆心之间横向距离的平方  ** 表示次方
            var y2 = (ndy2 - dy2) ** 2                             // 获取两个圆心之间纵向距离的平方
            var xy = Math.sqrt(x2 + y2)                            // 两个平方相加后开根则得到两个圆心之间的直线距离 Math.sqrt()开根
            var R = br + dr                                        // 获取两个圆的半径和 用于判断是否碰撞
            if (x <= 0) {
                x = 0
            } else if (x >= window.innerWidth - aa.offsetWidth) {
                x = window.innerWidth - aa.offsetWidth
            }
            if (y <= 0) {
                y = 0
            } else if (y >= window.innerHeight - aa.offsetHeight) {
                y = window.innerHeight - aa.offsetHeight
            }
            aa.style.left = x + 'px'
            aa.style.top = y + 'px'
            if (Math.sqrt(((bb.offsetLeft + bb.offsetWidth / 2) - (aa.offsetLeft + aa.offsetWidth / 2)) ** 2 + ((bb.offsetTop + bb.offsetWidth / 2) - (aa.offsetTop + aa.offsetWidth / 2)) ** 2) >= R) {  // 这一长串的代码的值就是上面xy的值 这样书写可以实时获取发生改变
                aa.style.backgroundColor = 'black'        // 判断两圆心之间的直线距离是否大于等于两圆直径之和 是代表未碰撞将移动球体颜色改为黑色
                bb.style.backgroundColor = 'black'        // 将参考球体的颜色改为黑色
            } else {                                      // 不是则代表已碰撞 执行以下代码
                var col = '#'                             // 声明一个变量用于接收十六进制的颜色代码 #为开头
                for (var i = 0; i < 6; i++) col += parseInt(Math.random() * 16).toString(16); // 循环六次获取六次十六进制的随机数给col
                aa.style.backgroundColor = col            // 将移动盒子的颜色改为col随机颜色
                bb.style.backgroundColor = col            // 参考盒子的颜色也为col随机颜色 但是与移动盒子颜色相同 每一次移动的颜色都不相同
            }                                             // 实现碰撞变换不同颜色的操作
        }
    }

​ 知识点:toString(16)代表将获取的数值进行十六进制的转换

31.随机颜色

1.十六进制写法
function randomColor() {                // 六位十六进制写法
   var col = "#";                       // 声明变量用于接收值 #为十六进制开头
   for (var i = 0; i < 6; i++) {col+=parseInt(Math.random() * 16).toString(16)}; // parseInt(Math.random() * 16)获取0~15的随机整数
   return col;                          // toString(16) 将获取的随机整数进行十六进制转换后拼接给col并返回输出
}
function randomColor() {                // 八位十六进制写法
   var col = "#";                       // 比上面多了两次循环的两位数值  用于生成透明度
   for (var i = 0; i < 8; i++) {color+=parseInt(Math.random() * 16).toString(16)};
   return col;
}
2.rgb写法
function randomColor(){
     var col="rgb(";                     // 声明变量为rgb的开头rgb(
     for(var i=0;i<3;i++) {col+=parseInt(Math.random()*256)+","}; // parseInt(Math.random()*256)获取0~255的随机整数 加上逗号用于间隔rgb
     col=col.substring(0,col.length-1)+")";                     // 将最后一个逗号切割掉并添加结尾)
     return col;                                                // 返回获取到的随机颜色rgb值
 }
3.rgba写法
function randomColor(alpha){                  // 传入的参数为透明度的值
     alpha = alpha==undefined? (Math.random()).toFixed(1) : alpha; // 判断传入的值是否为und 是则取保留一位小数的随机数 不是则为自身
     alpha=Number(alpha);                     // 将传入的值转换成数字 转换不了则解析为NaN
     if(isNaN(alpha)) alpha=1;                // 判断传入的值是否为NaN 是则让该值为1
     var col = "rgba(";                       // 声明变量为rgba的开头rgba(
     for(var i=0;i<3;i++){                    // 循环三次生成rgb三个值
         col+=parseInt(Math.random()*256)+",";// 将获取到的0~255的随机整数用逗号隔开添加给rgba内
     }
     col+= alpha+")";                         // 在三个值都添加完后将透明度的值添加到末尾 并加上结束)
     return col;                              // 返回得到的rgba值
 }                                            // 因为透明度为0~1值所以需要单独赋值 并不能通过增加循环次数来实现

32.swiper

​ swiper是一款轮播图插件 插件就是将很多功能方法封装起来成一个文件 可以动态的添加调用

​ 要使用swiper插件需要先下载swiper.css与swiper.js并通过link和src将文件分别引入 根据书写的js标准使用不同版本的swiper 当前为es5所以使用swiper3

​ 学习es6后则使用swiper6 功能更为强大

​ 案例

    <link rel="stylesheet" href="../css/swiper.min.css"> /* 引入swiper的css文件 带有min为压缩文件*/
    <style>
    * {
        margin: 0;
        padding: 0;
    }

    .lj_ {
        width: 1000px;
    }

    img {
        width: 100%;
        display: block;
    }
    .lj_left {
        background: url(../images/icon-slides.png) no-repeat -90px 50%;
    }
    .lj_left:hover {
        background-position: -5px 50%;
    }
    .lj_right {
        background: url(../images/icon-slides.png) no-repeat -132px 50%;
    }
    .lj_right:hover {
        background-position: -50px 50%;
    }
    .lj_>.lj_fyq {
        text-align: right;
        width: 990px;
        padding-right: 10px;
    }
    .swiper-pagination-bullet{
        border: 2px solid #fff;
        background: rgba(0,0,0,.4);
        border-color: hsla(0,0%,100%,.3);  /* hsla(Hue,Saturation,Lightness,Alpha)  即hsla(色相,饱和度,亮度,透明度) css3新增*/
        opacity: 1;                        /* 色相H 色彩的基本属性 0~360 0或360为红色,120为绿色,240为蓝色*/
    }                                      /* 饱和度S 色彩的纯度或者饱和度 0~100% 值越高色彩越纯 越低则逐渐变灰暗 0%为灰色 100%为全色*/
    .swiper-pagination-bullet-active {     /* 亮度L 0~100% 值越大颜色向白色变化 越小则向黑色变化 0%为暗 50%为普通 100%为白*/
        background: hsla(0,0%,100%,.4);    /* 透明度 0~1 与rgba的a相同*/
        border-color: rgba(0,0,0,.4);
    }
    </style>
    <div class="swiper-container lj_">                                              // swiper-container为整个轮播图的容器
        <div class="swiper-wrapper">                                                // swiper-wrapper为放置滑块slide的容器
            <div class="swiper-slide"><img src="../images/20210817092910.png"></div>// swiper-slide为一个滑块 可以放任何东西甚至swiper
            <div class="swiper-slide"><img src="../images/20210817093034.png"></div>// 使用swiper插件写轮播图需要使用规定好的类名
            <div class="swiper-slide"><img src="../images/20210817093045.png"></div>// 否则引入的css文件找不到对应元素
        </div>
        <div class="swiper-pagination lj_fyq"></div>                                // swiper-pagination为分页器 轮播图下面一排小点
        <div class="swiper-button-prev lj_left"></div>                              // swiper-button-prev向前一级 左箭头
        <div class="swiper-button-next lj_right"></div>                             // swiper-button-next向下一级 右箭头
    </div>
<script src="../js/swiper.min.js"></script>         // 引入swiper的js文件 min为已压缩的文件  先引用后使用
<script>
    var mySwiper = new Swiper('.swiper-container', {// 要使用swiper则需要先初始化最外层容器 可以写多个用id或类名区分 但是必须保留初始类名
        pagination: '.swiper-pagination',           // 启用分页器 值写分页器的css选择器即可
        paginationClickable: true,                  // 让分页器点击后显示该滑块 默认为false 点击无效果
        nextButton: '.swiper-button-next',          // 启用右箭头
        prevButton: '.swiper-button-prev',          // 启用左箭头
        autoplay: 3000,                             // 自动轮播 值为轮播的间隔时间
        loop: true,                                 // loop为环路 true为启用环路 会在原有滑块的最前方和最后方复制若干个滑块 默认复制一个
        loopslides : 3,                             // 环路需要用到的滑块个数 解决前后白块问题
        slidesPerView:'auto',                       // 当前展示窗口内同时展示的滑块数量 可以为整数或者小数 小数不可loop
        effect: 'coverflow',                        // 滑块切换效果
        speed: 500,                                 // 效果实现时间
        centeredSlides: true,                       // 展示滑块居中显示
        spaceBetween: 20,                           // 每个滑块之间间隔的距离
        coverflow: {                                // 切换效果的对象
            rotate: 0,                              // y轴上的倾斜角度 越大越斜 0为平整 不会为90 默认50
            stretch: 0,                             // 滑块之间的距离 值越大越紧凑 默认0
            depth: 100,                             // 滑块在z轴上的缩放 值越大看起来越小 默认100
            modifier: 0.5,                          // depth和rotate和stretch的倍率,相当于depth*modifier、rotate*modifier、                                                                stretch*modifier,值越大这三个参数的效果越明显。默认1。
            slideShadows: false,                    // 关闭滑块阴影 默认true
        }
    })
    var span = document.getElementsByClassName('lj_fyq')[0].getElementsByTagName('span') // 获取所有的分页器按钮
    for (i=0;i<span.length;i++) {                   // 循环遍历分页器按钮
        span[i].style.backgroundColor = 'switch'    // 改分页器颜色为白色
    }
</script>

33.ES6

​ ECMAScript6版本以后 统称为ES6

​ ES6 默认为严格模式"use strict" 严格模式下的变量 必须先声明后使用

1.声明变量

​ 1.var 变量提升/变量声明后存储到window对象中/函数作用域/可以重复声明

​ 2.let 没有变量提升/变量存储到块级中/块级作用域/不可重复声明 var有的特点let都没有 这两种方法的共同点: 作用域内找不到变量则从外部查找

​ 3.const let有的特点const都有

​ const声明变量 必须赋值 const不可以重新赋值 所以一般用于定义不变的值(常量) 对于定义为的引用数据类型的值 是可以进行修改的

2.常量

​ 数字 字符串 布尔 数组数据类型为常量

3.unicode

​ 格式:\uxxxx 0000~FFFF 不同编码代表不同的值

4.for…of循环遍历

​ 遍历具备迭代器的数据类型的遍历方法

5.模板字符串

​ `` 就是模板字符串

​ ${} 字符串里面的变量 表达式 方法 函数都可以放在这里面

6.案例

​ 1.小米手机模块

    let a = [                                         // 将要用到的文本内容和图片路径放进数组内方便拿取
        {
            img: '../images/1.webp',
            p1: 'Xiaomi Mix 4',
            p2: 'CUP全面屏',
            price: '4999元',
            img2: '../images/2.webp',
        }
    ]
    let b = `                                         // 使用模块字符串去写html结构
    <div class="box">                                 // 单个手机的模块内容
        <img src="${a[0].img}">                       // ${a[0],img} 获取所需要的值
        <h3>${a[0].p1}</h3>
        <p class="p1">${a[0].p2}</p>
        <p class="p2">${a[0].price}</p>
    </div>
    `
    let c = `
    <div class="cont">                                // 最外层容器
        <div class="big">                             // 放置大图
            <img src="${a[0].img2}">
        </div>
        <div class="boxs">                            // 所有手机模块的容器
        </div>
    </div>
    `
    document.body.innerHTML = c                       // 将写好的外部容器放入body内
    let d = document.getElementsByClassName('boxs')[0]// 获取放置所有手机模块的容器
    for (i = 0; i < 8; i++) {
        d.innerHTML += b                              // 在所有手机模块容器内 每循环一次添加一个单个手机模块内容
    }

​ 2.99乘法表

    for (i = 1; i < 10; i++) {
        for (j = 1; j <= i; j++) {
            document.write(`${j} * ${i} = ${i}*${j}`)
        }
        document.write('<br>')
    }
7.解构赋值

​ 将数据结构拆解后赋值 达到直接拿取的目的

​ 解构赋值时 解构内容与被解构的数据结构必须完全一致才能进行解构 否则会报错

每执行一条语句加分号 声明变量尽量加分号 否则后面的内容都会被当成赋值进行操作

​ 解构内容有默认值时又未赋值时输出默认值 赋值为undefined时也是输出默认值

1.数组解构

​ 被解构的数据必须也是 数组类型||可迭代的类型

    let a = 1;
    let b = 2;
	{[a,b] = [b,a]}         // 将a,b值互换
2.对象解构

​ 语法:let { } = …

​ 1.变量名称与属性名称必须一致 才会进行解构 否则会在对象中添加一个与变量名称相同的新属性名 但是是未赋值的 所以输出为undefined

​ 2.对象中的方法进行解构后 直接调用方法名称就可以执行

​ 3.取别名 属性名:别名 取别名之后再调用就只能使用别名调用了 原有属性名是查找不到该方法的 而取别名是为了防止不同对象中属性重名时解构的调用报错

​ 对对象解构的同时 将属性名称进行取别名的操作形成下意识行为

​ 4.内置方法

​ 直接将call从toString中解构是找不到的 toString是Object原型上的方法 但它是对象形式的 所以call在Object的原型链上 从原型链上解构可以找到

  let { toString } = Object.prototype   // 将toString方法从Object的原型中解构出来
  console.log(toString.call([]));       // 直接对toString进行call方法调用

​ 5.注意:如果是对单一对象进行解构时 不能直接等号解构 {} = {} 每一个{}都被解析为一个代码块 代码块 = 代码块报错 需要用括号括起来 ({} = {})

3.字符串解构

​ 解构内容与字符串的下标一一对应赋值即可

4.数字解构

​ 解构数字时会将数据类型视为对象类型的数字类型 等同于let num = new Number() 所以可以按对象解构的方法解构

5.函数解构

​ 是对函数的参数进行解构 实参与形参的解构必须一致才能传入

6.()的问题

​ 1.变量声明语句内的解构内容不允许使用()

​ 2.函数的参数不允许使用() 因为它也是变量声明模块

​ 3.等号左边的解构内容进行赋值时不允许使用() 因为会导致左右结构不一致无法解构

8.函数扩展

​ 在es6中使用箭头函数

​ 语法:let a = () => {} a为函数名称 ()为存放形参 {}存放执行语句 当形参只有一个的时候 可以省略() 如果没有写{} 后面的第一条语句为执行语句 并且默认return

​ 在形参中写= 表示赋予默认值

​ 1.箭头函数没有arguments 但是有 … 扩展运算符 将剩余参数都放入紧跟的形参内形成类数组 …a必须是最后一个形参 否则报错

​ 2.箭头函数没有自己的this指向 它的this指向声明函数的时候所在作用域的this

​ 3.因为没有自己的this指向 也就形成不了构造函数 也就不存在实例化对象

小贴士

​ 用于对接口的调用 测试代码执行成功或失败

​ try {

​ 成功的代码

​ } catch {

​ 失败的代码

​ }

new

​ new帮我们做了这样几件事:

​ 1.帮我们创建了一个空对象,例如obj;

​ 2.将空对象的原型链指向函数的原型

​ 3.利用函数的call方法,将原本指向window的绑定对象this指向了obj。传递实参时,对象的属性会被挂载到obj上

​ 4.利用函数返回对象obj

function Foo(name) {
    this.name = name;
    return this;
}
var obj = {};
obj.__proto__ = Foo.prototype;
// Foo.call(obj, 'mm');
// 1.this.name = name; 通过函数的call方法将this指向obj,实参传入后 this.name = 'mm' 那么obj.name = 'mm' 也就是说name属性被挂载到obj对象上了
// 2.return this;  就是return obj,这样Obj这个对象就被返回出来了  将结果赋值给变量foo
var foo = Foo.call(obj, 'mm');
console.log(foo);

​ 上下两部分代码执行出来的结果完全一致

function Foo(name) {
    this.name = name;
}
var foo = new Foo('mm');
console.log(foo);
9.字符串新增方法

​ 1.includes() 查找指定的字符串是否存在 返回布尔

​ 2.startsWith() 查找指定的字符串是否在头部 必须完全一致 返回布尔

​ 3.endsWith() 查找指定的字符串是否在尾部 必须完全一致 返回布尔 空格符也算

​ 4.repeat(n) 复制 n为复制次数 0为不复制为空 1为复制一次就是原有字符串 小数则取整(不进行四舍五入) n不可以为负 改变原字符串

​ 5.replaceAll(a,b) 替换所有匹配的字符串 a为指定字符串 b为替换为的值

10.数值扩展

​ 1.Number.isFinite() 判断参数是否为有限的(finite) 返回布尔 如果参数不是数值(不进行隐式转换) 一律返回false

​ 2.Math.sign() 判断参数是正数(返回+1)、负数(返回-1)、零(返回0)或负零(返回-0)。会进行隐式转换 无法转换的值返回NaN

​ 3.Math.sqrt() 计算开平方(会隐式转换)

​ 4.Math.cbrt() 计算开立方(会隐式转换)

​ 5.BigInt数据类型 大整数 由于位数过多数值过大后js无法保证数值的精准度 BigInt用于精确显示数值过大的数 为了区分在数值后面加n区分

11.数组扩展

​ 1.扩展运算符 (…)

​ a.复制数组

    let a = [1,2,3]
    let b = a
    // 以上写法仅仅只是将b的指向地址与a的指向地址相同 修改b数组同样也会将a数组改变
    let c = [...a]

​ b.合并数组

    let a = [1,2]
    let b = [3,4]
    let c = [5,6]
    let d = [...a,...b,...c]         // [1,2,3,4,5,6]

​ c.解构数组

    let [first,...rest] = [1,2,3,4]  // ...rest  2,3,4

​ d.解构字符串

    let a = 'hello'
    let b = [...a]                   // ['h','e','l','l','o']

​ e.将类数组转换成真正的数组

	let a = document.querySelectorAll('div')
    let b = [...a]                

​ f.Map/Set Map本质是键值对的集合 是一个存储类似于键值对形式的二维数组的数据格式

    let map = new Map([
        [1,'name'],
        [2,'sex'],
        [3,'age'],
    ])
    let arr = [...map.keys()]        // [1,2,3]  获取map所有的键名
    let value = [...map.values()]    // ['name','sex','age']  获取map所有的键值
    let entries = [...map.entries()] // [[1,'name'],[2,'sex'],[3,'age']]  获取map所有的键值对

​ 2.Array.isArray(a) 判断a是否是数组 返回布尔值

​ 3.Array.from(a) 将a的数据类型转换成数组 a是类数组 || 可遍历的对象

​ 4.Array.of(a,b,c) 将数据a,b,c转化成一个新数组[a,b,c] 类似于new Array(a,b,c)的功能

​ 5.a.copyWithin(a,b,c) 针对数组进行复制 会改变原子项的内容

​ a–从a处开始进行替换(必须,可为负值,为负从后开始倒数)

​ b–从b出开始读取 默认为0 如果为负值 则从末尾开始计算

​ c–到c处结束 默认等于数组长度 如果为负值 表示从末尾开始计算

​ 6.a.fill(b,c,d) 填充数组 会改变数组的内容 填充数组a 填充内容b 填充范围下标[c,d) 只写一个值则为全部替换 a数组内的空子项会被填充

    [1,2,3,4,5,6].copyWithin(0,3)    // [4,5,6,4,5,6] 从第0项开始替换 从第3项开始读取为4,5,6 将1,2,3替换为4,5,6

​ 7.for of

	for (let key of a.keys()) {       // 获取a的键名
        console.log(key)
    }
	for (let value of a.values()) {   // 获取a的键值
        console.log(value)
    }
	for (let entries of a.entries()) {// 获取a的键值对
        console.log(entries)
    }

​ 8.a.flat(n) 将a数组扁平化,降维 n默认为1 拉平一层 如果需要拉平未知的所有层数 则使用Infinity

	[1,2,[3,[4,[5,6]]]].flat(3)       // [1,2,3,4,5,6]

​ 9.a.flatMap() 将a数组内部的每一个子项进行函数的处理后映射出来成为一个新的数组 flatMap默认只拉平一层数组

​ 10.空位问题 a.forEach(),filter(),reduce(),every()和some()都会跳过空位

​ b.map()会忽略空位进行映射但是会保留这个值

​ c.join()和toString()会将空位视为undefined,而undefined和null会被处理成空字符串。

​ d.es6方法明确将空位转换为undefined

12.深拷贝/浅拷贝

​ 浅拷贝 会将原数据进行改变 直接赋值也是浅拷贝的一种

​ for…in 只拷贝第一层所有的数据

​ Object.assign 合并对象 也是将对象进行拷贝(地址指向一致)

​ 深拷贝 拷贝所有层的数据

​ …扩展运算符

​ Array的slice和concat方法

​ Json对象中的parse和stringify 先Json.stringify()将对象、数组转换成字符串 再Json.parse()将字符串转成json对象

​ 递归实现深拷贝

  function deepClone(obj) {                               //定义一个deepClone函数 传入一个obj参数
    let objClone = Array.isArray(obj) ? [] : {};          //定义一个变量接收 判断传入的参数是否为数组 是则返回一个空的数组 不是则返回空对象
    if (obj && typeof obj === "object") {                 //判断参数是否存在 并且 参数的数据类型为对象 用以排除null 则执行以下代码
      for (key in obj) {                                  //for in遍历获取每个参数的key值
        if (obj.hasOwnProperty(key)) {                    //判断参数内是否存在key值 返回布尔
          if (obj[key] && typeof obj[key] === "object") { //完全判断当前参数是否为对象 是则递归复制
            objClone[key] = deepClone(obj[key]);          //将空数组/对象的[键] 传入当前函数再一次遍历获取对应的子元素
          } else {
            objClone[key] = obj[key];                     //如果不是,简单复制
          }
        }
      }
    }
    return objClone;                                      //返回判断完后的数组
  }
13.对象扩展

​ 1.简洁表示法 当对象的属性名与属性值一致的时候 可以省略属性值

​ 当对象的属性值为函数的时候 可以省略:function 直接写成 a() {}

​ 2.属性名表达式 在es6中对象的属性名称可以是表达式形式 表达式的结果为真正的属性名

​ 3.可枚举性 对象的每个属性都有一个描述对象,用来控制该属性的行为。

​ 通过Object.getOwnPropertyDescriptor(对象名,属性名)方法可以获取该属性的描述对象

​ // configurable: true true 则表示可以被修改 false则表示只读

​ // enumerable:true 可枚举 false表示不可继承 true则表示可以继承

​ // value: 123 值

​ // writable: true 可以修改

​ 4.Object.assign(obj1,obj2…) 将多个对象合并到第一项对象中

​ 合并对象中的属性名称如果相同则会被后面的覆盖 所以合并对象的时候要将相同属性名称改为不一样的属性名称

​ 如果参数不是对象,则会自动将参数转换成对象类型

​ 还可以克隆对象

14.class

​ class关键字 定义类名 函数类型

​ 在类{}的内部 定义的所有方法均挂载在原型上

​ constructor 定义方法(属性) 类一定含有constructor方法 如果没有人为定义则会自动添加一个空的constructor方法 一旦将类进行实例化 则自动调用该方法

​ 所有属性写在constructor()内部 一般在只有在constructor内 才可以使用this指向类本身 其他方法内的this都指向undefined

​ extends 继承 语法:子类 extends 父类

​ super 父类的构造函数 既可以当作函数使用,也可以当作对象使用

​ 必须在constructor中使用

​ super作为对象的时候,指向的是父类的原型

​ super一旦进行赋值操作,这个时候就不再指向父类的原型 而是this

​ static 静态方法 私有的方法 作为类的继承 static静态方法必须通过类的方法进行调用 而不是实例化对象

​ 静态方法在本身类内部通过类的方法进行调用 在本身以外的类只能通过该类的静态方法来调用静态方法 类的继承时 父类的静态方法也会被继承

​ 类的原型的构造器指向类本身 A === A.prototype.constructor

​ 类的构造器指向大F A.constructor === Function

​ 子类的原型链指向父类 a.proto === A 表示构造函数的继承 总是指向父类

​ 子类的原型的原型链指向父类的原型 a.prototype.proto === A.prototype 表示方法的继承 总是指向父类的原型

​ 实例化对象的constructor指向类的原型的constructor 指向类本身

15.Symbol

​ Symbol 唯一标识 字符串 基础数据类型

​ 作用:获取属性名 唯一的属性 不跟任何属性名产生冲突

​ Symbol.for(‘’) 将多个变量的symbol指向 改为希望指向的同一个地址 symbol接受一个字符串作为参数 查找有没有同名symbol值 如果有则直接使用该值

​ 没有则新建一个该字符串的symbol值 并将其注册在全局内

​ Symbol.keyFor() 查询指定变量是否注册了symbol值 注册则直接输出该值 否则输出undefined

  let s1 = Symbol.for("foo");
  Symbol.keyFor(s1) // "foo"

  let s2 = Symbol("foo");
  Symbol.keyFor(s2) // undefined
16.Mixin

​ 将多个对象进行合并 进行调用时,可以统一化

​ 如果全局调用对象,会影响原对象

​ 如果已经进行了合并之后 哪里需要使用该对象 则哪里调用对象的方法

​ 哪里即便修改了对象的方法,也不影响整合后的 对象里面的方法

  function mix(...mixins) {                           // 获取所有的mixins值整合成一个数组
    class Mix {                                       // 定义一个Mix类
      constructor() {                                 // 默认执行
        for (let mixin of mixins) {                   // 循环遍历整个数组
          copyProperties(this, new mixin());          // 拷贝实例属性
        }
      }
    }
    for (let mixin of mixins) {                       // 循环遍历整个数组
      copyProperties(Mix, mixin);                     // 拷贝静态属性
      copyProperties(Mix.prototype, mixin.prototype); // 拷贝原型属性
    }
    return Mix;                                       // 返回Mix对象
  }
17.Promise

​ Promise对象是异步编程的一种解决方案,是用于解决回调地狱问题的方法

1.promise的状态

​ promise一共有三种状态 pending等待 fulfilled成功 rejected失败 状态只能由 pending–>fulfilled 或者 pending–>rejected

状态一旦发生改变 则不会再变化 任何时候都可以获取到状态改变后的结果 且状态发生改变后是不可逆的 也无法修改

2.语法:

​ new Promise((res,rej) => {})

3.参数

​ promise中的两个参数 第一个参数一定是resolve() 状态发生改变成为成功状态 第二个参数一定是reject() 状态发生改变成为失败状态

​ 即使参数的名称写成(reject,resolve) 也是第一个为成功 第二个为失败 为了方便阅读 我们将第一个参数定为res 第二个为rej 来表示成功和失败

​ res和rej都是内置的函数方法 需要进行()调用才会发生状态的改变 ()内为发生状态改变后传入的参数

​ new Promise的返回的结果 是promise对象

  let p1 = new Promise((res,rej) => {
      res(1)
  })
  let p2 = new Promise((res,rej) => {
      rej(p1)                         // 当发生状态改变时传入的参数为另一个promise对象的返回结果时 当前p2对象的状态由p1决定
  })                                  // 即使在p2中使用rej来改变状态 p2的状态依然是p1的状态res成功

当promise对象的状态改变为失败时 即使不使用.then()或者.catch()进行接收 浏览器依然会将该错误进行抛出

4.promise的缺点

​ promise一旦新建立即执行 无法中途取消。

​ 如果不设置回调函数进行接收 promise内部抛出的错误则不会输出到外部

​ 当处于pending状态时,无法得知目前执行到哪一个阶段(刚刚开始还是即将完成)

5. .then()

​ 语法:promise对象.then() 表示下一步操作

​ then函数用于接收promise对象的返回结果 只有当promise对象发生状态的改变时 才会执行.then()方法

​ .then(res,rej) then函数内部也是两个可选参数 第一个是promise执行成功时执行的回调函数 第二个是promise执行失败时执行的回调函数

.then()内是没有res和rej方法的 只有两个函数作为接收res或者rej的回调 如果直接在then方法内使用res或者rej方法会直接报错

​ 只写一个函数时 无法捕获到promise对象中rej或者报错的代码

new promise()是一个同步代码 而.then则是异步代码 但是因为它是回调函数 所以优先级高于异步代码

6. .catch()

​ 只用于捕获promise中的错误

​ 其实相当于 .then(null,rej) then函数的一个函数为空 只写第二个函数

7. .finally()

​ 只要promise状态发生改变 就会执行.finally()方法

​ .finally()不接收promise发生状态改变后传入的参数 不论promise状态怎么改变都不会影响到.finally() 且必定会执行一次finally函数

8.ansyc/await

​ 通过ansyc所定义的函数 在本质上与普通函数没有任何区别

​ 但是 通过ansyc定义的函数 其返回值为promise对象 即使在ansyc函数内使用return 也不会改变返回值为promise对象这件事

​ return的值会作为promise对象发生状态改变时传入的参数进行使用

await后面接异步代码才会执行生效 await只能在ansyc函数内部使用 当代码执行到await时 一定要执行完await的执行代码 才会执行下一行代码

​ await后面接的promise对象必须发生状态的改变 或者异步代码执行完 才算是将await后面的代码执行完毕 否则代码不会继续执行

​ 如果await后面接的不是promise对象 则与普通函数的代码没有任何区别

  async function async1() {       // 定义函数async1
    console.log('async1 start')   // 同步代码3 第二个输出 async1 start
    await async2()                // async函数内遇到await必须执行完毕才能继续执行 调用async2()函数 此时状态已经发生改变 async1函数执行完毕
    console.log('async1 end')     // 此时的console.log()等同于.then() 回调函数1 第六个输出async1 end
  }
  async function async2() {       // 定义函数async2
    console.log('async2')         // 同步代码4 第三个输出 async2
  }
  console.log('script start')     // 同步代码1 第一个输出 script start
  setTimeout(function () {        // 异步代码 在所有的同步代码以及回调函数执行完毕后执行
    console.log('setTimeout')     // 第八个输出 setTimeout
  }, 0)
  async1();                       // 同步代码2 执行async1函数
  new Promise(function (resolve) {// new promise为同步代码
    console.log('promise1')       // async1执行完毕后执行同步代码5 第四个输出 promise1
    resolve();                    // 发生状态改变为成功 执行.then回调函数
  }).then(function () {           // 回调函数高于异步代码的执行  回调函数2
    console.log('promise2')       // 第七个输出 promise2
  })
  console.log('script end')       // 同步代码6 第五个输出 script end

以上案例的重点在于 await执行完毕后 后面紧跟着的console.log已经变成等同于回调函数的代码 所以按照同步高于回调 回调高于异步的代码执行顺序进行执行

9. .all()

​ 将括号内所有的参数合并成一个新的promise 传入的参数全部都是promise实例 如果不是 则会通过Promise.resolve()转换成Promise实例

​ 传参时的形式 必须以含有迭代器接口的数据类型传入 即将所有的参数写成数组形式 或者字符串形式 一般为数组形式 方便阅读

​ 根据所有的promise的状态 以与的形式 来决定新合成的promise的状态

​ 所有的参数状态都为成功 合成的promise状态则为成功 输出的值为所有promise函数的返回值组成的新数组

​ 只要有一个参数的状态为失败 合成的promise状态则为失败 输出的值试试第一个失败的promise的返回值

10. .race()

​ 将括号内所有的参数合并成一个新的promise 传参规则与.all()方法一致

区别:.race()合并的新的promise的状态 是根据第一个发生状态改变的promise参数来决定的

11. .resolve()

​ Promise.resolve(a) 将a转换成promise对象 a可以是任何值 传入的这个a即为promise对象的值 转换的promise对象的状态一定是resolve

12. .reject()

​ Promise.reject(a) 将a转换成promise对象 a可以是任何值 传入的这个a即为promise对象的值 转换的promise对象的状态一定是reject

13.手动创建一个promise类
  class Promise {                         // 创建一个Promise的类
      constructor(executer) {             // 构造函数constructor里面是个执行器
          this.status = 'pending';        // Promise的初始状态为 pending
          this.value = undefined          // Promise成功时的值默认为undefined
          this.reason = undefined         // Promise失败时的值默认为undefined
          let res = value => {            // 定义成功执行的函数res 传入参数value
              if(this.status == pending) {// 判断只有当前promise的状态为pending时 才能执行以下代码
                  this.status = 'resolve';// 将promise的状态改为成功resolve
                  this.value = value;     // 将promise的输出值赋值为传入的参数value
              }
          }
          let rej = reason => {           // 定义失败执行的函数rej 传入参数reason
              if(this.status == pending) {// 判断只有当前promise的状态为pending时 才能执行以下代码
                  this.status = 'reject'; // 将promise的状态改为成功reject
                  this.reason = reason;   // 将promise的输出值赋值为传入的参数reason
              }
          }
          try {                           // 成功时执行的代码
              executer(res,rej);          // 把res和rej两个函数传给执行器executer
          }catch (err) {                  // 失败时执行的代码 传入参数err
              rej(err);                   // 通过rej将报错内容传出
          }
      }
      then(onFufilled,onReject) {         // 定义Promise类的then方法 传入参数onFufilled,onReject
          if(this.status = 'resolve') {   // 判断当前promise状态是否为成功 是则执行以下代码
              onFufilled(this.value);     // 调用onFufilled函数将当前promise的value值当做参数传入
          }
          if(this.status = 'reject') {    // 判断当前promise状态是否为失败 是则执行以下代码
              onReject(this.reason);      // 调用onReject函数将当前promise的reason值当做参数传入
          }
      }
  }

瀑布流案例(随机更新)

1.放大镜
    *{
        margin: 0;
        padding: 0;
    }
    body{
        display: flex;
    }
    .smour{
        width: 100px;
        height: 100px;
        position: absolute;
        top: 0;
        left: 0;
        background: rgba(0,0,0,.5);
        display: none;                  /* 让小的指示盒子先隐藏起来 */
    }
    .middle{
        width: 300px;
        height: 300px;
        position: relative;
    }
    .middle>img{
        width: 300px;
        display: block;
    }
    .big{
        width: 300px;
        height: 300px;
        overflow: hidden;               /* 将图片溢出的部分隐藏起来 */
        display: none;                  /* 让放大镜区域的盒子先隐藏起来 */
        position: relative;
    }
    .big>img{
        width: 900px;                   /* 让大图片的宽度与放大镜区域的宽度比  和 小图片的宽度和指示区域的宽度比相同 才能达成放大区域无误 */
        display: block;
        position: absolute;
        top: 0;
        left: 0;
    }
    <div class="middle">
        <img src="../images/2.jpg">
        <div class="smour"></div>
    </div>
    <div class="big">
        <img src="../images/2.jpg">
    </div>
    let middle = document.getElementsByClassName('middle')[0] // 获取小图片容器
    let smour = document.getElementsByClassName('smour')[0]   // 获取指示区域元素
    let big = document.getElementsByClassName('big')[0]       // 获取放大镜区域元素
    let img = big.getElementsByTagName('img')[0]              // 获取大图片
    middle.onmouseover = function () {                        // 给小图片容器绑定鼠标移入事件
        smour.style.display = 'block'                         // 移入时将指示区域显示
        big.style.display = 'block'                           // 将放大镜区域显示
    }
    middle.onmouseout = function () {                         // 给小图片容器绑定鼠标移出事件
        smour.style.display = 'none'                          // 移出时将指示区域隐藏
        big.style.display = 'none'                            // 将放大镜区域隐藏
    }
    middle.onmousemove = function (event) {                   // 给小图片容器绑定鼠标移动事件
        event = event || window.event                         // event对象事件兼容性处理
        var x = event.clientX - 50                            // 获取鼠标距离页面左边距离再-宽度的一半
        var y = event.clientY - 50                            // 获取鼠标距离页面顶部距离再-高度的一半
        if (x<0) {                                            // 移动临界值判定  防止指示模块移出图片
            x=0
        }
        if (x>200) {
            x=200
        }
        if (y<0) {
            y=0
        }
        if (y>200) {
            y=200
        }
        smour.style.left = x + 'px'                           // 指示模块在x轴上让鼠标居中
        smour.style.top = y + 'px'                            // 指示模块在y轴上让鼠标居中  结合使用让指示区域的中心一直跟着鼠标走
        img.style.left = -3*x + 'px'                          // 大图片则根据倍数进行位移 但是移动方向与鼠标移动方向相反 所以乘以负倍数
        img.style.top = -3*y + 'px'                           // 两者结合可使得放大镜内的内容一直保持为指示区域内的内容
    }
2.瀑布流
    * {
        margin: 0;
        padding: 0;
    }

    .war {
        overflow: hidden;                                     /*最外层触发BFC清除浮动影响*/
    }

    ul {
        float: left;
        list-style: none;
        border: 1px solid #000000;
        margin: 0 20px;
    }

    .d {
        width: 200px;
    }
    <div class="war">
        <ul></ul>
        <ul></ul>
        <ul></ul>
    </div>
    function scrollon() {                                                 //封装整个元素生成的函数
        var uls = document.querySelectorAll('ul')                         //获取页面中所有的ul
        var min_index = 0                                                 //定义一个初始下标
        var uls_s = []                                                    //定义一个空的数组用于添加排序
        for (j = 0; j < 30; j++) {                                        //循环生成三十个元素
            var divs = document.createElement('div')                      //生成一个新的div元素
            divs.className = 'd'                                          //给新生成的div添加类名为d
            divs.innerHTML = j                                            //给新生成的div赋予内容标记为当前下标以示区分
            divs.style.background = 'rgb(' + parseInt(Math.random() * 256) + ',' + parseInt(Math.random() * 256) + ',' + parseInt(Math.random() * 256) + ')'                                       //给新生成的div赋予随机颜色区分元素
            divs.style.height = parseInt(Math.random() * 100 + 100) + 'px'//给新生成的div赋予随机高度 模拟实际元素排版
            if (j < uls.length) {                                         //判断当前循环次数小于ul的数量 执行以下代码 让每个ul至少有一个元素
                uls[j].appendChild(divs)                                  //给当前选中的ul添加新生成的元素
                uls_s[j] = uls[j].offsetHeight                            //获取添加元素后的ul的总高度 将其添加给定义好的空数组
            } else {                                                      //当每个ul里都至少有一个元素后执行以下代码
                var nArr = sortArr(uls_s)                                 //将获取所有ul的高度添加好的数组进行冒泡排序后赋值给新的变量
                for (x = 0; x < uls.length; x++) {                        //循环遍历所有的ul
                    if (nArr[0] == uls[x].offsetHeight) {                 //判断排序后的数组中的最小值与所有的ul的高度进行比较 相等后执行
                        min_index = x                                     //获取匹配的ul的下标赋予定义好的变量进行保存
                    }
                }
                uls[min_index].appendChild(divs)                          //通过匹配到的最小值下标 给最短的ul添加新生成的元素
                uls_s[0] = uls[min_index].offsetHeight                    //将添加新生成元素后的ul的高度重新赋予高度数组中的最小值 重新排序
            }
        }
        function sortArr(arr) {                                           //冒泡排序函数 arr为需要排序的函数的形参
            for (var i = 0; i < arr.length - 1; i++) {                    //循环比较除去自身以外的数
                for (var j = 0; j < arr.length - 1 - i; j++) {            //已经进行过比较的函数无需进行二次比较 所以-i
                    if (arr[j] > arr[j + 1]) {                            //判断当前数值是否大于下一个数值 是则执行
                        var item = arr[j]                                 //将较大数值赋予新生成的变量进行保存
                        arr[j] = arr[j + 1]                               //将较小的数值赋予较大数值的位置
                        arr[j + 1] = item                                 //将保存好的较大数值赋予原先较小数值的位置
                    }                                                     //实际上就是比较后将大的数值放后面 小的数值放前面 以达到排序目的
                }
            }
            return arr                                                    //返回排好序的数组
        }
    }
    window.onload = function () {                                         //当页面加载完毕后触发事件函数
        scrollon()                                                        //执行元素的生成函数
    }
    window.onunload = function () {                                       //当页面刷新时触发事件函数
        document.documentElement.scrollTop = 0                            //将滚动条返回至顶部
    }
    window.onscroll = function () {                                       //当滚动条发生滚动时触发事件函数
        var war = document.querySelector('.war')                          //获取最外层元素
        var wh = war.offsetHeight                                         //获取最外层元素的总体高度
        var vh = window.innerHeight                                       //获取当前页面的可视高度
        var sh = window.scrollY                                           //获取滚动条距离页面顶部的距离
        if (vh + sh == wh) {                                              //判断页面可视高度+滚动条距离页面顶部的距离是否等于整个页面的高度
            scrollon()                                                    //是则执行元素生成函数
        }
    }

赋予随机颜色区分元素
divs.style.height = parseInt(Math.random() * 100 + 100) + ‘px’//给新生成的div赋予随机高度 模拟实际元素排版
if (j < uls.length) { //判断当前循环次数小于ul的数量 执行以下代码 让每个ul至少有一个元素
uls[j].appendChild(divs) //给当前选中的ul添加新生成的元素
uls_s[j] = uls[j].offsetHeight //获取添加元素后的ul的总高度 将其添加给定义好的空数组
} else { //当每个ul里都至少有一个元素后执行以下代码
var nArr = sortArr(uls_s) //将获取所有ul的高度添加好的数组进行冒泡排序后赋值给新的变量
for (x = 0; x < uls.length; x++) { //循环遍历所有的ul
if (nArr[0] == uls[x].offsetHeight) { //判断排序后的数组中的最小值与所有的ul的高度进行比较 相等后执行
min_index = x //获取匹配的ul的下标赋予定义好的变量进行保存
}
}
uls[min_index].appendChild(divs) //通过匹配到的最小值下标 给最短的ul添加新生成的元素
uls_s[0] = uls[min_index].offsetHeight //将添加新生成元素后的ul的高度重新赋予高度数组中的最小值 重新排序
}
}
function sortArr(arr) { //冒泡排序函数 arr为需要排序的函数的形参
for (var i = 0; i < arr.length - 1; i++) { //循环比较除去自身以外的数
for (var j = 0; j < arr.length - 1 - i; j++) { //已经进行过比较的函数无需进行二次比较 所以-i
if (arr[j] > arr[j + 1]) { //判断当前数值是否大于下一个数值 是则执行
var item = arr[j] //将较大数值赋予新生成的变量进行保存
arr[j] = arr[j + 1] //将较小的数值赋予较大数值的位置
arr[j + 1] = item //将保存好的较大数值赋予原先较小数值的位置
} //实际上就是比较后将大的数值放后面 小的数值放前面 以达到排序目的
}
}
return arr //返回排好序的数组
}
}
window.onload = function () { //当页面加载完毕后触发事件函数
scrollon() //执行元素的生成函数
}
window.onunload = function () { //当页面刷新时触发事件函数
document.documentElement.scrollTop = 0 //将滚动条返回至顶部
}
window.onscroll = function () { //当滚动条发生滚动时触发事件函数
var war = document.querySelector(‘.war’) //获取最外层元素
var wh = war.offsetHeight //获取最外层元素的总体高度
var vh = window.innerHeight //获取当前页面的可视高度
var sh = window.scrollY //获取滚动条距离页面顶部的距离
if (vh + sh == wh) { //判断页面可视高度+滚动条距离页面顶部的距离是否等于整个页面的高度
scrollon() //是则执行元素生成函数
}
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值