JavaScript

一).什么是js

JavaScript是一个解释型的语言,JavaScript是一个脚本语言(侵入性 xss攻击),JavaScript是一个弱

类型语言(没有强制的类型)。

JavaScript由BOM(browser object model 浏览器对象模型),

DOM(document object model 文档对象), ECMAScript (基础语法)。 ECMAScript主要版本有

ES3(所有的浏览器都支持),ES5(大部分浏览器支持) , ES6(部分浏览器支持)。(babel.js他是

一个专门用来转换ECMAScript相关版本的一个脚本工具)。

二).JavaScript的相关书写

● 内嵌写法
<a href="javaScript:void">点击</a>
● 内联写法 (写主要代码)
    <!-- 内联写法 script标签可以写在任意的位置 建议写在最后 代码执行流程 从上到下的 文档流--
>
● 外联的写法
<script src="./demo.js"></script>

三).JavaScript的变量

变量就是一个存储单位,他会根据你赋的值在内存中开启空间(根据赋值得到对应的类型)

  • 内存空间

  • 变量声明

采用var关键词来进行声明 (var关键词声明的是伪全局变量

var 变量名 = 变量值
  • 变量命名的相关规范

  1. 不能是关键词和保留字

  1. 不能以数字开头

  1. 只能由数字,字母,下滑线,$构成

  1. 使用驼峰命名法 (首字母小写其他的首字母大写)

  1. 语义化命名 (见名知意)

四).JavaScript的数据类型(根据赋值来决定类型)

一.基础值类型(存储在栈上)

1).数值类型(所有的数字都是数值类型)number 包含整型,浮点类型
var age=20;
console.log(age)//number 数值类型
2).string 字符串 由一组双引号或单引号组成
var str="你好";
console.log(str) // string
var str1='大家好'
console.log(str1) //string
3).bool布尔类型 它的值只有2个 true 和 false
var flag=true;
console.log(typeof flag) //true
4).null 表示内存中空地址
var obj=null;
console.log(typeof obj) // object 历史遗留问题
5).undefined未定义js特有类型
var myname; // 它的默认值是undefined
myname="刘德华"; //用刘德华 覆盖 undefined

二.引用数据类型 (存储在堆上)

object 、function 、date ....

三.类型转换

1.基础值类型转换引用数据类型 (装箱)

  • String

  • Number

  • Boolean

var number = 10
console.log(typeof number)
//只要是被new都是引用数据类型
var obj = new Number(number)
console.log(typeof obj);
//String
var str = 'hello'
console.log(typeof new String(str))
//Boolean
var bool = true
console.log(typeof new Boolean(bool))

2.引用数据类型转为基础值类型 (拆箱)

  • toString 转为字符串

//obj对象转为字符串 对象中有东西 属性(方法)
console.log(typeof obj.toString());//转为字符串
console.log(typeof String(obj));//转为字符串
//obj对象转为对应的数值
console.log(typeof Number(obj))
//obj对象转为对应的布尔类型
console.log(typeof Boolean(obj))
console.log(Boolean(obj));

3.基础值类型之间的转换

转为字符串(String 及 toString方法)

  • 数值转字符串

// 数值转换字符串
var number1 = 10
var str1 = String(number1)
console.log(typeof str1);
  • 布尔类型转为字符串

//boolean类型转为字符串
var bool = true
var str2 = String(bool)
console.log(typeof str2);
  • null及undefined转为字符串

//null转为字符串
var nullObj = null
var str3 = String(nullObj)
console.log(typeof str3);
//undefined转为字符串
var un
var str4 = String(un)
console.log(typeof str4);
  • toString转换

//利用toString来转换
console.log(typeof number1.toString());
console.log(typeof bool.toString());
//null和undefined没有toString方法
// console.log(typeof nullObj.toString());
// console.log(typeof un.toString());

转为数值类型 (Number 及 parseInt 和 parseFloat)

  • 字符串转为数值 无法转换为NaN

  • boolean类型转为数值 true为1 false为0

  • null转为数值 值为0

  • undefined转为数值 值为NaN

console.log(Number('124345'))
console.log(Number(null))//null转为数值为0
console.log(Number(true))//true为1 false为0
//NaN是数值类型
console.log(typeof NaN)//number
console.log(Number(undefined))//NaN Not a Number
//任意类型转为数值的时候 无法进行转换就会出现NaN
console.log(Number('abc'))//NaN
console.log(Number('1234abc'))//NaN
//parseInt 转整型 (切割前面的内容) parseFloat(保留小数) 转浮点型
console.log(parseInt('1234.123abc'))
console.log(parseInt('a123.123abc'))//NaN
console.log(parseFloat('a123.123'))//NaN
console.log(parseFloat('1234.123abc'))

转为boolean类型 (Boolean在条件表达式下自动转为布尔类型)

  • 数值转为boolean类 型 非0及NaN都是true

  • 字符串转为boolean类型 非空字符就是true

  • null和undefined转为boolean类型 都是false

//数值转为boolean类型 非0及NaN都是true
console.log(Boolean(123))
console.log(Boolean(0))
console.log(Boolean(NaN))
//字符串转为boolean类型 非空字符就是true
console.log(Boolean(''))
console.log(Boolean(' '))
console.log(Boolean('123'))
//null和undefined转为boolean类型
console.log(Boolean(null))
console.log(Boolean(undefined))

4.Number

  • NaN 无法被转换为数值的时候出现的

  • infinity 无穷大

//常量值
console.log(Number.MAX_SAFE_INTEGER)
console.log(Number.MAX_VALUE)
console.log(Number.MIN_SAFE_INTEGER)
console.log(Number.MIN_VALUE)
console.log(Number.NaN) //NaN的值
console.log(Number.NaN,NaN)
//无法被转换为数值的时候出现NaN abc 转为数值 dfg 转为数值
console.log(NaN == NaN)//false
//无穷大
console.log(Number.NEGATIVE_INFINITY,-Infinity) //负无穷大
console.log(Number.POSITIVE_INFINITY,Infinity) //正无穷大
console.log(-Infinity == Number.NEGATIVE_INFINITY)

5.typeof 操作符

var str="你好";
typeof str;  //string

var age=20;
typeof age  // number

var flag=true;
typeof flag  // boolean

var o=null;
typeof o  //object

var name;
typeof name // undefined

var person={};  //对象
typeof person  // object

var arr =new Array(11,22,33)  //数组
typeof arr    // object

五).运算符

算数运算符

+ - * / % ++ --
  • +特殊的算术运算符

  • 对于有字符串的值进行+ 那么对应的+号做的连接 返回的是字符串

  • 如果没有字符串那么其他的会被转为number类型进行运算(其他类型还是会被转为number)

  • 其他的算术运算 (会将对应的值转为number类型进行运算 如果出现NaN 那么结果就是NaN)

  • ++前置先执行+1操作再执行本行代码 ++后置就是先执行本行代码再执行++

console.log(1+true)//2
console.log(1+true+null)//2
console.log(1+true+undefined)//NaN
//出现字符串做的为连接
console.log(1+2+3+undefined+'undefined'+true+10)//NaNundefinedtrue10
console.log(1+''+2) //12
console.log(1*1+2+3+(3-4))//5
console.log(1*null)
console.log(null-undefined+10*100+(1000/10))//NaN
//取余 取模 小数取大数得到本身 大数取小数得余数
console.log(3%4)//3
console.log(4%3)//1
console.log(10%2+10)//10
//++的意思在原本的基础上自增1
var a = 10
console.log(++a)//11
//++ 前置和后置的区别
// ++前置先执行+1操作再执行本行代码 ++后置就是先执行本行代码再执行++
console.log(a++)//11
console.log(a)//12
console.log(12+13+(a++)-(a--)+(++a))//37 12+13+12-13+13
//执行顺序
// 先算括号里面 再执行方法 再算*/ % 再算+-
console.log(a*12+undefined-(10+5)%(5).toString())//NaN
console.log(a.toString()+undefined-(9*9)%10)//NaN

拼接运算符

 //    + 号 左右2遍,只要有1遍出现字符,就会是拼接
     //         如果2遍都是数值 相加
        
         console.log(10+20);     // 30
         console.log(10+'20');   //'1020'
         console.log('10'+20);   //'1020'
         console.log('10'+'20'); //'1020'

逻辑运算符

  • && 全部都为true才是true 取得是最后一个true 只要一个为false那么对应的结果就是false 取得是第一个false

  • || 只要有一个是true 结果就是true 那么取得是第一个true 全部为false结果才为false 取得是最后一个false

  • !进行boolean类型的强制转换 取反将true变false false变为true

短路与 && 到达为false 后面的不执行 短路或 || 到达为true后面的不执行

&&  ||  !
// &&都为真就是真(取得最后一个真) 只要有一个是假得就是假(第一个假)
console.log(1&&'hello'&&true&&3)//3
console.log(1&&'hello'&&0&&false)//0
//|| 有一个是真就是真(取第一个真) 如果全部为假就是假(最后一个假)
console.log(1||'hello'||true||3)//1
console.log(1||'hello'||0||false)//1
console.log(0||false)//false
console.log(!1)//取反 返回的是boolean类型的值

比较运算符 (返回boolean类型)

  • 返回的值都是boolean类型的值

  • “1” == 1 比较值 “1”!== 1 比较值的同时比较类型

  • NaN!=NaN

  • 出现NaN大部分都是false (!=)

  • 任意类型和数值进行比较会被转为数值

  • 字符串和字符串比较那么比较的是acsii码(从第一位开始比较 大写A 65 小写a 97)

> < == >= <= != ===
console.log(1>2)//false
console.log(1>=1)//true
console.log(1 && 1>1 || 2>2)//false
//== 和 === ==是表示值相等 ===表示是在==的基础上类型也要相等
console.log(1 == '1')//true
console.log(1 === '1')//false
//NaN==NaN false NaN!=NaN true
console.log(NaN==NaN)//false
console.log(NaN!=NaN)//true
//0.1+0.2 != 0.3 二进制解析 二进制运算(减法快 )
console.log(0.1 == 0.1)
console.log(0.2 == 0.2)
console.log(0.1+0.2 == 0.3)//false

位运算符(解析成二进制进行运算)

& ^ | < >

位移运算符(二进制上进行位移)

>> <<
// 位移运算
// 2 10
console.log(2>>2)//0
//左移 2 10 1000 (快速把2变成8)
console.log(2<<2)

赋值运算符

  • 赋值运算最后执行

  • += -= *= ...都是会在原本的基础上进行赋值

位运算符

三目运算

boolean值?true的结果:false的结果
var a = 1+2>3?10:-10 //a的结果为-10
var i = 1?20:30 //20

表达式

表达式就是由运算符拼接的公式
  • 算法运算符拼接的表达式 算术表达式

1+1+1
  • 比较运算符拼接的表达式 条件表达式(转为boolean类型)

1+1>2
  • 比较运算符拼接的表达式 条件表达式(转为boolean类型)

1+1>2
  • 逻辑运算符拼接的表达式 关系表达式

1+1>2 && 1+2=3

运算符的执行顺序

  • 示例

var n = 20
n++
var i = 10+29+(++n)-(n++)/10+30%7-5*3>10 && 20-15*29/18>13-(n++)/n+''+109 ? 18-
(2-4)*3+undefined-true+(n++) : n-(n++)*3+false+true //-47
console.log(n)//25

条件控制语句

  • 概述:

程序控制语句所有的语言都具备 ,主要分为循环控制语句(循环执行)和条件控制语句(根据不同的条

件执行不同的内容)

常用的条件控制语句

  • if else (传递条件表达式 根据条件表达式来判断)

  • switch case (空间复杂度大于if else 时间复杂度小于if else 传入一个值 根据来进行匹配)

if else

if(条件表达式){
//条件满足执行的内容
}else{
//条件不满足执行的内容
}

示例

判断一个数值是否奇数还是偶数

//prompt 弹窗输入框返回的是你的输入值 他的类型是string
var n = prompt('请输入你需要验证的数值')
if (n % 2 == 0) {
console.log('是偶数')
} else {
console.log('是奇数')
}

else if 多条件

//else if
//用于判断的方法 isNaN
if(isNaN(Number(n))){
console.log('输入出错')
}else if (n % 2 == 0) {
console.log('是偶数')
} else {
console.log('是奇数')
}

if else 只会进入第一个找到的满足条件的内容

//if else 只会进入其他的第一个满足的条件 后续的不执行
if(2>1){
console.log('进来了')
}else if(3>2){
console.log('来玩玩')
}

示例2

输入的范围为100-999 判断一个数是否为水仙花数 (153 1的三次方+5的三次方+3的三次方 = 153)

var n = prompt('请输入100-999之间的数值')
if(n>=100 && n<=999){
//if else嵌套
//取每一位的值
var a = n%10 //个数
var b = parseInt(n%100/10)//十位
var c = parseInt(n/100)//百位
if(a*a*a + b*b*b + c*c*c == n){
console.log('是水仙花数')
}else{
console.log('不是水仙花数')
}
}else{
console.log('输入出错')
}

if else的嵌套

if else 允许多层嵌套 但是建议嵌套层级不要超过俩层

if(条件表达式){
//true的时候走的
if(条件表达式){
....
}else{
//false的时候走的
}
}else{
//false的时候走的
}

示例

判断输入的数值的奇偶

var n = prompt('请输入你需要验证的数值')
//用于判断的方法 isNaN
if(!isNaN(Number(n))){
if (n % 2 == 0) {
console.log('是偶数')
} else {
console.log('是奇数')
}
}else {
console.log('输入出错')
}

输入一个数 判断是否为3的倍数 以及是否是15的倍数(1-999)

// 输入一个数 判断是否为3的倍数 以及是否是15的倍数(1-999)
var printIn = prompt('请输入1-999之间的数值')
if(printIn >= 1 && printIn <= 999){
//取余3 取余15
if(!(printIn % 3)){//值为0的情况下进入 整除3
console.log('他是3的倍数')
//是否为15的倍数
if(printIn % 5 == 0){
console.log('他是15的倍数')
}
}else{
console.log('不为3的倍数也不为15的倍数')
}
}else{
console.log('输入出错')
}
注意事项
  • if else他只会进入其中的一个条件中 不会同时进入俩个同级的代码块

  • if 里面的条件可以为表达式也可以为值 但是都会被强制转换为boolean类型

  • if else可以嵌套多层 一般建议嵌套不超过俩层(可读性 可维护性)

switch case

switch(值){
case 值1:
执行的代码
break
case 值2:
执行的代码
break
...
default:
上面都不满足的默认的执行代码
}

示例

根据输入的指令打印对应的操作

/*
某个空调有对应的开关 当你按下的开关键为1号键的时候 执行加热操作
按下2号键 执行制冷操作
按下3号键 执行通风操作
按下的键为其他 不执行操作
*
/
var code = prompt('请输入你需要执行的指令')
switch (Number(code)) {
case 1://单独的分支
console.log('正在执行加热操作')
break //break 跳出 结束这个switch块 后续的不再执行
case 2:
console.log('正在执行制冷操作')
break
case 3:
console.log('正在执行通风操作')
break
default:
console.log('错误指令 不执行操作')
}
  • switch 的值比对用到的是=== 必须要类型和值都一致

  • 每个case块都是一个分支 如果没有break那么会进入下一个分支

  • 多个分支可以执行一个操作

  • break用于跳出switch块 那么后续的分支不再执行

  • switch 不适用于范围内的比对

  • default 默认的执行 上面都不满足的情况下执行

switch块允许多层嵌套

switch(n){
case 值1:
switch(i){
case 值1:
...
break
}
break
case 值2:
....
default:
...
}

示例

判断一个输入值的奇偶

var n = prompt('请输入值')
//判断是否输入正确
switch(isNaN(Number(n))){
case true:
switch(n%2){
case 0:
console.log('偶数')
break
case 1:
console.log('奇数')
break
}
break
case false:
console.log('输入出错')
}

成绩表 输入对应的成绩 60分为及格 70分为一般 80分为良好 90 分为优秀 100分为�plus 其他分数输

入无法判断

switch(prompt()-0){
case 60:
console.log('及格')
break
case 70:
console.log('一般')
break
case 80:
console.log('良好')
break
case 90:
console.log('优秀')
break
case 100:
console.log('�plus')
break
default:
console.log('输入错误')
}

LOL 小学生之手 Q 大杀四方 W 致残打击 E 无情铁手 R 诺克萨斯断头台 A 鼠标点击 平A 其他键

defeat

switch(prompt()){
case
'Q':
console.log('大杀四方')
break
case
'W':
console.log('致残打击')
break
case
'E':
console.log('无情铁手')
break
case
'R':
console.log('诺克萨斯断头台')
break
case
'A':case'click':
console.log('平A')
break
default:
console.log('defeat')
  • switch 适合实际的值的列举(枚举) if 使用范围内容条件判断

  • switch 空间复杂度高于if switch时间复杂度低于if

  • 枚举相当于一个箱子 箱子里面有很多对应的值 每个值有对应的名字

  • 箱子 --- 1号 2号 .... 通过列举的名字来获取里面的内容(里面内容为常量 (不变的量))xun

六).循环控制语句

程序控制语句

  1. 条件控制语句(根据对应的条件执行对应的代码片段)

  1. 循环控制语句(根据循环条件循环执行多次对应的代码)

循环控制语句的流程

  1. 定义初始值

  1. 设置迭代条件(初始值的基础上迭代)

  1. 执行的代码 循环体

  1. 设置终止条件(布尔类型表达式 返回的是boolean的值(强制转换为boolean类型)

常用的循环控制语句

  1. while

  1. do while

  1. for

  1. ...

while循环

//外部定义初始值
var 初始值变量 = 值
while(终止条件(当他为false的时候就会结束对应的循环)){
//执行的代码 循环体
//迭代条件
}

示例

打印1-100的值

var i = 1
while(i<=100){
console.log(i)
i++
}

求和 1-100的求和

var i = 1
var sum = 0
while(i<=100){
console.log(i)
sum += i
i++
}
console.log(sum)
  1. var关键词修饰的变量进行变量提升 伪全局变量

  1. var关键词修饰的变量会被预编译 但是不会读取赋值操作 而是强制赋值undefined

  1. 在循环中不建议声明变量 会覆盖之前的值 会加大空间复杂度

while循环嵌套

while(条件){
while(条件){
循环体
}
}
  1. 循环嵌套时间复杂度会增加(不建议嵌套超过俩层)

  1. 循环嵌套执行的次数为外层循环次数*内层循环次数

示例

打印乘法口诀表 (外层控制行 内层控制列)

/*
乘法口诀表为9行9列 1行为1列 2行为2列 .... 9行为9列
*
/
var row = 1,col //声明行和列
while (row <= 9) { //打印行
col = 1 //列值返回初始值 为了下一行
while (col <= row) { //列最大为行数
document.write(row+'*'+col+'='+row*col+'&emsp;')//在文档上书写内容
col++
}
document.write('<br/>') //换行
row++
}

do while循环

var 初始值变量 = 初始值
do{
循环体
}while(条件)

do while和while的区别

  1. while 最少执行0次 do while 最少执行一次

  1. do while常用于 先执行对应的内容再判断是否循环(人机交互)

示例

打印1-100

var i = 1
do{
console.log(i)
i++
}while(i<=100)

do while也可以嵌套

/*
打印四行五列的矩形
*
/
var row = 0,
col, str = ''
do {
col = 0
do {
str += '*'
col++
} while (col < 5)
str += '\n'//换行
row++
} while (row < 4)
console.log(str)

for循环

任何循环之间可以互相转换 while循环适用于你不知道执行次数的时候 for循环适用于知道执行次数

for(初始值;迭代条件;迭代量){
执行的代码 循环体
}

for循环嵌套

任意循环可以互相嵌套

for(初始值;迭代条件;迭代量){
for(初始值;迭代条件;迭代量){
//循环体
}
}

示例

打印直角三角型

/*
*
***
*****
*******
*********
*
/
for(var i=0;i<5;i++){
for(var j=0;j<2*i+1;j++){
document.write('*')
}
document.write('<br/>')
}

循环总结

  1. 循环是用于反复多次执行一段代码

  1. 循环的三种方案可以互相嵌套 以及三种方法可以随意转换 常用的为for和while

  1. while循环用于处理不知道对应的执行次数 for循环常用于知道对应的执行次数

  1. 循环要避免对应的死循环(死循环就是循环的迭代条件一直为true 没有办法停止)

while死循环写法 for死循环写法

while(true){ //死循环
}
for(;;){ //死循环
}
  1. while循环的时间复杂度低于for循环 for循环的执行效率要低于while循环

  1. do while循环先做后判断 最少执行一次 while及for最少执行0次

用于循环中的关键词

break关键字

break的功能:

1,在switch语句中使流程跳出switch结构。

2,在循环语句中使流程跳出当前的循环

注意:

1, 如果已执行break语句,就不会执行循环体中位于break后的语句。

2, 在多层循环中,一个break语句只向外跳一层

示例:

1, 判断一个数是不是合数。(指自然数中除了能被1和本身整除外,还能被其他的数整除(不包括0)的数。)

2, 判断一个数是不是素数。(除了1和它本身以外不再有其他的除数整除。)

示例

查找1-100内的素数

for(var i=1;i<=100;i++){
var count = 0
//再进行对应的整除 用i整除 2-i-1的值 只要有能整除的那么这个数就是不是素数 没有就是素数
for(var j=2;j<i;j++){
if(i%j==0){
count++
break
}
}
if(count!=1){
console.log(i)
}
}

continue关键字

continue的功能:

只能在循环语句中使用,使本次循环结束,即跳过循环体中下面尚未执行的语句,接着进行下次是否执行循环的判断。

注意:

1,continue语句只能用在循环里。

2,对于 while 和 do-while 循环,continue 语句执行之后的动作是条件判断;对于 for 循环,随后的动作是变量更新。

示例

1-100 逢7过 里面带7和7的倍数跳过

for(var i=1;i<100;i++){
if(i%7==0 || parseInt(i/10)==7 ||parseInt(i%10)==7){
continue
}
console.log(i)
}

扩展内容

时间复杂度

时间复杂度讲的是恒定机器中的执行次数和对应的变量的比例 也就是说在恒定机器内执行次数越多 那

么时间复杂度越高,那么对应的时间复杂度越高 他的执行效率就越低。将时间复杂度降低那么就可以提

高对应的效率。

时间复杂度(用O来表示)跟对应的执行次数成正比

O1 常数阶 每行代码执行一次

console.log('123')//1次
console.log('123')//1次
console.log('123')//1次
console.log('123')//1次

On 线性阶 循环执行多次由一个n变量来控制

for(var i=0;i<n;i++){
console.log('123')
}

Ologn 对数阶 由俩个变量来控制的 (递归)

var i = 2
while(i<n){
i*=k
}

Onlogn 线性对数阶 线性阶包含对数阶

for(var i=0;i<n;i++){
var j = 2
while(j<n){
j*=k
}
}

On2 平方阶 俩个线性阶包含

for(var i=0;i<n;i++){
for(var j=0;j<n;j++){
console.log('123')
}
}

On3立方阶 3个线性阶

Onk次方 k个线性阶

时间复杂度排序

O1<Ologn<On<Onlogn<On^2<On^3<On^k....

空间复杂度

空间复杂度讲的是在内存开辟上 有多个变量内存被同时开辟,开辟的内存越多对应的空间复杂度越高,

占用的内存大小就越大。

七).函数介绍

函数的概念function

  • 函数就是把特定功能的代码抽取出来,使之成为程序中的独立实体。

函数的作用

  • 正如函数的概念,我们可以根据需要,将特定的功能用函数来包裹(封装)

使用函数的好处

  • 函数可以在同一个程序或其他程序中多次重复使用(通过函数调用)

  • 使程序变得更简短而清晰,提高可读性

  • 有利于程序维护

函数的分类

  • 函数可以分为:系统函数 内置函数 自定义函数

  • 系统函数 内置函数

是官方提供好的函数,可以直接使用

如:alert() lsNaN() console.log()

document.writte()Boolean(),Math.pow()等

  • 自定义函数

是用户自己定义的函数,用户可以根据实际需求,对特定的功能使用函数来封装

函数的简单定义

定义函数的语法格式;
function 函数名(){
    代码块;
}

注意:

  • 必须使用function关键字,且为小写,函数名可以自己给定

  • 函数名的命名规则和变量名一致

  • 函数名后必须写圆括号()

实例; 定义一个函数printOut

function printOut(){
    document.write("Hell World!");
}

函数的调用

  • 函数的调用方式;函数名()

如;调用下面的函数;printOut()
function prntOut(){
    document.write("Hello World!")
}

注意;

  • 调用函数后会执行函数内部的代码块;

  • 函数在不调用的情况下是不会执行的,只有调用后函数中的代码才会执行

函数的标准定义

定义函数的语法格式:

function 函数名(参数1,参数2,.......)
{
    执行语句;
    return 返回值;
}

注意:

  • 函数名后圆括号()中的参数数量不定,也可以没有;(根据功能需要)

  • retun关键字的作用是将某个值返回,如果没有返回值则默认返回undefined;

函数中的arguments数组

JS中函数不介意传递进来多少个参数,也不在乎传进来参数是什么数据类型,在调用函数时也未必一定要传递指定数量的参数,原因是 ECMAScript 中的参数在内部是用一个数组(arguments)来表示的。函数接收到的始终都是这个数组,而不关心数组中包含哪些参数(如果有参数的话)。

arguments可以判断参数的个数,arguments是个数组(后面会详细讲解数组)。

我们可以使用arguments.length来获取参数的个数

可以使用arguments[i] 的方式来访问数组中的第i个参数(i为自己给定的整数下标)

1.它是一个伪数组

2.用于动态接受实参

3.arguments只能在'函数内部使用'

4.可以通过下标取值

练习:向一个函数传入不定数量的数值求和。


function fn(){

  var sum = 0;
  for (var i = 0; i < arguments.length; i++){
    sum += arguments[i]
  }
  return sum;
}
console.log(fn(10,20))

递归

  • 什么是递归

递归是一个算法,算术其实就是固定的套路,递归算法是为了降低时间复杂度提高效率所设计的算法,

他可以完成所有循环可以做的事情。

函数自己调用自己

要有临界点(结束的条件)

  • 递归常见使用场景

  1. 循环能做的 递归都能实现 (不考虑性能)

  1. 对象or数组的深拷贝

  1. 磁盘上文件的遍历

  1. 管理系统的左侧动态菜单

递归的用途 (可以在不知道层级的情况下走到底)

  • 文件目录遍历

  • DFS查找

  • 多级对象分析合并

  • 深拷贝

  • ...

递归的流程

  • 初始值 (不变的值)

  • 规律

  • 自己调自己

示例

function 函数名(参数){
    if(条件){
        初始值 进行返回
                       }else{
        规则值 返回值 自己调用自己
    }
}
//n表示次数 1次 值为1 2次 值为3
function fn(n){
    if(n==1){
        return 1
    }else{
        return n+fn(n-1)
    }
}
console.log(fn(100))
求1+100的和

函数的作用域和作用域链

作用域概述

一个变量的作用范围称为作用域,作用域主要划分为全局作用域(全局可用),局部作用域(局部可用

又为函数作用域

示例

var a = 10 //全局作用域
function fn(){
    var a = 20 //局部作用域
    var b = 30
}
fn()
console.log(a)//10
console.log(b) //b is not defined

在全局中不能访问局部作用域的变量

题目

console.log(a)//undefinde
var a = 10
function fn(){
    // a = undefined
    a = 20 //局部变量赋值
    console.log(a) //局部变量打印
    var a = 30 //局部作用域 var修饰关键进行预编译
}
function fn1(){
    a = 40 //全局作用域
    var b = 30
    console.log(a+b)
}
function fn2(){
    //预编译
    console.log(a) //undefined
    var a = 50
}
fn1()//70
console.log(a)//40
fn()//20
console.log(a)//40
fn2()//undefinde
console.log(a)//40
  • var 关键词会进行变量提升

  • 只要是在function中使用var关键词声明那么这个变量就是局部变量 那么在这个里面使用到所有这

  • 个变量都是指向这个局部变量

  • 如果在function中没有使用var关键词声明那么这个变量就是全局变量

作用域链

作用域链就是逐层向上查找对应的作用域(变量声明)形成的链子,如果没有找到那么就会报错。

var a = 10
function fn(){
    console.log(a)//undefinde
    var a = 20 //局部变量赋值
    function fn1(){
        console.log(a) //20
        function fn2(){
            console.log(a)//20
          }
        fn2()
      }
    fn1()
}
fn()

八).数组

数据结构

数据结构主要是数据的一个存储和逻辑结构的体现,只要能存储数据的一个结构我们就称为数据结构。

相关知识点

  • 数组 又被称为顺序表主要通过牵引下标来进行访问(排序)

  • 队列 先进先出的容器

  • 栈 先进后出

  • 树 二叉树

  • 图 对应的指针的指向

  • 链表 通过对应的指针来进行指向

  • 散列表 hash表 (hashcode来进行编码的)

  • ...

数组

概述

数组是一种线性的数据结构,他可以存储对应的数据,一般通过对应的牵引下标进行数据的访问。在一般情况下我们在数据里面存储的数据类型是一致的。(数组也可以存储不同数据类型的数据 )

数组的定义

使用[]赋值来定义

var arr =[]
//里面的数据使用,隔开
var arr =[1,2,3]

使用new关键词

var arr new Array();//没有数据的数组定义
//  传入一个参数 表示定义的长度
var arr =new Array (10)//表示当前长度为10
//传入多个参数 表示赋值填入
数组的特征
  • 数组具备下标 可以通过下标来访问和赋值操作

  • 数组具备length属性重新修改length重新修改是可行(改大会进行扩容操作 改小会进行删除操作)

var arr = [1,2,3,4,5]
//通过下标访问 下标从0开始
console.log(arr[0])//访问第一个元素
arr[3] = 18
console.log(a            rr)
//可以做到 自动扩容
arr[10] = 20
console.log(arr)
//通过length来访问对应的长度
console.log(arr.length)
//length可以赋值 大于会进行扩容操作 小于的时候会进行删除操作
arr.length = 2
console.log(arr)
console.log(arr.length)
数组的遍历

遍历:就是把数组中的每个元素从头到尾都访问一次(类似我们每天早上学生的点名)

var arr = ['red','green','blue'];
for (var i = 0; i < 3; i++){
  console.log(arr[i]);
}
//1.因为我们的数组牵引号从0开始,所以 i必须从 0开始 i < 3
//2.输出的时候 arr[i]  i 计算器当牵引号来用

使用es5新增的for in关键词

//使用es5新增的for in循环 (for in循环是为了遍历对象 数组也是一个对象)
//for in遍历的是下标(对象的key)
for(var index in arr){
// console.log(index)
console.log(arr[index])
}

使用es6新增的for of关键词

//在for in基础上进行改版的 for of (专门遍历数组的)es6
//for of不能遍历对象 只能遍历数组(伪数组)
for(var value of arr){
console.log(value)//值
}

for in 和 for of的区别

  • for in是用于遍历对象的 他遍历的是对象的key(es5)

  • for of是用于遍历数组的 他遍历的是数组的值 (es6)

练习

将一个数组中的第二个元素和第三个元素进行位置互换

var arr = [1,2,3,4,5]
//传入的对应的需要互换的位置
function fn(pos1,pos2,arr){
//保存第一个位置的值
var temp = arr[pos1-1]
//将第一个位置的值进行覆盖
arr[pos1-1] = arr[pos2-1]
//将保存的第一个位置的值赋给第二个位置
arr[pos2-1] = temp
}
fn(2,3,arr)

将数组 [2,3,1,5,6,7,3] 中的最大值和最小值的位置进行互换

//得到最大值和最小值的下标
function fn(arr){
//假设第一个是最大值 同时也是最小值
var max = 0 //下标
var min = 0 //下标
//将对应的最大值及最小值和其他的比较 如果比最大值还大那么你就是最大值 如果比最小值还小那么
就是最小值
for(var i=1;i<arr.length;i++){
if(arr[max]<arr[i]){ // 如果比最大值还大
max = i //记录最大下标
}
if(arr[min]>arr[i]){// 如果比最小值还小
min = i //记录最小下标
}
}
//换位置
var temp = arr[min]
arr[min] = arr[max]
arr[max] = temp
return arr
}
console.log(fn([2,3,1,5,6,7,3]))
数组的方法

1.(破西)push 往数组的最后位置添加成员,返回数组的新长度

2.(怕普)pop 删除数组的最后一位,返回被删除的元素

3.(些父特)shift 删除数组的第一位成员,返回被删除的成员

4.(安些福特)unshift 往数组的第1个位置添加成员,返回数组的新长度

5.(锁特)sort 1).默认是根据ASCII码,进行排序 2).添加一个回调函数,a-b 升序,b-a 降序

6.(蕊沃思)reverse 倒序

7.concat 合并,拷贝

8.(囧按)join 返回制定格式的新的字符串,默认是 ","隔开

9.(思赖思)slice 截取,不会改变原来的数组

10.(思布赖思)splice 1. 2个参数,删除,第2个参数,删除的个数 2. 3个参数,第2个参数是0, 插入 3. 3个参数,第2个参数是1或者1以上的,替换

11.Array.isArray 判断是否为一个数组

12.indexOf() 找到了返回下标,找不到返回-1 第2个参数,默认是从0开始,也可以指定位置 ES5

13.forEach() 只能做遍历,不会有返回值 不能被终端, 可以 try...catch 结合throws new Error('')

14.(卖普)map 有返回值,返回的是一个新的数组,数组里的成员,根据map的条件决定

15.(桑木)some 如果有一个条件满足,就返回true

16.(佛特)filter 返回一个新的数组,数组的内容(长度),又filter的条件决定

17.(爱喂)every 必须数组的所有成员,满足条件,才返回true

18.(瑞丢思)reduce 2个参数 1个参数:是一个回调函数,该回调函数有4个参数 pre :如果reduce给了2个参数,pre的初始值就是第2个参数的值 如果reduce没有给2个参数,pre就读取数组里第一个位置的值 当做初始值 cur 当前的值 index 如果reduce有初始值(第2个参数),下标从0开始 如果没有初始值,它的下标从1 arr 当前的数组(原来的数组)


    //1.数组的方法
    // 1).方法的作用
    // 2).方法的参数
    // 3).方法的返回值

    console.log("--------------push----------");
    //1. push() 推,往数组最后的位置天成员,返回数组的新长度,参数可以是1个,也可以是多个
    var arr = [11, 22, 33, 44];
    console.log(arr.push("你好", "哎哟不错"));//新的长度5
    console.log(arr);
    console.log("--------------pop----------");
    //2. pop() 删除数组最后一位成员,返回被删除的成员
    var arr = [11, 22, 33, 44];
    console.log(arr.pop());
    console.log(arr);
    console.log("--------------shift----------");
    //3. shift() 删除数组的第一位,返回被删除的成员
    var arr = [11, 22, 33, 44];
    console.log(arr.shift());
    console.log(arr);

    console.log("--------------unshift----------");
    //4. unshift() 往数组的第一个位置添加成员,返回数组的新长度
    var arr = [11, 22, 33, 44];
    console.log(arr.unshift("你好", true, 'AA'));
    console.log(arr);
    console.log("--------------sort----------");
    //5. sort()  它默认是根据ASCII码进行排序,只能排序10以内的值,
    //使用回调函数
    //            回调函数内部 返回 a-b 升序,b-a降序
    var arr = [6, 5, 1, 9, 3, 7, "11"];  //1  ,11   
    //数值0~9:48-57, 1:49
    arr.sort(function (a, b) {
      return a - b
      // return b - a;
    })
    console.log(arr);
    console.log("--------------reverse----------");
    //6. reverse() 倒序
    var arr = ["你", "好", "呀"];
    console.log(arr.reverse());
    //7. concat()合并,会返回一个新的数组,还能做拷贝
    console.log("--------------concat----------");
    //合并
    var arr1 = [11, 22, 33];
    var arr2 = ["AA", 'BB'];
    var arr3 = arr1.concat(arr2, "帅哥", "美女");
    console.log(arr3);
    console.log(arr1);
    console.log(arr2);

    //拷贝
    var arr1 = [11, 22, 33];
    var arr2 = arr1.concat();
    console.log(arr2);
    console.log(arr2 == arr1);
    console.log("--------------join----------");
    //8. join() 返回指定格式的字符串,默认是是 ","隔开
    var arr = [11, 22, 33, 44];
    console.log(arr.join());
    console.log(arr.join(""));
    console.log(arr.join("-"));
    console.log(arr);

    console.log("--------------slice----------");
    //9. slice()  不会改变原来的数组,截取返回一个新的数组
    //    第一个参数表示开始位置,第2个参数,结束位置,
    //不包含结束位置
    //         0   1   2    3
    var arr = [11, 22, 33, 44];
    //22,33
    console.log(arr.slice(1, 3));   // [1,3)
    console.log(arr.slice(1));   // [1,3)
    console.log(arr);
    //10.splice()
    //1. 2个参数删除,返回一个新的数组,里面包含被删除的数
    //   3个参数
    //       第2个参数是0,插入
    //       第2个参数是1以上,替换
    //          0  1   2   3   4   5
    var arr = [11, 22, 33, 44, 55, 66];

    //删除
    // console.log(arr.splice(3, 1));
    //插入
    // arr.splice(3, 0, "AA", "BB", "CC")
    // console.log(arr);
    //替换
    arr.splice(3, 2, "AA", "BB")
    console.log(arr);

    console.log('--------------indexOf------------');
    //11. indexOf() 找成员,找到了返回下标,找不到返回-1
    var arr = [11, 22, 33, 44, 55, 33, 44, 55];
    console.log(arr.indexOf(33)); //2
    console.log(arr.indexOf(66)); //-1
    //indexOf的第2个参数表示开始位置,默认是从0开始,也可以指定位置
    console.log(arr.indexOf(33, 3));//5

    console.log('--------------Array.isArray------------');
    //12.Array.isArray()  判断是否为数组,是返回true,不是返回false
    var arr = [];
    console.log(Array.isArray(arr));//true
    var arr = 123
    console.log(Array.isArray(arr));//false


    //13.高阶函数
    console.log('--------------forEach------------');
    //   forEach() 纯粹是做遍历(循环)使用 没有返回值,它的参数是一个函数,函数里有3个参数
    //        第1个参数,item 每一个成员
    //        第2个参数,index 下标
    //        第3个参数,arr 原来的数组
    var arr = [11, 22, 33, 44, 55];
    var res = arr.forEach(function (item, index, arr) {
      console.log(item, index, arr);
    })
    console.log(res); //undefined

    console.log('--------------map------------');
    //   map() 能做遍历,它有返回值,返回一个新数组,参数与forEach一样
    //         返回的内容,可以在回到函数里控制
    var arr = [11, 22, 33, 44, 55];
    var res = arr.map(function (item, index, arr) {
      console.log(item, index, arr);
      return item * 10
    })
    console.log(res);
    console.log('--------------some------------');

    //   some() 只要满足一次条件,就返回true,默认是返回false
    //          如果条件满足,就不在执行遍历
    var arr = [11, 22, 33, 44, 55];
    var res = arr.some(function (item, index, arr) {
      console.log(item, index, arr);
      return item > 30
    })
    console.log(res);


    console.log('--------------every------------');
    //every() 满足所有的成员,才返回true,如果有一个不满足
    //      就返回false
    var arr = [11, 22, 33, 44, 55];
    var res = arr.every(function (item, index, arr) {
      return item > 20
    })
    console.log(res);

    console.log('--------------filter------------');
    //   filter  过滤,返回一个新的数组
    var arr = [11, 22, 33, 44, 55];
    var res = arr.filter(function (item) {
      return item > 30;
    })
    console.log(res); //[33, 44, 55]

    console.log('--------------reduce------------');
    //   reduce()
    // 1). 只有一个参数,是一个回调函数
    //      回调函数4个参数
    //      pre:第1次是数组的第一个值,第2次以上reduce的返回结果,
    //      cur:当前数组的成员
    //      index:数组的下标
    //      arr:原来的数组

    var arr = [11, 22, 33, 44, 55];
    console.log(arr);
    var res = arr.reduce(function (pre, cur, index, arr) {

      //          11    22
      //          undefined 33
      //          undefined 44
      //          undefined 55
      console.log(pre, cur);
    })
    console.log(res);



    //pre 
    //cur current
    var res = arr.reduce(function (pre, cur) {
      //           11   22
      //           33   33
      //           66   44
      //           110  55
      console.log(pre, cur);
      return pre + cur
    })
    console.log(res);

    //    var arr = [11, 22, 33, 44, 55]; 累计相加
    //  var res= arr.reduce(function (pre, cur) {
    //     return pre * cur
    //   })
    // reduce 第2个参数是给 pre赋予初始值
    var arr = [11, 22, 33, 44, 55];
    var res = arr.reduce(function (pre, cur) {
      //                0    11
      //                11   22
      //                33   33
      //                66   44
      //                110  55
      return pre + cur;
    }, 100)
    console.log(res);

        //ES3
        // push
        // pop
        // shift
        // unshift
        // sort
        // reverse
        // concat
        // join
        // slice
        // splice

        //ES5 
        //Array.isArray()
        //forEach 纯的遍历,没有返回值,而且不能被中断,真要打断,请使用for循环
        //          偏门可以 try..catch实现打断
        var arr = [11, 22, 33, 44];
        //item 每一个成员
        //index  下标
        //arr 原来数组
        // try..catch...
        try {
            arr.forEach(function (item, index, aaa) {
                console.log(item);
                console.log(index);
                console.log(aaa);
                console.log('----------------');
                throw new Error("自己制作麻烦打断")
            })
        } catch (error) {
            // console.log(error);
        }

        console.log("11111");


        console.log('--------------map---------------');
        //map  遍历,会返回一个新的数组,数组的内容,
        //  根据回调函数的return来决定
        var arr = [11, 22, 33, 44];
        var res = arr.map(function (item, index, arr) {
            console.log(item);
            console.log(index);
            console.log(arr);
            return item * 10
        })
        console.log(res);

        //some 如果有一个条件满足,就返回true,默认返回false
        var arr = [11, 22, 33, 44];
        var res = arr.some(function (item, index, arr) {
            if (item > 50) {
                return true
            }
        })
        console.log(res);

        //ervery, 所有元素都满足指定的条件,
        //才返回true,有一个不满足就是false
        var arr = [11, 22, 33, 44];
        //是否都大于10
        var res = arr.every(function (item, index, arr) {
            if (item > 20) {
                return true;
            }
        })
        console.log(res);

        //filter 满足条件就留下来,返回一个新的数组
        var arr = [11, 22, 33, 44];
        var res = arr.filter(function (item, index, arr) {
            return item > 30
        })
        console.log(res);


        //reduce

        var arr = [1, 3, 5, 7, 10];
        //reduce 目前只有一个回调函数
        //  pre, 第一次就去数组的第一个值
        //        第2..次...就是上一次的返回值
        //  cur, 当前
        //  index 下标,如果reduce只有一个回调函数
        //      的情况,index就从1开始
        //  arr    原来数组
        console.log('---------------------');
        // arr.reduce(function (pre,cur,index) {
        //     //pre 1次 1 ,cur 3
        //     //pre 2次 99 ,cur 5
        //     //pre 3次 99 ,cur 7
        //     //pre 4次 99 ,cur 10
        //         console.log(pre,cur);
        //         return 99;
        // })


        // //第一次
        // function (pre,cur) {
        //         // pre  1,
        //         //cur 3
        // }
        //  //第2次
        // function (pre,cur) {
        //     // pre undefined
        //     //cur 5
        // }
        //  //第3次
        // function (pre,cur) {
        //     // pre undefined
        //     // cur 7,
        // }
        //  //第4次
        // function (pre,cur) {
        //      // pre undefined
        //      // cur 10
        // }


        var arr = [1, 3, 5, 7, 10];

        //     //reduce 放一个函数参数,函数的下标从1
        //    var res= arr.reduce(function (pre,cur) {
        //         return pre+cur
        //     })
        //     console.log(res);

        //当reduce传入2个参数,那么匿名函数的下标从0开始

        var arr = [1, 3, 5, 7, 10];
        var res = arr.reduce(function (pre, cur, index, arr) {
            // console.log(pre, cur, index);
            // pre  初始值 0
            // cur     1

            // pre undefined 
            // cur  3

            // pre undefined 
            // cur  5

            // pre undefined 
            // cur  7

            // pre undefined 
            // cur  10

            return pre + cur
        }, 0)
        console.log(res);
添加(add push set...)删除 (delete (删除直接回收) remove(删除完还在内存中) pop...)
  • push 添加到后面(返回新的长度)

var arr = [1,2]
var newLength = arr.push(3)
console.log(newLength) //3
console.log(arr)//[1,2,3]
  • pop 删除最后一个 (返回删除的元素)

var arr = [1,2]
var deleteItem = arr.pop()
console.log(deleteItem)
console.log(arr)//[1]
  • shift 删除第一个 (返回删除的元素)

//shift和unshift
var arr = [1, 2]
var deleteItem = arr.shift()
console.log(deleteItem)
console.log(arr)
  • unshift 添加到第一个 (返回的新的长度)

var length = arr.unshift(3) //2
console.log(length)
console.log(arr)
修改 (replace set...)

覆盖

arr[0] = 新的值

先删再加 splice

var arr = [1,2,3]
//从下标为1的位置删除 删除一个 插入一个4和5
arr.splice(1,1,4,5)//新的数组 这个数组其实就原本的数组
console.log(arr)

splice的删除的操作

var arr = [1, 2, 3, 4]
//省略个数 删到最后(包含下标1)
arr.splice(1)
console.log(arr)
var arr = [1, 2, 3, 4]
//从下标1开始 删除俩个(包含下标1)
arr.splice(1,2)
console.log(arr)

splice的添加

//添加
var arr = [1,2,3,4]
arr.splice(2,0,5)
console.log(arr)

splice返回的是删除的数据组成的数组

查询(query select get...)

indexOf 根据传入的值查询第一次出现的下标

查询索引indexOf 传入对应的元素 返回的是index (如果没有返回-1)

var arr = [2, 4, 6, 8, 4]
console.log(arr.indexOf(4))
//从第三个开始数 从前往后数 第二个参数为查找坐标(从左到右)
console.log(arr.indexOf(4,2))//4

简单实现indexOf

var arr = [2, 4, 6, 8,4]
//模拟实现indexOf
function myIndexOf(value,start) {
if (value == undefined) {
throw new Error('参数未传递')
}
//如果没有传递start 那么应该为默认值0
if(start == undefined){
start = 0
}
//开始查询
for (var i=start;i<arr.length;i++) {
if (arr[i] == value) {
return i
}
}
return -1
}
console.log( myIndexOf(4))

lastIndexOf (从右到左)

var arr = [2, 4, 6, 8, 4]
console.log(arr.lastIndexOf(4))//4
console.log(arr.lastIndexOf(4,2))//1

简单实现

//模拟实现indexOf
function myLastIndexOf(value, start) {
if (value == undefined) {
throw new Error('参数未传递')
}
//如果没有传递start 那么应该为默认值0
if (start == undefined) {
start = arr.length
}else if(typeof start != 'number'){
throw new Error('参数类型不正确')
}
//开始查询
for (var i = start; i >= 0; i--) {
if (arr[i] == value) {
return i
}
}
return -1
}
console.log(myLastIndexOf(4))
console.log(myLastIndexOf(4,3))

以上方法都会对于原本的数组影响

不影响原本数组的方法(一定有返回值)

数组的拼接 concat方法

var arr1 = [1, 2]
var arr2 = [3, 4]
//数组的concat方法传入的参数为数组
var concatArr = arr1.concat(arr2)
console.log(arr1) //[1,2]
console.log(arr2) //[3,4]
console.log(concatArr) //[1,2,3,4]
//可以传入数组也可以传入对应的值
var concatArr = arr1.concat(3,4)
console.log(concatArr)

slice 截取 返回新的数组

var arr = [1, 2, 3, 4, 5, 6]
//传入一个开始下标 结束的下标(默认没有传入结束的下标截取到末尾)不包含结束的下标
var sliceArr = arr.slice(4,5)//[5]
console.log(sliceArr, arr)

join 将对应的数组转为字符串

var arr = [1, 2, 3, 4, 5]
//join如果不传参默认使用,来进行分割每个元素 如果传入参数那么使用参数来分割
var str = arr.join('a')//传入的参数一定是字符串
console.log(str)

位置变换的相关方法

sort 排序

//排序的相关方法
var arr = [2,3,5,10,4,6]
//默认情况下 sort方法是按照ascii码排序
// arr.sort()
//高阶函数用法 将函数作为参数的函数叫做高阶函数
var newArr = arr.sort(function(a,b){
return a-b //正序 b-a就是倒序
})
console.log(arr)
console.log(newArr)
console.log(newArr == arr)

reverse 反转

//反转方法
var arr1 = arr.reverse()
console.log(arr)
console.log(arr1 == arr)
排序算法

常用的排序算法 (On2)

冒泡排序 (逐层冒泡)

选择排序 (选择一个数跟其他的进行比较)

插入排序 (在插入数据的时候的排序法)

希尔排序 (快速插入排序)

快速排序(快速冒泡排序 (数据量不大的情况下最快))

归并排序(大数据处理中最快的排序)

堆排序

桶排序

...

冒泡排序(On^2)

前一个跟后一个比较 俩俩相比 比较完就交换位置 直到所有的内容比较完

 var arr = [5,2,9,8,1]
        //冒泡排序
        function bubleSort(arr){
            //冒泡排序 外层的轮数
            for(var i=0;i<arr.length-1;i++){
                //控制比较的次数
                for(var j=1;j<arr.length-i;j++){
                    //j和j-1 俩俩进行比较
                    if(arr[j]<arr[j-1]){
                        //换位置
                        var temp = arr[j]
                        arr[j] = arr[j-1]
                        arr[j-1] = temp
                    }
                }
            }
            return arr
        }
        console.log(bubleSort(arr));
选择排序(On^2)

选择一个下标和所有的数进行比较 如果比较完发现这个下标不是之前的下标就换位置

var arr = [5,2,9,8,1]
        //选择排序
        function selectorSort(arr){
           for(var i=0;i<arr.length-1;i++){
                //将当前的i值当做最大值
                var max = i
                for(var j = i+1;j<arr.length;j++){
                    //判断当前的最大值小于j下标所在的值
                    if(arr[max]<arr[j]){
                        max = j
                    }
                }
                //判断当前的最大值不是开始设置的值 要进行位置交换
                if(max != i){
                    var temp = arr[i]
                    arr[i] = arr[max]
                    arr[max] = temp
                }
           }
           return arr
        }
        console.log(selectorSort(arr));
快速排序(二分排序)Onlogn
 var arr = [5,2,9,8,1]
        //快速排序
        function quikSort(arr){
            //当我们的数组里面只有一个元素或者一个元素都没有的时候 退出返回这个数组
            if(arr.length <= 1){
                return arr
            }
            //取基数值 取第一个
            var mid = arr[0]
            var left = []
            var right = []
            //for循环 拿出所有的值跟中间值比较
            for(var i=1;i<arr.length;i++){
                arr[i]>=mid?right.push(arr[i]):left.push(arr[i])
            }
            return quikSort(left).concat([mid],quikSort(right))
        }
        console.log(quikSort(arr));

九).字符串

字符串概述

字符串也是一种数据结构叫做串(共同的内容串在一块)。字符串是一种基础值类型,基础值类型不可变。字符串在算法解决问题的时候会使用比较频繁,常用于解决一些查找一些相关的问题(马拉车算法解决回文字符串的)

字符串的声明

赋值声明

//可以使用对应的单引号也可以使用双引号
var str = '字符串'

通过new关键词来构建

var str = new String('传入任意类型')

赋值声明和new关键词声明的区别

  • new关键词声明的字符串 用typeof验证得到的是object 赋值声明得到是string

  • 使用new关键词·········声明的字符串俩个是不相等的

var str = 'hello'
var str1 = 'hello'
console.log(str == str1) //true
console.log(str === str1) //true
var str2 = new String('hello')
var str3 = new String('hello')
console.log(str2 == str3)//false
console.log(str2 === str3)//false
console.log(str == str2)//true
console.log(str === str2)//false

es6新增字符串模板 ``

var str = 'hello'
//在字符串模板内容 ${}表示引用变量 传入的是变量名 支持标准js语法
var str1 = `jack ${str}`
//var str1 = 'jack'+str
console.log(str1)

字符串的属性以及访问

  • length属性 表示字符串长度(只读属性)

var str = 'hello'
console.log(str.length)//5
str.length = 10 //无意义的操作 字符串不可变
console.log(str.length)//5
  • 字符串访问其他的某个内容可以通过下标来访问

//串这个数据结构是有序的 可以通过下标来访问对应的里面的某个字符
console.log(str[0])//h
str[0] = 'A' //字符串不可变 没有意义
console.log(str)//hello
console.log(str[0])//h

字符串相关的方法(字符串不可变)

字符串相关方法是以返回值来操作 (本身是不变的)

  • indexOf 根据传入的字符串返回第一次出现的下标(找不到返回-1)从前往后

console.log('aaaabbbb'.indexOf('a'))//0
console.log('aaaabbbb'.indexOf('ab'))//3
  • lastIndexOf 根据传入的字符串返回第一次出现的下标 (找不到返回-1)(从后往前)

console.log('aaaabbbb'.lastIndexOf('a')) //3
console.log('aaaabbbbab'.lastIndexOf('ab')) //8
  • search 方法类似于indexOf 支持正则表达式

// search 返回对应的下标 找不到返回-1 支持正则表达式
console.log('aaabbb'.search('ab'))//2
console.log('aaabbb'.search('ab',3))//2 search只有一个参数 没有开始位置
  • 底层实现lastIndexOf及indexOf

//简易实现indexOf
var str = 'abcbcaabcd'
function myIndexOf(value,index){
if(value == undefined){
throw new Error('传参个数错误')
}
//如果index没有传入设置默认值
if(index == undefined){
index = 0
}
//遍历查找
//得到对应的长度
//如果当前的开始位置+value的长度大于本身的字符串长度 那么返回-1
if(index+value.length > str.length){
return -1
}else{
//遍历对应的字符串
for(var i=index;i<str.length - value.length;i++){
//根据value长度来拼接
var v = str[i]
for(var j=1;j<value.length;j++){
v+=str[i+j]
}
//根据对应的value来比较
if(value == v){
return i
}
}
}
return -1
}
console.log(myIndexOf('abc',2))
//lastIndexOf的实现
function myLastIndexOf(value,index){
if(value == undefined){
throw new Error('传参个数错误')
}
//如果index没有传入设置默认值
if(index == undefined){
index = str.length
}
//遍历查找
//得到对应的长度
for(var i=index;i>=0;i--){
//字符串拼接
var v = str[i]
for(var j=1;j<value.length;j++){
v+=str[i+j]
}
if(v == value){
return i
}
}
return -1
}
  • charAt 根据下标返回字符

console.log('abc'.charAt(0))//a
  • charCodeAt 根据下标返回字符串的ascii码

console.log('abc'.charCodeAt(0))//97
  • 静态方法 String.fromCharCode

//静态方法
// 将asscii码变成字符串 String
var str = String.fromCharCode(97) //返回一个字符串
console.log(str)

截取相关的方法 (如果只传递一个参数那么默认都是截取到最后)

  • substring 传入开始下标及结束下标(包含开始的下标不包含结束的)

var str = 'abcdef'.substring(3,5)//de
  • substr 传入开始下标和个数

var str = 'abcdef'.substr(2,2)//从下标2开始截取俩个 cd
  • slice 传入开始下标及结束下标 (可以一个参数都不传递默认截取所有)

var str = 'abcdef'.slice(2,4)//cd

连接的方法

  • concat

var str = 'hello'
//返回一个新的字符串
var concatStr = str.concat('world')
console.log(concatStr) //helloworld

支持正则的四个方法

  • search

//search 查找(根据传入的字符串返回第一次出现位置的下标)
var str = '1
_
abcd'
//\w数字字母下划线
var index = str.search(/\w/)
console.log(index)//0
  • match

//match 匹配 返回一个数组(包含匹配的内容)执行一次
var str = '
_
abc?789'
var strArr = str.match(/\w/)
console.log(strArr)
  • replace

//replace 替换 根据对应的匹配内容进行替换 执行一次
var str = 'abcabc'
// var str1 = str.replace('a','f')//传入字符串形式 将a替换为f
var str1 = str.replace(/a/,'f')//传入正则形式 将a替换为f
console.log(str1)
//高阶函数写法的replace 支持传入函数作为参数
//传入一个函数作为参数的形式 里面的参数是匹配的内容 里面的return是替换的内容
var replaceStr = str.replace(/a/,function(value){
console.log(value)//a
return 'f'
})
console.log(replaceStr)
  • split

//split 分割的 将对应的字符串分割为一个数组返回
//数组的join相反 将数据拼接成一个字符串返回 将字符串拆分为数组
var str = 'a,b,c'
//如果不传参 他就将这个字符串直接填入数组返回
// var arr = str.split(',')
var arr = str.split(/,/)
console.log(arr)
  1. 练习

将"abcabcabcabc" 中的bc全部替换为hello

var str =
abcabcabcabc"
//先找bc 再进行替换没有了退出
while(str.search('bc')!=-1){
str = str.replace('bc','hello')
}
console.log(str)

统计一个字符串在另一个字符串内的出现次数

//统计一个字符串在另外一个字符串中出现的次数
function getCount(from,str){
//abcabcabc bc
var arr = from.split(str)
return arr.length-1
}
var count = getCount('bacfddesdasda','dd')
console.log(count)

其他相关的函数

  • 去除首尾空格 trim

var str = ' a b c '
console.log(str.trim())
  • 转大小写

  • toUpperCase 转大写

  • toLowerCase 转小写

console.log(str.toUpperCase())//ABCBC
console.log(str.toLowerCase())//abcbc
  • toLocalUpperCase

  • toLocalLowerCase

//toLocalUpperCase
//toLocalLowerCase
//根据本地格式进行转换
console.log('a'.toLocaleUpperCase())
console.log('A'.toLocaleLowerCase())
//toString 转为字符串 他是所有对象都存在的方法(万物皆对象)
  • 统计一个字符串中大写字母的个数

//遍历比较
function fn(str) {
var count = 0
//先遍历
for (var i = 0; i < str.length; i++) {
if (str.charAt(i) >= 'A' && str.charAt(i) <= 'Z') {
count++
}
}
return count
}
console.log(fn('AbcASna'))
//利用正则
console.log( 'AbcASna'.match(/[A-Z]/g).length)

html元素相关的方法

  • sub 返回一个sub标签

var str = 'hello world'
console.log(str.sub())//<sub>hello world</sub>
document.write(str.sub())
  • fontColor 返回font标签 color属性

//fontColor
console.log(str.fontcolor('red'))
document.write(str.fontcolor('red'))
  • fontSize 返回font标签 size属性

//fontSize
console.log(str.fontsize(30))
document.write(str.fontsize(30))

总结

  • 字符串不可变 字符串的方法以返回一个新的字符串来进行操作

  • 字符串indexOf方法用于获取下标的 charAt方法用于或者字符串

  • 字符串截取方法substring slice 开始下标和结束下标作为参数 substr 以个数来作为截取

  • 字符串支持正则的方法 search match replace split

  • 字符串进行拼接的方法concat

  • 字符串去除首尾空格的方法trim ,toUpperCase 转大写 toLowerCase 转小写

Math类

Math是数学类,他里面包含关于数学操作的方法及属性(里面的内容设计为静态 直接Math去点)

属性

PI 数学Π

E 科学计数法e

方法

  • max 最大值 传入多个值比较出最大值并返回

  • min 最小值 传入多个值比较出最小值返回

  • pow 取幂次方

  • sqrt 开平方

  • ceil 向上取整

  • floor 向下取整

  • round 四舍五入

  • random 取0-1之间的随机数 包含0 不包含1

  • abs 取绝对值

//相关方法
console.log( Math.max(1,2,3,4,5,6))
console.log( Math.min(1,2,3,4,5,6))
//返回2的三次方
console.log( Math.pow(2,3))
//开平方
console.log( Math.sqrt(4))
//取整
//向上取整
console.log( Math.ceil(2.4321))//3
//四舍五入
console.log( Math.round(2.4321))//2
//向下取整
console.log( Math.floor(2.4321))//2
//随机数 0-1 包含0不包含1
console.log(Math.random())
//绝对值 正值
console.log(Math.abs(-10))
//三角函数 一度= Π/180
console.log(Math.sin(45*Math.PI/180))
console.log(Math.cos(45*Math.PI/180))
console.log(Math.tan(45*Math.PI/180))

取区间的随机数

//min max
function randomNumber(min,max){
return Math.random()*(max-min)+min
}

十).日期对象及对象讲解

日期对象概述

日期对象是用于表示日期时间的一个对象,他里面包含对应设置日期时间及获取日期时间的方法。

日期对象的声明

使用new关键词声明

new Date()

无参构造声明(构建对象的方法叫做构造函数)

  • 构造函数首字母大写

  • 使用new关键词调用构造函数产生新的对象

//无参构造声明是获取当前的本地时间
var date = new Date()
//他是以本地的时间格式进行显示的 UTC (构建标准时间)
console.log(date)

传入字符串作为参数(格式不正确会出现Invalid Date)

//传递对应的字符串 以字符串转为对应的时间
var date = new Date('2000/2/14 10:43:10')
console.log(date)

传递多个number类型的参数(如果值超出自动向上递增)

//传递多个数字 最多支持传递6个参数 年 月 日 时 分 秒
//传递的月份 从0开始到11
var date = new Date(2000,2,12,10,40,10)
console.log(date)//3月12
var date = new Date(2022,10,10)
console.log(date)//2022年11月10日 00:00:00

传递毫秒值来构建

//传递一个参数为number值 他将number值识别为毫秒 + 格林兰治时间(1970/1/1 00:00:00)
var date = new Date(2000)
console.log(date)

日期对象的比对

日期对象在对比的过程中会自动转为毫秒值进行相关比对计算

var date2 = new Date()
console.log(date2 - date1)//毫秒值
console.log(date2+date1)//连接操作
console.log(date2*date1)//毫秒值计算

日期对象的相关方法

get开头 获取

  • 获取年 getYear

  • 获取月 getMonth (月份0-11)

  • 获取日 getDate

  • 获取星期几 getDay (星期天是0)

  • 获取时 getHours

  • 获取分 getMinutes

  • 获取秒 getSeconds

  • 获取毫秒 getMilliseconds

  • 获取时间戳 getTime

  • 获取时区偏移量 getTimezoneOffset

var date = new Date()
//获取的相关方法
console.log(date.getFullYear()) //获取年
console.log(date.getMonth()) //获取月 (0-11)
console.log(date.getDate()) //当前日
console.log(date.getDay()) //获取星期几 (星期天为0)
console.log(date.getHours()) //获取小时 (0-23)
console.log(date.getMinutes()) //获取分钟 (0-59)
console.log(date.getSeconds()) //获取秒中 (0-59)
console.log(date.getMilliseconds()) //获取毫秒 (0-999)
console.log(date.getTime()) //离格林兰兹时间的毫秒值 时间戳(唯一标签)
console.log(date.getTimezoneOffset())//获取时区

获取UTC时间(抛除时区的影响)

  • getUTCHours

  • ....

set开头 设置

  • setFullYear 设置年份

  • setMonth 设置月份

  • setDate 设置天

  • setHours 设置小时

  • setMinutes 设置分钟

  • setSeconds 设置秒钟

  • setMilliseconds 设置毫秒

var date = new Date();
date.setFullYear(2000,11,12) //设置年份 还可以传入月份及日期
date.setMonth(10) //设置月份 还可以传入日期
date.setHours(16) //设置小时 还可以传入分秒毫秒
date.setDate(10) //设置天
date.setMinutes(10) //设置分钟 还可以传入对应的秒 毫秒
date.setMilliseconds(10) //设置毫秒
date.setSeconds(10) //设置秒 还可以传入毫秒
console.log(date)

设置UTC时间(抛除时区的影响)

  • setUTCHours

  • ....

转字符串相关方法

var date = new Date()
console.log(date.toString())
console.log(date.toDateString()) //转日期
console.log(date.toTimeString()) //转时间
console.log(date.toLocaleString()) //根据本地格式转为字符串 (可以进行格式化)
console.log(date.toLocaleDateString()) //根据本地格式转为日期字符串
console.log(date.toLocaleTimeString()) //根据本地格式转为时间字符串
console.log(date.toUTCString()) //返回UTC时间字符串
console.log(date.toISOString()) //遵从ISOS协议的字符串

对象

概述

对象是一个存储的容器,他是一个引用的数据类型。他里面可以存储一些属性(值)及方法(函数其实

就是对应的动作),他里面存储是以key:value的形式进行存储的(key是唯一标识不可以重复,value

是值),可以通过key来访问对应的value

对象的声明(Object)

赋值声明

var obj = {} //空对象
console.log(obj)

以new关键词来调用构造函数进行声明(new Object)

var obj = new Object()//空对象
console.log(obj)

Object属于顶层父类 所有的其他对象都是他的子类

对象在比较的时候比较的是地址

{} == {} //false

对象的增删改查

查 (for in遍历查询)

  • 可以通过对象名.属性名来进行访问

  • 可以通过对象名[属性名字符串]来进行访问

var obj = {
name:
'
jack',//key为name的属性他的值为jack
sayHello:function(){ //sayHello 对应的值为一个函数
console.log('hello')
},
children:{
username:
'
rose
'
}
}
//for in来遍历对象的中属性
for(var key in obj){
console.log(key)
console.log(obj[key])
}
//通过key来访问对应的值
//对象名.属性名
console.log(obj.name)
//通过[]来访问 对象名[属性名字符串]
console.log(obj['name'])
obj.sayHello()
//通过父对象访问里面的属性对象
console.log(obj.children.username)
console.log(obj['children']['username'])

增 (对象属性赋值)

//增加 对于没有的属性进行赋值就是添加
obj.age = 18
console.log(obj)

修改 (重新赋值)

//修改 对于有的属性重新赋值就是修改
obj.name = 'tom'
console.log(obj)

删除 delete关键词

//对于属性进行删除
// delete obj.age
delete obj['age']
console.log(obj)

this关键词

this表示这个,this是一个指针引用他没有特定的值,他是根据对应的调用着来执行其引用地址。

对象中的函数里的this指向当前对象

//对象中函数里的this 指向对应的对象
obj.say = function(){
console.log(this == obj)//true
}
obj.say()

函数中的this 指向调用函数的对象

//对象中函数里的this 指向对应的对象
obj.say = function(){
console.log(this == obj)//true
}
obj.say()
function test(){
console.log(this)//指向window
}
//所有在全局声明的变量及函数都是全局的变量 全局变量指向global对象 在浏览器中的global对象是
window
//所以对应的全局声明的函数是属于window 也就是说这个函数是由window来调用的 所以里面的this就指
向window
window.test() // test() 省略了window window可以被省略的
var a =
'
hello'
console.log(window.a)
console.log(window['a'])
  • this在普通函数中指向其调用者,在全局范围内调用者为window 在对象中调用者为当前对象

  • 在事件处理中 this指向当前事件的派发者

<button id="btn">点我</button>
<script>
var btn = document.getElementById('btn')
btn.onclick = function(){
console.log(this) //按钮
}
</script>

window对象的俩个函数

setInterval 定时器 (在一定事件内循环执行某个操作 执行多次)

写法

以匿名函数的形式

//参数1对应执行的代码 参数2为间隔的时间 参数3为传递给参数1的函数的参数(省略)
setInterval(function(arg){},2000,1)

以具名函数的形式

function fn(arg){
console.log(arg)
}
setInterval(fn,2000,2)

以字符串的形式(不建议)

setInterval('console.log("hello")',1000)

clearInterval 清除定时器(必须要做 如果不做那么定时器会无限执行)

var i = 0
var timer = setInterval(function (arg) {//setInterval会返回一个标识这个标识是number
类型
i++
console.log(arg)
console.log(i,timer)
//当i到达100的时候停止
if(i==100){
clearInterval(timer)//清除定时器 一定要做的
}
}, 100, 1)

setTimeout 延时器 (延迟执行某个操作 执行一次)

clearTimeout清除延时器

//和setInterval用法一致 只不过他只执行一次
var timer = setTimeout(function(){
console.log(2)
},3000)
//清除延时器
console.log(timer)
clearTimeout(timer)

总结

  • setInterval及setTimeout是异步的 也就是说他不会阻塞正常代码的执行

  • setInterval和setTimeout是通过定时触发器线程计时完放入任务对象等待js引擎空闲再执行 他

  • 一定慢于js引擎直接执行的代码

  • 同步比异步快

  • 定时器或延时器开启后必须关闭否则会占用内容

  • 不要在定时器中嵌套定时器(如果需要的话那么每一次都需要在外层的定时器都需要关闭内层定时

  • 器)

  • 外层定时器的间隔时间一定要打印里层的间隔时间

计时器案例

<div id="showText">00:00:00</div> <button id="start">开始计时</button> <button
id="stop">停止计时</button>
<script>
//获取所有的元素
var show = document.getElementById('showText')
var start = document.getElementById('start')
var stopBtn = document.getElementById('stop')
//定时器
var timer
//点击开始按钮开始计时
start.onclick = function () {
//清除上一次的定时器
clearInterval(timer)
var h = 0 //时
var m = 0 //分
var s = 0 //秒
//时间发生变化
timer = setInterval(function () {
//在每次变化的时候改变对应的showText里面的显示内容
s++
if(s==60){
m++
s=0
}
if(m==60){
h++
m=0
}
//将对应的内容进行拼接显示到对应的showText中
show.innerHTML = `${fn(h)}:${fn(m)}:${fn(s)}`
}, 1000)
}
//补零的方法
function fn(number){
if(number<10){
return '0'+number
}
return number
}
//点击停止按钮停止计时
stopBtn.onclick = function () {
//清除定时器
clearInterval(timer)
}
</script>

十一).BOM

BOM概述

BOM全称 (browser object model) 浏览器对象模型,主要用操作浏览器相关的内容(调用浏览器相关

的功能及获取浏览器携带的内容),BOM是JavaScript的重要组成,但是他缺少规范,所以他是通过共

有对象来解决没有规范的问题。(前面BOM相关的内容没有融入w3c 导致他有兼容问题)。ie的bom

对象里面的相关参数及方法是最多的,由于是共有对象所以这些参数都被其他浏览器采用了。

BOM的共有对象

  • window 窗口对象(浏览器的global对象)

  • location 地址栏对象

  • history 历史对象

  • navigator 导航对象(浏览器信息对象)

  • screen 屏幕对象 (适配)

  • frames 框架对象 (其实也是个window)

  • document 文档对象

window

概述:

window窗口对象,他是浏览器的global对象,他包含所有的全局变量。

属性

  • caches 返回一个缓存对象 CacheStorage

  • closed 是否关闭(默认值为false)

  • cookieStore 存储cookie的空间

  • crossOriginIsolated cors设置(跨域设置)

  • innerHeight 窗口可操作区域的高度

  • innerWidth 窗口可操作区域的宽度

  • indexedDB 浏览器内置数据库对象

  • localStorage 本地存储

  • sessionStorage 他是session生命周期本地存储

  • chrome 谷歌内核的属性

  • console 控制台

  • scrollX 滚动栏的坐标 scrollY 滚动栏的坐标

  • ....

其他的所有对象都是window对象的属性 window可以被省略

方法

打印方法(控制台打印方法)

// 控制台打印方法
console.log('日志打印') //日志打印
console.error('错误打印') //错误打印
console.warn('警告打印') //警告打印
console.info('信息打印') //信息打印
console.debug('调试打印') //调试打印
console.group('分组打印') //分组打印
console.table('表格打印') //表格打印

弹框方法

  • alert 弹信息框 没有返回值

  • prompt 弹输入框 有返回值返回值为输入的内容 (string)

  • confirm 弹选择框 有返回值 返回为boolean类型

//弹窗
console.log(alert('hello'))
console.log(prompt('请输入内容'))
console.log(confirm('请问你是否是单身贵族'))

打开窗口的方法 open

document.getElementById('openBtn').onclick = function(){
//打开的url路径地址 打开的方式(a标签的target一致) 打开时的配置(使用=号赋值 使用,号隔
开)
window.open('http://www.baidu.com','_blank','width=100,height=200,left=200,top=
200')
}

关闭窗口的方法 close (关闭当前窗口)

document.getElementById('closeBtn').onclick = function(){
//打开的url路径地址 打开的方式(a标签的target一致) 打开时的配置(使用=号赋值 使用,号隔
开)
window.close()
}

改变窗口位置的方法

  • moveTo

  • moveBy

//moveTo 给定实际坐标 moveBy 给定变化的距离
window.moveTo(30,30) //到达30,30的坐标
window.moveBy(20,20) //10 10 变成 30,30的坐标

改变窗口大小的方法

  • resizeTo

  • resizeBy

//resizeTo 给定实际大小 resizeBy 在当前下发生变化
window.resizeTo(30,30) //大小改为width 30 height 30大小
window.resizeBy(30,30) //原本的width增加30 原本的height也增加30

改变的滚动栏位置的方法 (*)

  • scrollTo

  • scrollBy

document.getElementById('scrollToBtn').onclick = function(){
window.scrollTo(300,300) //滚动栏x轴到达30 y轴到达30
console.log(window.scrollX,window.scrollY)
}
document.getElementById('scrollByBtn').onclick = function(){
window.scrollBy(300,300) //滚动栏x轴到达30 y轴到达30
console.log(window.scrollX,window.scrollY)
}

打印机功能调用 print打印方法 (*)

  • print (打印的基础实现)

window.print()

find 查找

window.find()

窗口 foucs 获取焦点 blur 失去焦点

window.foucs()
window.blur()

setInterval 和 setTimeout

clearInterval 和 clearTimeout

fetch 发送一个异步请求 (*)

//fetch 异步的请求 Axios的底层实现 (内核为xhr)
var obj = window.fetch('http://www.baidu.com')
console.log(obj)

Location (*)

概述

location是地址栏对象,他可以获取地址栏上的所有信息。

https://mbd.baidu.com/newspage/data/landingsuper?
context=%7B%22nid%22%3A%22news
9664507230212976443%22%7D&n
type=-1&p_from=-1
  • https:// 协议

  • 浏览器访问根据协议的不同指定不同的端口号 80端口http 443端口https

  • /newspage/data/landingsuper 路径地址

  • ?

  • context=%7B%22nid%22%3A%22news_9664507230212976443%22%7D&n_type=-1&p_from

=-1 传递参数(get请求)

location的相关属性(都支持赋值)

  • hash 获取#后面携带的内容(#通常在最后面)

  • host 主机 ip地址+端口号

  • hostname 主机名 ip地址

  • href url路径

  • port 端口号

  • pathname 路径名

  • search 获取?后面传递的参数

  • protocol 协议

  • origin 跨域地址

  • ancestorOrigins 获取倒序排列的文档对象及来源的浏览器上下文 (插件开发)

console.log(location)
//获取hash前面带#
console.log(location.hash)
//默认自己添加#
location.hash = 'abc'
console.log(location.host)
//localhost == 127.0.0.1
//location.host = "123.123.123.123:8080" 一般不建议设置
console.log(location.hostname)//主机名其实就是个ip地址
console.log(location.href)//路径
// 跳转页面
// location.href = 'http://www.baidu.com' //不会产生历史页面的
console.log(location.origin)
console.log(location.port)
//获取?后的内容 里面的写法为key=value&key=value
console.log(location.search)
location.search = 'name=jack&age=19'
//http(不安全 明文 80) 及 https (安全 加密 443(openssl(对称加密 非对称加密 hash加密
等等) )) 超文本传输协议
console.log(location.protocol)//协议
//只读属性 返回的是一个domstringlist对象
console.log(location.ancestorOrigins)

location的方法

  • assgin 跳转页面 (会产生历史页面)

  • replace 替换url跳转页面

  • reload 重新加载页面

//获取元素
document.getElementById('btn1').onclick=function(){
location.assign('http://www.baidu.com')
}
//等同herf赋值
document.getElementById('btn2').onclick=function(){
location.replace('http://www.baidu.com')
}
document.getElementById('btn3').onclick=function(){
location.reload()
}

history (*)

概述

history对象是历史对象,他记录所有的历史页面。

history的属性

  • length 历史页面个数(包含当前页面)

  • state 值 (默认值为null)

  • scrollRestoration 滚动恢复属性 (auto || manual)

history的方法

  • forward 前进

  • back 后退

  • go 去任意历史页面

//常用的方法 前进 后退 任意跳转 事件会自动传递参数 参数叫event
document.getElementById('forward').onclick = function(){
history.forward()
}
document.getElementById('back').onclick = function(){
history.back()
}
document.getElementById('go').onclick = function(){
//0为区间 0是自己 1为前进 -1为后退
history.go(-1)
}
  • pushstate 添加一个state(新增历史页面)

  • replacestate 替换state (不会新增历史页面)

document.getElementById('pushstate').onclick = function(){
//第一个参数为数据 state的数据值 第二个 unsend 常用值为"" 第三为url 改变的url
history.pushState('hello',"",'./location对象讲解.html')
console.log(history.state)
console.log(history.length)
}
document.getElementById('replacestate').onclick = function(){
//第一个参数为数据 state的数据值 第二个 unsend 常用值为"" 第三为url 改变的url
history.replaceState('你好',"",'./location对象讲解.html')
console.log(history.state)
console.log(history.length)
}

pushstate及replacestate都会进行的操作

会影响state值 会改变url地址 但是不会跳转页面(页面不会刷新)

navigator

概述

navigator他是属于浏览器的导航对象,里面包含浏览器的相关信息以及你的系统信息。

属性

  • userAgent 用户信息

  • appName

  • appVersion

  • language (国际化)

  • ...

screen 屏幕

概述:

用于屏幕相关信息 (适配

属性

  • width 屏幕宽

  • height 屏幕高

  • availWidth 屏幕可视区宽度

  • availHeight 屏幕可视区高度

  • availLeft 屏幕可视区离左边的距离

  • availTop 屏幕可视区离上边的距离

console.log(screen.width)//宽
console.log(screen.height)//高
console.log(screen.availHeight) //可视区高度
console.log(screen.availWidth) //可视区宽度
console.log(screen.availLeft) //可视区离左边的距离
console.log(screen.availTop) //可视区离上边的距离

document

概述

document是文档对象,他指代的是html整个文档。包含用于操作对应的html文档的相关内容。他是整

个DOM里面最大的对象,他是属于BOM的。

相关属性

  • body 获取body

  • forms 获取所有的表单

  • head 获取head

路由

概述

根据不同的url地址来给你渲染不同的内容,路由主要分为俩种。前端路由,后端路由。

后端路由
后端介绍

后端 主要提供数据的,以及对应的数据进行相关的业务处理。后端用到的语言主要java,php,

node.js...

后端路由

接口路由 (json格式 restful接口)

根据不同的接口返回不同的数据

渲染路由 (ssr 服务器渲染 前后端不分离)

根据不同的url地址来渲染不同的页面 (后端服务器的压力大)

优点

利于seo (搜索引擎优化)

首页渲染速度快 (主页都是做ssr)

缺点

服务器压力大

维护不便

前端路由

根据不同url地址来渲染不同的页面(浏览器解析)

前端路由划分

页面路由 (根据不同的url跳转不同的页面 (刷新操作))

location.href = 地址
location.assign('地址')
location.replace('地址')

hash路由 (根据不同hash值渲染不同的内容 不刷新)

<body>
<button onclick="location.hash = 'hello'">hello</button>
<button onclick="location.hash = 'byby'">byby</button>
<div id="context"></div>
</body>
</html>
<script>
//监听事件 onhashchange
window.onhashchange = function(){
var hash = location.hash
if(hash=='#hello'){
document.getElementById('context').innerHTML = 'hello'
}else{
document.getElementById('context').innerHTML = 'byby'
}
}
</script>

history路由 (根据不同的url来渲染不同的内容 不刷新)

<body>
<button onclick="history.pushState('','','./document.html')">1</button>
<button onclick="history.replaceState('','','./histroy.html')">2</button>
<div id="context"></div>
</body>
</html>
<script>
//监听事件 onpopstate
//等待back 或者 go 或 forwad才触发
window.onpopstate = function(){
if(location.href.search('document')){
document.getElementById('context').innerHTML = 'hello'
}else{
document.getElementById('context').innerHTML = 'byby'
}
}
</script>

hash路由和history路由常用于SPA(单页应用程序)程序,不刷新页面也能实现视图的切换。所以对应

的在vue或者react的路由底层设计中只有俩种模式一种为hash模式一种为history模式。这种程序一般

运用在前后端分离的基础上。

单页应用程序的优缺点

优点

无刷新 减少了页面的回流(重新渲染)

业务更加清晰 代码结构更加明了

缺点

不利于seo

所以为了解决单页应用不利于seo的问题出现了一个新的技术叫预渲染

十二).DOM

DOM概述

DOM 文档对象模型(document object model) 主要是用于操作html文档及相关内容(css)。对于文档

的操作会造成浏览器的重新渲染(重绘(改变的一个元素的显示内容及部分样式) 重排(改变一个元素

的位置及大小尺寸)(回流)重绘不一定重排 重排必定发生重绘)

DOM具备的内容

  • document (属于BOM)文档对象

  • rootElement (根元素)

  • element (所有的标签都是element)

  • attribute (所有标签的属性都是attribute)

  • text (所有标签内容的文本都是text)

从上的包含关系可以看出大的内容可以获取或操作包含的内容也就是

  • document可以获取rootElement

  • document可以获取element

  • element可以获取里面的element

  • element可以获取里面attribute以及text

获取元素相关的操作 document和element都有的方法

  • getElementById 通过id来获取元素(元素 element)

  • getElementsByClassName 通过classname获取所有的符合的元素 (伪数组)

  • getElementsByTagName 通过标签名获取所有符合的元素 (伪数组)

  • getElementsByName 通过name属性来获取符合的元素 (伪数组)

  • querySelector 通过传入选择器来获取符合的第一个元素 (元素 element)

  • querySelectorAll 通过传入选择器获取符合的所有元素 (伪数组)

  • 所有跟上面一致后面带NS表示命名空间获取查找

// 获取元素的方式
// 通过id获取div
console.log(document.getElementById('box'))//传入一个id名返回一个元素(htmlElement)
console.log(document.getElementsByClassName('context'))//传入一个class名返回
HTMLCollection 伪数组
console.log(document.getElementsByTagName('div'))//传入一个标签名返回HTMLCollection
console.log(document.getElementsByName('hello'))//传入一个name属性名返回NodeList 伪
数组
console.log(document.querySelector('.context')) //根据选择器获取匹配的第一个元素
console.log(document.querySelectorAll('#box')) //根据选择器获取所有匹配的 返回
NodeList 伪数组
//通过id为box的div获取里面的a标签
var box = document.getElementById('box')
console.log(box.querySelector('a'))
//document获取根元素
console.log( document.getRootNode())

伪数组具备下标的特性及length属性 但是不具备数组的方法

element操作attribute

getAttribute 获取属性值的方法 setAttribute 设置属性的方法

var element = document.getElementById('box')
//使用getAttribute函数来获取属性值
var value = element.getAttribute('href') //根据属性名获取属性值
console.log(value)
//getAttribute相匹配的setAttribute
element.setAttribute('number','10')//设置属性number值为10
element.setAttribute('number','20')//修改属性number为20

getAttributeNode 获取属性对象 setAttributeNode 设置属性对象

//获取单个的属性节点 及 设置对应的属性节点
var obj = element.getAttributeNode('class') //返回的是一个属性对象
console.log(obj.value)
//传入一个Attr对象 进行设置
// element.setAttributeNode()

获取所有的属性名 getAttributeNames

var attrNames = element.getAttributeNames() //返回所有的属性名
console.log(attrNames)

attributes 属性 (属于element的属性)

//使用attributes来获取所有的属性对象 伪数组 NameNodeMap
console.log(element.attributes)
// console.log(element.attributes)
//访问对应的class属性对象
//利用下标访问
console.log(element.attributes[2])//Attribute对象
//利用key来访问
console.log(element.attributes['class'].value)
//Attribute对象里面的属性值的访问使用value属性方法
console.log(element.attributes[2].value)//class的属性值
//NameNodeMap里面对应的属性的增删改查
var attrs = element.attributes
//查询 getNamedItem item
console.log(attrs.getNamedItem('class').value)
console.log(attrs.item(2).value)
//修改 添加是一样的方法
attrs[0].value = 'http://www.baidu.com'
// attrs.setNamedItem() 传递一个Attr对象来进行修改或者添加
//删除方法
attrs.removeNamedItem('hello')

element都具备的属性 (直接赋值或者获取)

  • id

  • className

  • tagName

  • titile

  • style

  • innerHTML 显示的html代码 和 innerText 显示的文本

console.log(box.innerHTML) //直接获取里面所有的html代码 赋值的时候会自动识别html代
码(xss攻击)
box.innerHTML = '<font color="red">hahahah</font>'
// console.log(box.innerText) //只会获取文本 不会识别html代码
box.innerText = '<font color="red">hahahah</font>'
  • 对于本身自带的属性可以直接点的形式来进行获取或者赋值

//对于element元素本身自带的属性 可以直接设置 a标签本身自带href input标签本身自带value属性
//通过直接点出来的形式进行设置和获取
// id class style 标签名 title
var box = document.querySelector('div')
console.log(box.id)
console.log(box.className)
console.log(box.style) //返回一个样式对象
console.log(box.tagName) //标签名
console.log(box.title) //标签名
//对于标签本身不自带的属性 出现的值为undefined
console.log(box.hi)
box.id = 'box'
box.className = 'context'
//直接设置a标签的href属性
element.href = 'http://www.bilibili.com'

元素内样式的操作

获取样式

style 返回一个样式对象 通过样式对象.样式名来获取

//获取样式
var div = document.querySelector('div')
//第一种方式 使用style属性进行获取
console.log(div.style) //返回的是一个对象 样式对象 里面包含的对应的样式
console.log(div.style[0]) //获取style里面包含的样式 fontsize样式名 (如果不是内嵌的样式
那么使用style属性无法获取)
console.log(div.style.fontSize)//获取style属性中包含的fontSize的样式值
//如果要获取style属性中包含样式(内嵌的样式)那么使用element.style.样式名(使用驼峰命名)
//但是这种获取方式无法获取外联样式及内联样式 只能获取内嵌
console.log(div.style.color)

getComputedStyle 返回一个样式对象 通过样式对象.样式名来获取

//第二种获取方式 通过对应的方法来进行获取 (兼容问题)
//第一个参数需要被获取的元素 这个返回的style里面是有内置的默认值的 如果你设置的样式他就会覆盖这
个默认值
console.log(getComputedStyle(div))//属于window的方法 返回的是样式对象 他可以获取所有的
样式
console.log(getComputedStyle(div).position)
console.log(getComputedStyle(div).color)
console.log(getComputedStyle(div).fontSize)

存在兼容问题

//兼容问题解决
//兼容问题 ie的
// element.currentStyle
function getStyle(ele,name){
return getComputedStyle?getComputedStyle(ele)
[name]:element.currentStyle['name']
}

设置样式

style来进行设置

优点

  • 可以单独对于某一个样式进行精确控制

缺点

  • 每次只能设置一个样式

  • 他会发生多次重绘和重排 (效率低)

//设置样式
// 第一种方式 (每次设置只能设置一个)
div.style.fontWeight = 700
div.style.fontSize = '40px'

通过指定class名字来设置

//第二种设置通过class名字来设置(一次性可以设置多个样式)
div.className = 'font'

优点

  • 可以一次性设置多个样式

  • 只会发生一次重绘和重排 (效率高)

缺点

  • 没有办法做到随意更改

创建对应的内容

  • 创建元素 createElement

  • 创建属性 createAttribute

  • 创建文本 createTextNode

  • 创建片段 createDocumentFragment

//创建元素
var ele = document.createElement('a') //传入标签
console.log(ele)
//创建属性
var attr = document.createAttribute('href')//传入属性名 通过value来进行赋值
attr.value = 'http://www.baidu.com'
console.log(attr)
//将attr和ele联系在一块 建议使用setAttribute
ele.setAttributeNode(attr)
//创建文本节点 传入对应的文本内容
var text = document.createTextNode('你好')
console.log(text)
//appenChild 建议使用innerText
ele.appendChild(text) //将文本添加到元素中
console.log(ele)
//创建片段
// document.createDocumentFragment

节点相关操作

节点主要分为三类 分别为

  • 元素节点 (element)

  • 属性节点 (attribute)

  • 文本节点(textNode)

关于节点的属性

  • nodeType 节点类型

  • nodeName 节点名

  • nodeValue 节点值

var ele = document.createElement('a') //传入标签
//创建属性
var attr = document.createAttribute('href')//传入属性名 通过value来进行赋值
attr.value = 'http://www.baidu.com'
//创建文本节点 传入对应的文本内容
var text = document.createTextNode('你好')
console.log(ele.nodeType)//1
console.log(attr.nodeType)//2
console.log(text.nodeType)//3
console.log(ele.nodeName)//标签名 大写
console.log(ele.tagName)//标签名 大写
console.log(attr.nodeName)//属性名
console.log(text.nodeName)//#text
console.log(ele.nodeValue)//null
console.log(attr.nodeValue)//属性值
console.log(text.nodeValue)//文本值

节点操作的方法

节点的添加
  • append 追加到后面(支持追加多个)

  • appendChild (追加到最后)

  • insertBefor (插入到某个子元素之前)

  • insertAdjacentHTML (插入html)

  • insertAdjacentText (插入文本)

  • insertAdjacentElement (插入元素)

var ele = document.createElement('a') //传入标签
var ele1 = document.createElement('p') //传入标签
//将a标签添加到div中
var box = document.getElementById('box')
//追加子元素 在后面添加
box.appendChild(ele)
//插入到b的前面 第一个参数是需要插入的元素 第二个参数是子元素
//不允许重复插入同一元素 插入同一元素只会改变位置
box.insertBefore(ele,box.querySelector('b'))
//append追加
// box.append(ele)
// box.append(ele,ele1) //支持追加多个
box.append('<b>hello</b>')//如果传入的是string类型 他会当作文本节点插入
//插入对应元素的
//传入俩个参数 位置 内容
// 位置字符串 beforeBegin afterBegin 开始 beforeend aterend 结尾
box.insertAdjacentHTML("afterbegin",'<header>hahahah</header>') //插入html
box.insertAdjacentHTML("beforeBegin",'<header>hahahah</header>') //插入html
box.insertAdjacentHTML("beforeend",'<header>hahahah</header>') //插入html
box.insertAdjacentHTML("afterend",'<header>hahahah</header>') //插入html
// box.insertAdjacentText() //插入文本
box.insertAdjacentText("afterend",'<header>hahahah</header>') //插入文本
// box.insertAdjacentElement() //插入元素
box.insertAdjacentElement("beforeBegin",document.createElement('footer'))
删除
  • remove 移除本身

  • removeChild 传入对应的子节点进行删除

//remove移除自身的所有
// box.remove()
// removeChild删除子元素
box.removeChild(box.querySelector('b'))
替换
  • replaceChild 替换子节点

  • replaceChildren 替换所有的子节点

//替换子节点 用于替换的节点 子节点
box.replaceChild(document.createElement('div'),box.querySelector('b'))
//替换所有的子节点 replaceChildren
box.replaceChildren(document.createElement('canvas'),document.createTextNode('文
本'))
克隆节点 (返回新的节点)
  • cloneNode 返回一个新的节点传入一个boolean类型的值是否深度克隆

//cloneNode 传入一个boolean类型的值 是否深度克隆 默认值为false
console.log( box.cloneNode()) //只会克隆自身 里面的内容不会进行克隆
console.log( box.cloneNode(true)) //会克隆所有的内容
判断是否存在子节点
//判断是否存在子节点 (不包含属性节点)
console.log(div.hasChildNodes())

判断是否存在属性的相关方法

  • hasAttribute 判断是否存在指定属性

  • hasAttributes 判断是否存在属性

<div hello="你好"></div>
<script>
var div = document.querySelector('div')
//判断是否存在指定属性(必须声明) 返回值boolean
console.log(div.hasAttribute('hello'))//true
console.log(div.hasAttribute('id'))//false
</script>

节点的关系 (关系查询)(只读属性)

父子关系
  • childNodes 获取的子节点

  • children 获取所有的子元素节点

  • firstChild 获取第一个子节点

  • firstElementChild 获取第一个子元素节点

  • lastChild 获取最后一个节点

  • lastElementChild 获取最后一个子元素节点

  • parentNode 父节点

  • parentElement 父元素节点

//获取子节点
console.log(div.childNodes)//所有的子节点 NodeList 包含空文本节点
console.log(div.childNodes.length)//所有的子节点的个数
//获取所有的子元素节点
console.log(div.children)//所有的子元素节点 HTMLCollection
console.log(div.children.length)
//获取第一个子节点
console.log(div.firstChild)
//获取第一个元素节点
console.log(div.firstElementChild)
//获取最后一个子节点
console.log(div.lastChild)
//获取最后一个元素节点
console.log(div.lastElementChild)
//获取父节点 parent
console.log(div.parentNode) //父节点
console.log(div.parentElement) //父元素
兄弟关系
  • previousSibling 前一个节点

  • previousElementSibling 前一个元素节点

  • nextSibling 后一个节点

  • nextElementSibling 后一个元素节点

//兄弟关系
console.log(div.previousSibling) //前面兄弟节点
console.log(div.previousElementSibling) //前面的兄弟元素节点
console.log(div.nextSibling) //后面兄弟节点
console.log(div.nextElementSibling) //后面的兄弟元素节点

去除空文本节点

function removeEmptyTextNode(node) {
//去除所有的空文本节点 删除
var childs = node.childNodes //获取所有的子节点
//遍历 伪数组实现了迭代iterator 可以被for of遍历
for (var child of childs) {
//找到所有的空文本节点
if (child.nodeType == 3 && child.nodeValue.trim() == '') {
child.remove()
}
}
}

十三).DOM和BOM回顾及事件讲解(上)

BOM (bowser object model)浏览器对象模型

  • window 窗口对象 (全局的变量及函数都是属于window (global对象))

  • location 地址栏对象 (获取地址栏内容 使用地址栏跳转页面)

属性:hash、search、host、hostname、pathname、port、protocol、origin、href

方法:assign 、replace

  • history 历史对象

属性:state、length

方法:forward 、back、go、pushState、replaceState

  • screen 屏幕对象

属性:width、height、availWidth、availHeight..

  • navigator 导航对象

属性:userAgent

  • frames 窗口(window)

DOM (document object model) 文档对象模型

  • document 文档对象(整个html文档)

  • element 元素对象 (所有的标签)

  • attribute 属性对象 (所有标签里面的属性)

  • text 文本对象 (显示的文本)

dom里面的知识点:

  • 空文本(换行符、制表符、空格..)也是一个文本节点

  • 属性如果是自身自带的属性 可以直接点出来(input.value、img.src 、a.href ...)如果不是就不能

点出来。

  • dom会操作对应的html的内容、当你的html内容发生变化、那么对应的页面就会重新渲染。会造

成大量的重绘和回流。(后续dom操作要被舍弃(效率))

重绘和回流(回流必定重绘)

  • 重绘对应一个元素操作 (不改变他的宽高和对应的位置 而改变其他的样式)

  • 回流对应的元素操作 (改变对应的元素所有的内容 宽高和对应的位置都会发生变化)

事件

概述:

事件是一个异步机制。他相当于一个 执行者执行 -- 观察者观察 --- 处理函数执行 这个流程称为事件。

事件的组成

  • 事件名 (内置的)

  • 执行对象 (元素对象)

  • 处理函数 (自定义函数)

  • 观察者 (js的事件引擎)

例子

我去便利店买包华子、老板告诉我没货。我跟老板说等有货通知我。货来了老板就通知我了,我去就买

了。

分析

事件名 ---- 买华子

执行者 ---- 我

观察者 --- 老板

处理 --- 买了

示例

<button>点击</button>
button.onclick = function(){
console.log('点击了')
}

事件名 click(内置的)

执行对象 button (元素对象)

处理函数

后续的函数 (自定义函数)

观察者 (js引擎 事件引擎)

在js内书写的事件,他的观察者都是事件引擎。 我们需要关注就是前面的三个内容(事件名、执行对

象、处理函数)

事件的声明书写方式

内联模式(在标签内部)

<!-- 内联模式就是在onclick书写中设置对应的代码 -->
<button onclick="alert('你好')">点我</button>
<button onclick="fn(1)">点我</button>
<script>
function fn(arg){
alert('我是函数执行'+arg);
}
</script>

脚本模式 (在script中 常用)

//脚本模式的写法 事件名 执行对象 处理函数
var btn = document.getElementsByTagName('button')[2]
//执行对象.on+事件名 = 处理函数
btn.onclick = function(){
console.log('你好');
}
function handler(){
console.log('处理函数执行了');
}
btn.onclick = handler

内联模式和脚本模式的区别

  • 内联模式里面的函数需要手动调用 而对应的脚本的模式的处理函数自动调用

  • 内联模式里面的函数调用的this指向window 对应的脚本模式里面的处理函数的this指向当前的调

用者

事件名的分类

鼠标事件(鼠标触发的 mouse)

  • (课来课)click 单击事件

  • (都克莱克)dblclick 双击事件

  • (毛思)mouse down(到了) 按下

  • mouse (奥普)up 弹起

  • mouse (恩特)enter 移入

  • mouse (李屋)leave 移出

  • mouse (哦屋)over 移入

  • mouse (奥特)out 移出

  • mouse (目屋)move 移动

  • (肯太k思)context (麦钮)menu 右键点击

注意事项
  • click和对应mousedown和mouseup的执行顺序(mousedown -- mouseup -- click)

  • mouseenter(mouseleave)和mouseover(mouseout)的区别 (mouseenter 子元素不会触发

mouseover子元素会触发)

<div>我是一个div
<p>你好</p>
</div>
<script>
//获取div
var div = document.querySelector('div')
//脚本模式 元素对象.on+事件名 = 处理函数
div.onclick = function(){
console.log('单击事件执行了');
}
//双击事件必定触发单击
div.ondblclick = function(){
console.log('双击事件执行了');
}
//鼠标的按下和弹起 执行顺序 按下 -- 弹起 -- 单击
div.onmousedown = function(){
console.log('鼠标按下');
}
div.onmouseup = function(){
console.log('鼠标弹起');
}
//鼠标的移进和移出
//enter和leave 进入子元素不会触发
div.onmouseenter = function(){
console.log('鼠标移进去了');
}
div.onmouseleave = function(){
console.log('鼠标移出了');
}
//over和out 进入子元素也会触发
div.onmouseover = function(){
console.log('鼠标over移入了');
}
div.onmouseout = function(){
console.log('鼠标out移出了');
}
//鼠标的移动
div.onmousemove = function(){
console.log('鼠标移动了');
}
//鼠标点击右键 (点击事件不会触发)
div.oncontextmenu = function(){
console.log('右键点击了');
}
</script>

键盘事件 (键盘触发的 key)

  • keydown 按下

  • keyup 弹起

  • keypress 字符串(除功能键的其他键)

<input type="text">
<script>
//获取输入框
var input = document.querySelector('input')
//键盘相关事件
input.onkeydown = function(){
console.log('键盘按下');
}
//在input 弹起会出现俩次
input.onkeyup = function(){
console.log('键盘弹起');
}
//字符键按下 (除了功能键)
input.onkeypress = function(){
console.log('字符键按下');
}
window.onkeypress = function(){
console.log('在window上触发了keypress事件');
}
</script>
注意事项
  • 执行顺序 keydown - keypress - keyup

  • keypress 一定会触发 keydown

HTML事件 (HTML中自带的一些事件(只能用于专门HTML))

  • window的事件

  • 加载事件(load)

  • 卸载事件 (unload)

  • 关闭窗口的事件 (onclose)

  • 打印之前调用的事件 (onbeforeprint)

  • 卸载之前调用的事件 (onbeforeunload)

  • 滚动栏发生变化的事件 (onscroll)

//加载事件
window.onload = function(){
console.log('hello');
}
//卸载事件(刷新)
window.onunload = function(){
console.log('卸载');
}
//关闭窗口事件 (清除相关缓存和对应的资源操作)
window.onclose = function(){
console.log('关闭');
}
//打印之前调用 调用print方法之前 更改打印的然后继续打印
window.onbeforeprint = function(){
console.log('打印了');
}
//在卸载之前调用 将一些内容回收
window.onbeforeunload = function(){
console.log('卸载之前');
}
//滚动栏滚动
window.onscroll = function(){
console.log('滚动栏位置发生变化');
}
//加载事件的其他使用
var image = new Image() //构建一个图片对象
image.onload = function(){ //如果图片加载好了
image.src = '图片地址' //给图片设置对应的src地址
}
//onscroll的其他使用
document.querySelector('div').onscroll = function(){
console.log('div的滚动栏滚动了');
}

表单的事件

  • (思麦特)submit 表单提交事件

  • (维晒特)reset 表单重置事件

  • (搜来克特)select 内容被选中(只针对于输入框和文本域)

  • change 表单的value值发生变化

  • input 可输入的表单标签里面进行输入

  • blur 失去焦点

  • (佛给思)focus 获取焦点

//表单相关的事件
//加给整个form表单的事件
document.forms[0].onsubmit = function(){
console.log('提交了');
}
document.forms[0].onreset = function(){
console.log('重置了');
}
//change value值发生变化 失去焦点
document.querySelector('input').onchange = function(){
console.log('值变化了');
}
// input 输入对应的值的时候调用
document.querySelector('input').oninput = function(){
console.log('输入值');
}
//select 选择里面的内容
document.querySelector('input').onselect = function(){
console.log('选择值了');
}
//失去焦点
document.querySelector('input').onblur = function(){
console.log('失去焦点了');
}
//聚焦
document.querySelector('input').onfocus = function(){
console.log('获取焦点了');
}

相关聚焦和失焦的方法

  • focus 聚焦的方法

  • blur 失焦的方法

//element也具备 focus() blur()
document.querySelector('input').focus()
setTimeout(function(){
document.querySelector('input').blur()
},3000)

event 事件源对象

概述

event 被称为事件源对象,他是一个全局的内置对象(属于window),他里面包含了一些关于事件执

行的相关的属性。

处理函数中Arguments

处理函数也是一个函数,函数就具备一个arguments的属性。argments是一个伪数组。那么就可以知

道对应的处理函数里面也拥有arguments

var btn = document.querySelector('button')
btn.onclick = function(){
//点击事件触发以后对应的arguments里面有一个对应的pointEvent的对象
console.log('arguments',arguments);
console.log(`arguments[0]`, arguments[0]);
//这个对象是在arguments里面存在第一位里面以属性为主 那么这个对象表示什么
//这个参数其实就对应的事件的一些内容 称为事件源对象 event
}
window.onkeydown = function(){
console.log('arguments',arguments);
//keyboardEvent 键盘的事件源对象
console.log(`arguments[0]`, arguments[0]);
}

从上述代码可得到对应的事件执行的处理函数里面会传递一个参数 ,这个参数的类型是一个event。这

个处理函数的arguments数组里面只有一个元素。

故而我们可以简写为

//这个地方的e 一般写做 e 或者是 event
btn.onclick = function(e){
//这个地方的e表示是第一个参数 也就是对应的event对象 这个event在这个里面有兼容问题
//兼容写法
e = e || window.event
//这个e表示的就是第一个参数 那么他也就是我们对应的event对象
console.log(`e`, e);
}

event的常用属性

鼠标坐标的相关属性

  • (思棍)screenX 表示鼠标离屏幕的X距离

  • screenY 表示鼠标离屏幕的Y距离

  • (配几)pageX 表示当前的鼠标离页面的X距离(包含卷起部分)

  • pageY 表示当前的鼠标离页面的Y距离(包含卷起部分)

  • (凯恩特)clientX 表示鼠标离页面可视区的X距离

  • clientY 表示鼠标离页面可视区的Y距离

  • (奥符赛特)offsetX 表示鼠标离父元素偏移的X距离

  • offsetY 表示鼠标离父元素偏移的Y距离

按钮辅助的相关属性

  • ctrlKey 是否按下了ctrl键

  • altKey 是否按下了alt键

  • shiftKey 是否按下shift键

//辅助的按键属性 返回布尔类型值
console.log(e.ctrlKey);//是否长按ctrl
console.log(e.altKey);//是否长按alt
console.log(e.shiftKey);//是否长按shift

鼠标相关的属性

  • button 按下是那个键

//按下的键是什么 三个值 0 1 2
console.log(e.button);//左键0 中间滚轮1 右键2

事件及相关触发的属性

  • type 当前的事件类型

//事件类型 返回事件名
console.log(e.type); //click
  • target 表示当前事件的触发对象

  • currentTarget 表示加事件的元素

//触发者元素
console.log(e.target);
//加事件的元素
console.log(e.currentTarget);

键盘相关的属性

  • (k)key 表示当前按下的键的字符(区分大小写)

  • (k考的)keyCode (在keydown里面 不区分大小 全部是大写的ascii码)

  • code (key+大写的字符)

  • ( 拆口的)charCode (在keypress里面才管用 他区分大小写 返回对应的ascii码)

window.onkeypress = function(e){
console.log(e.code);//当前的按钮按下键是哪个一个 返回的是一个key+大写字母
console.log(e.keyCode);//当前按下键的大写字母 ascii码
console.log(e.charCode); //当前字符的ascii码 keypress里面
console.log(e.key);//表示当前按下的键 (兼容问题)
}

事件委托机制(事件代理)

概述:将对应的事件委托给对应的父元素,然后通过对应的e.target获取实际的触发元素进行操作。

示例

//事件委托的写法
//获取需要加事件的父元素
var ul = document.querySelector('ul')
//给ul添加事件
//给父元素添加事件
//在内部判断触发元素 e.target
//对应执行操作
ul.onclick = function (e) {
e = e || window.event
//判断当前触发元素是否为li
if (e.target.tagName == 'LI') {
//排他思想
//把所有的li的class清除
for (var item of this.children) {
item.className = ''
}
e.target.className = 'active'
}
}

事件委托的运用场景

在一个父元素里面有多个子元素要添加相同的事件的时候

十四).事件下

事件捕获(event capturing)

事件捕获是由外到内的。在事件捕获阶段,事件会从DOM树的最外层开始,依次经过目标节点的各个父节点,并触发父节点上的事件,直至到达事件的目标节点。如下图

事件冒泡(dubbed bubbling)

上面我们说了事件捕获,事件冒泡和事件捕获是完全相反的。事件冒泡是由内到外的,它是从目标节点开始,沿父节点依次向上,并触发父节点上的事件,直至文档根节点,就像水底的气泡一样,会一直向上。如下图。

上图的例子中,事件处理顺序是 Window<=Document<=box1<=box2<=a
上面我们说到,addEventListener的第三个参数默认值是false,表示在事件冒泡阶段调用事件处理函数,这说明事件监听器默认监听冒泡事件。

阻止事件捕获和事件冒泡

在我们了解事件捕获和事件冒泡后,难免会思考一个问题:如果我们在某个节点上绑定了一个事件,我们需要在点击时触发这个事件,但是由于事件冒泡,这个节点的事件被它的子元素触发了,我们应该怎么阻止这种情况发生呢?

我们可以使用stopPropagation()方法来阻止这种情况发生,它将阻止事件沿着DOM树向上或向下进一步传播。如下图。

事件监听器(兼容问题)

  • addEventListener 添加事件监听器(可以添加多个处理函数)

  • removeEventListener 移除事件监听器 (只能移除addEventListener添加的 移除根据对应的处理

函数是否为一个)

//我需要俩个处理函数 事件监听器 可以有多个处理函数
//监听对应的事件执行 来执行对应的处理函数 (不会覆盖之前的事件的处理函数)
//传入事件名 传入处理函数 (如果是直接传入function 就不会被移除)
btn.addEventListener('click',function(){
console.log('点击了按钮');
})
btn.addEventListener('click',function fn(){
console.log('点击了按钮1');
})
btn.addEventListener('click',handler1)
//移除事件监听器 必须处理函数是同一个 不然不能被移除 只能移除addEventListener添加的
btn.removeEventListener('click',function fn(){
console.log('点击了按钮1');
})//不能移除
btn.removeEventListener('click',handler1) //能
btn.removeEventListener('click',handler)//不能移除

拖拽

基础三大事件

1. 鼠标按下事件 (mousedown)

2. 鼠标移动事件(mousemove)

3. 鼠标弹起事件(mouseup)

在页面进行拖拽

步骤

1. 给对应的盒子添加鼠标按下事件

2. 在鼠标按下事件内容获取鼠标在对应盒子里面的位置 (offsetX)

3. 在鼠标按下事件中给document添加移动事件

4. 在移动的时候获取鼠标在页面上的位置(pageX)

5. 计算对应的定位的位置 (鼠标在页面上的位置 - 鼠标在对应盒子内的位置)

6. 设置对应的盒子的位置

7. 在鼠标按下事件中给document添加弹起事件

8. 在弹起事件内移除对应的移动事件

<div></div>
<script>
//获取div
var div = document.querySelector('div')
//在div里面按下
div.onmousedown = function(e){
e = e || window.event
//获取按下时 鼠标在对应盒子里面位置
var currentX = e.offsetX
var currentY= e.offsetY
//在文档里面移动
document.onmousemove = function(e){
//获取每次移动在页面上的位置 - 对应的按下时鼠标在盒子里面的位置 = 对应的定位的位
置
var targetX = e.pageX - currentX
var targetY = e.pageY- currentY
//设置给对应的盒子
div.style.left = targetX + 'px'
div.style.top = targetY + 'px'
}
//在文档里面弹起
document.onmouseup = function(){
document.onmousemove = null
}
}
</script>

在区间进行拖拽

(奥佛赛特)offset家族(属性 元素对象)

  • offsetParent(派瑞特) 偏移父元素 (找离最近的定位父元素 如果没有定位就找body)

  • offsetHeight 偏移元素的高度

  • offsetWidth 偏移元素的宽度

  • offsetLeft 离父元素偏移的左边的距离 (number类型)

  • offsetTop 离父元素偏移的上边距离 (number类型)

<div class="outerBox">
    <div class="box">
        <div class="moveBox"></div>
    </div>
</div>
<script>
//获取移动的盒子
var move = document.querySelector('.moveBox')
//获取区间的大盒子
var box = document.querySelector('.box')
//给移动的盒子添加按下事件
move.onmousedown = function (e) {
//获取在移动的盒子里面按下的位置
e = e || window.event
var currentX = e.offsetX
var currentY = e.offsetY
//给区间的大盒子添加移动
box.onmousemove = function (e) {
e = e || window.event
//设置move这个盒子在box里面定位
//鼠标在页面的位置 e.pageX - 大盒子在页面的位置 - 鼠标按下的位置 currentX
var distance = computedPointElementInPage(this)
//接收对应的设置的定位位置
var targetX = e.pageX - distance.left - currentX
var targetY = e.pageY - distance.top - currentY
//进行区间判断
//最大的移动距离X = 大盒子宽度 - 小盒子的宽度
var maxX = this.offsetWidth - move.offsetWidth
var maxY = this.offsetHeight - move.offsetHeight
//如果当前的定位位置比0还小 设置为0
if(targetX < 0){
targetX = 0
}
if(targetY < 0){
targetY = 0
}
//如果比最大值还大 就设置最大值
if(targetX > maxX){
targetX = maxX
}
if(targetY > maxY){
targetY = maxY
}
//位置设置
move.style.left = targetX + 'px'
move.style.top = targetY + 'px'
}
document.onmouseup = function(){
//清除大盒子的移动事件
box.onmousemove = null
}
}
//如果要找盒子在页面上的位置 那么外面要从自己的基于的父元素的距离 + 对应父元素基于他的父元
素距离 .. 直到找到body
//封装一个方法计算盒子在页面上的位置 传递一个盒子
function computedPointElementInPage(element) {
//封装一个距离对象 left离左边的 top离上边的
var distance = {
left: 0,
top: 0
}
//到了body就是null 到了body对应while循环就结束
while (element.offsetParent) {
//将对应的左边的距离一个个+
distance.left += element.offsetLeft
//将对应的上边的距离一个个+
distance.top += element.offsetTop
//进入到父元素
element = element.offsetParent
}
//将计算好的结果返回出去
return distance
}
</script>

样式获取

  • style属性 只能获取内嵌样式

var div = document.getElementsByTagName('div')[0]
//style的弊端 他只能获取对应的内嵌的样式 也就是只能获取style属性里面写的内容
console.log(div.style.width); //空字符串
console.log(div.style.height); //300px
  • getComputedStyle 方法可以获取所有的样式

//对应的兼容获取所有样式的方法
var style = getComputedStyle(div, '')
console.log(style); //getComputedStyle获取样式对象里面都有默认值(所有的样式)
console.log(style.backgroundColor);
console.log(style.color);
console.log(style.width);
console.log(style.height);
  • currentStyle ie的低版本兼容

console.log(div.currentStyle); //ie低版本兼容 废弃
  • 兼容封装

// getComputedStyle 兼容问题
// console.log(div.currentStyle); //ie低版本兼容 废弃
//兼容写法 传入一个元素 返回一个样式对象
function getStyle(element) {
var style = window.getComputedStyle ? getComputedStyle(element, '') :
element.currentStyle
return style
}

十五).cookie

关于计算机的一些基础课程

  • 数据结构 (leetcode)

逻辑结构

存储结构 (数组,字符串,栈,队列,图,hash,树、链表...)

算法 (递归算法(BFS(广度优先查找),DFS(深度优先查找))、 动态规划 (DP)、贪心算法

(马拉车算法)、排序算法....)

  • 操作系统 (系统相关的操作)

windows 、 linux (指令 服务器部署) 、 mac

  • 计算机组成原理

冯诺依曼 (cpu 内存条 硬盘 ( 显卡))

  • 计算机网络

网络的构成及相关的协议

计算机网络

概述:
  • 将不同的计算机通过网络应用使用对应的传输介质进行联通

关键名词:

  • 网络应用(校园网拨号、宽带连接)

  • 传输介质(网线、wifi)

网络协议及其构成

概述:
  • 网络OSI构成由七层构成。(物理层、数据链路层、网络层、传输层、会话层、表示层、应用

层)。

TCP和UDP(传输层的通信协议 基于网络层的IP协议)

TCP使用对应的连接进行通信的,也就是说对应的通信设备一定要建立连接。(外卖配送)

UDP采用丢包的形式进行通信的,就是说对应的服务端只需要将对应的数据报包发送就好了,客户端需

要去获取对应的数据报包。(菜鸟驿站)

应用层的相关协议
  • FTP 文件传输协议

  • TFTP 简单的文件传输协议

  • SNMP 邮件传输协议

  • HTTP 超文本传输协议 (基于TCP/IP)

  • HTTPS 安全的超文本传输协议 (基于TCP/IP)

  • P2P 金融传输协议

  • NFS 蓝牙传输协议

  • DNS 域名传输协议

  • ....

HTTP和HTTPS

HTTP采用明文传输(不安全 能获取 能篡改)

HTTPS采用密文传输 (里面采用了openssl 里面有对称加密 和 非对称加密 及 CA证书)

后端和前端

  • 后端是提供对应业务操作(提供数据)

  • 前端是提供对应的页面渲染操作

后端和前端建立连接的根本是网络传输协议 (HTTP和HTTPS)

HTTP的几大特性

  • 无状态 (没有状态)

没有办法区分对应的用户。现在a用户登录淘宝,b用户也登录淘宝,a用户和b用户他没有办法区

分(a用户拿到了b用户的页面 b用户拿到了a用户的页面)

  • 无连接

就是发送完数据建立完连接就会马上断掉。连接不能保持(每次连接完毕 连接会马上断掉)。

  • 长连接/短连接

解决HTTP的无状态问题

  • 因为对应的http的无状态问题导致我们可能在进行某项操作的时候,对应的后端不能准确的区分对

应的访问者,这个时候我们就需要给每个对应的连接添加一个标识(sessionID)。sessionID需

要每次都带过去,那么我们应该放在哪?

  • 这个时候cookie就应运而生了。(cookie就是用来存放sessionID)。每次请求都要带上cookie才能

区分对应的访问者。那么我们解决http的无状态问题就是通过发送对应的cookie来解决的,这个

cookie里面会带上一个sessionID来用于区分。cookie是存放在浏览器上的。

Cookie

概述:cookie是为了解决http的无状态问题产生的,(cookie里面存储sessionID的方式来解决http的

无状态问题),cookie是存放在浏览器上的。

cookie的特性

  • 存储在浏览器上的

  • cookie的存储大小一般是4kb左右

  • cookie会随请求而发送

  • cookie可以跨域

  • cookie可以设置过期时间

  • cookie可以被篡改也可以伪造(cookie不安全)

cookie存储格式

key=value;expires=日期;path=地址;domain=域名;secure安全
  • key=value 键值对象

  • expires 过期时间设置

  • path 在对应的路径下才携带cookie

  • domain 跨域的域名

  • secure 安全(写了就是安全没写就不安全)

cookie相关操作

cookie是一个容器(位于浏览器上) 具备增删改查的方法(document.cookie || chrom.cookies)

  1. 增 (给对应的cookie里面进行赋值)

//对应cookie进行赋值 增操作
var date = new Date(2022,10,22)
document.cookie = `name=123;expires=${date}`
  • 删 (设置对应的过期时间)

//删除操作 就是设置过期时间为对应的当前时间 并不会马上删除 而是对应的浏览器关闭以后才会删
除
//如果当前时间已经过了 那么对应的cookie还是会存在
var date = new Date()
document.cookie = `name=jack;expires=${date}`
  • 改 (给对应的cookie里面的存在的key进行重新赋值)

//修改就对于已经有的key进行重新赋值操作
//如果没有设置过期时间 那么默认的过期时间是session session的过期时间是浏览器关闭就清除
document.cookie = 'name=jack'
  • 查 (不赋值直接读取对应的cookie)

//查询cookie 不赋值就是查询 显示的时候只会显示对应的key=value 其他并不会显示
console.log(document.cookie);

cookie相关操作封装

// 封装cookie相关操作
//获取方法 通过对应的key 获取对应的cookie的值
function get(key){
var cookie = {} //用于存储所有的cookie相关的内容的
//获取对应cookie的字符串 age=123; name=123
var cookieStr = document.cookie
var cookies = cookieStr.split(';')
for(var c of cookies){
//对应name=123进行分割
var cookieArr = c.trim().split('=')
//将第一个值当作key 第二个当作value {name:123}
cookie[cookieArr[0]] = cookieArr[1]
}
return cookie[key]
}
//封装一个设置相关方法
//key=value;expires=时间;path=地址;domain=域名;secure
function set(key,value,expires,path,domain,secure){
//如果没有key 直接报错
if(!key){
throw new Error('必须填入key值')
}
//如果没有value将value设置为空字符
if(!value){
value = ""
}
var cookieStr = `${key}=${value}`
//如果对应的传入过期时间是日期类型
if(expires instanceof Date){
cookieStr += `;expires=${expires}`
}
// 如果有path
if(path){
cookieStr += `;path=${path}`
}
//如果有domain
if(domain){
cookieStr += `;domain=${domain}`
}
// 如果有secure
if(secure){
cookieStr += `;secure`
}
document.cookie = cookieStr
}
//删除方法 设置对应的过期时间
function remove(key,value,expires){
set(key,value,new Date())
}

第三方封装的cookie

cookie.js (vue里面采用了)

1.下载(js文件)

2.打开官网 (看对应的demo)(看源码)

<script src="./cookie.min.js"></script>
<script>
//核心对象 window.Cookies
//get 获取的 set 设置 remove 删除
Cookies.set('name','tom')
console.log(Cookies.get('name'));
Cookies.remove('name','tom')
</script>
核心方法
  • set 设置方法

  • get 获取方法

  • remove 删除方法

JSON格式

概述: json是一种数据传输的格式,对应的数据传输中以后会常用到json。json传输格式的好处(跨平

台、跨语言)

json格式的表示形式

对象 {}

var json = {}

数组 []

var json = []
json文件里面的key必须是字符串,字符串必须是双引号括起来。

SON的序列化和反序列化

序列化 (就是将一个json对象转为对应的字符串)JSON.stringify()

//JSON.stringify() //转为json格式的字符串
var jsonStr = JSON.stringify(obj)
console.log(jsonStr);

反序列化 (将一个json格式字符串转为对应的对象)JSON.parse()

//JSON.parse() //将json格式的字符串转换对应对象
var obj = JSON.parse(jsonStr)
console.log(obj);

本地缓存

localStorage 本地缓存(持久化存在)

//setItem 设置元素 key value
//getItem 获取元素 通过key来获取value
// removeItem 移除元素 根据key删除对应元素
localStorage.setItem('username','jack')
console.log(localStorage.getItem('username'));
localStorage.removeItem('username')

sessionStorage 本地缓存 (跟服务器的session一致 关闭浏览器就没有了)

sessionStorage.setItem('password','123')
console.log(sessionStorage.getItem('password'));
sessionStorage.removeItem('password')
localstorage (作为客户端缓存)和sessionStorage (做服务端的缓存 用户信息)区别
  • localStorage 会持久化存在

  • sessionStorage 浏览器关闭就没有了

  • localStorage和sessionStorage存储地址不一样

localStorage和cookie的区别

  • localStorage存储大小为5M左右 cookie只有4kb左右

  • cookie会随请求发送 localStorage不会

  • cookie中存储的数据对应的类型较小(图片的base码) localStorage可以存储

  • cookie可以跨域 localStorage不行

  • cookie跨域设置对应的过期时间 localStorage不能(主动删除不然一直都在)

  • cookie和对应的localStorage存储位置不一样

共同点

  • localStorage和cookie都是存放对应的字符串

  • localStorage和cookie都是位于浏览器上

十六).正则

正则概述

正则是用于检验对应的字符串的一种特殊表达式,一般用于用户格式验证。

正则对象声明

  • 使用//来声明(常用的)

var regexp = /a/ig
log(regexp)
  • 使用new关键词声明

//使用new关键字 g表示全局
//第一个参数填写相关正则表达式 添加修饰符(匹配模式) g 表示 全局 i 表示 不区分大小写 m 表示
换行
var regx = new RegExp('abc','g')
console.log(regx);

正则的匹配修饰符

  • g 全局

  • i 不区分大小写

  • m 换行

  • s 单个匹配

正则表达式中的元字符

^表示开头
// ^ 开头
var regx1 = /^a/ //表示以a开头
//字符串支持正则的四个方法 match 匹配 search 查找 split 分割 replace 替换
console.log('abc'.match(regx1));//['a']
$表示结尾
// $ 结尾
var regx2 = /b$/ //以b结尾
console.log('abc'.match(regx2));//null
[]表示其中一个元素
//[] 表示其中任意一个元素
var regx3 = /^[abc]$/ //以abc其中一个开头及结尾 只能匹配a 或 b 或 c
console.log('abc'.match(regx3));//null
console.log('a'.match(regx3));//['a']
var regx3 = /^[abc][12]$/ //以abc中任意一个开头及以12中任意一个结尾 只能匹配 a1 a2 b1
b2 c1 c2
console.log('a12'.match(regx3));//null
var regx3 = /^abc[abc]$/ //以abc开头以abc其中一个结尾 abca abcb abcc
{}表示个数
  • {n}表示n个

  • {n,m}表示n个到m个

  • {n,}表示n个到无穷个 至少要有n个

//{} 表示个数
// {1,2} 1个到2个
var regx4 = /^[abc]{1,2}$/ //匹配abc中组成的1个或者俩个组合
//可以匹配 a b c aa ab ac bb bc ba ca cc cb
console.log('abc'.match(regx4));//null
console.log('a'.match(regx4));//['a']
console.log('ab'.match(regx4));//['ab']
//{1,} 一个及以上 至少要1个
var regx4 = /a{1,}/ //匹配一个a 或者是多个a
console.log('a'.match(regx4));//['a']
console.log('aaa'.match(regx4));//['aaa']
//{2} 表示俩个
var regx4 = /^b{2}$/ //表示俩个b
console.log('b'.match(regx4)); //null
console.log('bb'.match(regx4)); //['bb'
()分组
var regx5 = /^([a][ac]){2}$/ //能匹配 aaaa acac acaa
console.log('aaaa'.match(regx5))
console.log('acac'.match(regx5))
console.log('acaa'.match(regx5))
console.log('accc'.match(regx5))//null
+表示一个到多个
//+表示 {1,} 一个到多个
var regx6 = /^a+$/ //匹配一个a或者多个a
console.log('a'.match(regx6));//['a']
console.log('aaaa'.match(regx6));['aaaa']
* 表示0个到多个
//*表示{0,} 0个到多个
var regx7 = /^a*b$/ //匹配0个a或者多个a 以b结尾
console.log('b'.match(regx7));//['b']
console.log('ab'.match(regx7));//['a']
console.log('aaaab'.match(regx7));['aaaa']
? 表示0个到一个
//?表示0个到一个 {0,1}
var regx8 = /^a?b$/ //a可以有1个可以没有 以b结尾
console.log('b'.match(regx8));//['b']
console.log('ab'.match(regx8));//['ab']
console.log('aab'.match(regx8));//null
\d 表示数字 相当于[0-9] \D 表示非数字 ![0-9]
// 数字表示形式
var regx9 = /^[0-9]$/ //匹配0-9之间的数字
console.log('1'.match(regx9));//['1']
console.log('0'.match(regx9));//['0']
//第二种数字表示形式 使用\d 表示为数字 [0-9] \D 非数字
var regx9 = /^\d$/
console.log('1'.match(regx9));//['1']
console.log('0'.match(regx9));//['0']
var regx9 = /^\D$/ //非数字
console.log('1'.match(regx9));//null
console.log(' '.match(regx9));//[' ']
\w表示数字字母下划线 相当于[0-9A-Za-z] \W表示非数字字母下滑线 ![0-9A-Za-z]
//字母 数字 下滑线 \w 表示数字字母下滑线 \W 非数字字母下滑线
var regx10 = /^[0-9A-Za-z_]$/ //表示数字字母下滑线
console.log('1'.match(regx10));//['1']
console.log('A'.match(regx10));//['A']
console.log('a'.match(regx10));//['a']
console.log('_'.match(regx10));//['_']
console.log('?'.match(regx10));//null
var regx10 = /^\w$/
console.log('1'.match(regx10));//['1']
console.log('A'.match(regx10));//['A']
console.log('a'.match(regx10));//['a']
console.log('_'.match(regx10));//['_']
console.log('?'.match(regx10));//null
var regx10 = /^\W$/
console.log('?'.match(regx10));//['?']
console.log('a'.match(regx10));//null
.表示所有的
//.表示所有的
var regx11 = /^.$/
console.log('?'.match(regx11));//['?']
console.log(' '.match(regx11));//[' ']
console.log('.'.match(regx11));//['.']
\s 表示空白字符 \S表示非空白字符
//空白字符 \s空白字符 \S非空白字符串 (空格 回车 制表符..)
var regx12 = /^\s$/
console.log('.'.match(regx12));//null
console.log(' '.match(regx12));//[' ']
var regx12 = /^\S$/
console.log('.'.match(regx12));//['.']
console.log(' '.match(regx12));//null
转义 (将本身的意思显示出来去除对应元字符的功能)
  • 将需要转义的内容放到[]里面

  • 使用反斜杠来进行转义

//转义 需要匹配?或者.
//如果直接使用?和.他会解析为元字符 需要转义
//第一种直接把他加入中括号里面 (在中括号里面会解析为字符串)
var regx = /^[?.]$/
console.log('?'.match(regx));
console.log('.'.match(regx));
//第二种方式 使用反斜杠\ 来进行转义 转义符合
var regx = /^\?|\.$/
console.log('?'.match(regx));
console.log('.'.match(regx));

关于正则对象的属性及方法

属性

  • lastlndex

console.log(regx.dotAll);//是否在正则表达式中一起使用"s"修饰符
console.log(regx.flags);//模式修饰
console.log(regx.global);//是否全局匹配 g
console.log(regx.ignoreCase);//是否区分大小写 i
console.log(regx.multiline);//是否换行 m
console.log(regx.unicode);//是否进行编码匹配
console.log(regx.source);//表示里面内容
console.log(regx.sticky);//黏性

方法

  • test(太思特) 测试是否符合当前的正则表达式 (符合返回true 不符合返回false)

  • exec(诶个赛特) 执行方法 (类似于match 返回一个数组 匹配不成功返回null)

var regx = /^[abc]{2}$/ //匹配ab aa ac bc bb ba ca cb cc
console.log(regx.lastIndex);//返回lastIndex表示最后的下标 他是属于可读可写 默认值为0
//传入需要匹配的字符串 匹配成功返回true 失败返回false
console.log(regx.test('ab'));//true
console.log(regx.test('aaa'));//false
regx.lastIndex = 3 //自动默认更改 设置值为0
console.log(regx.test('ab'));//true
//执行方法 exec
console.log(regx.exec('ab')); //返回一个匹配的数组 类似match方法
console.log(regx.exec('abc')); //匹配不成功返回null

String 支持正则表达式的方法

  • search 搜索 (根据对应的正则返回第一次找的下标 如果没有找到返回-1)

  • match(骂起) 匹配 (根据对应的正则表达式返回匹配的数组,如果没有匹配的返回null)

  • split(斯不累特) 分割 (根据对应正则表达式进行字符串的分割 返回一个数组 如果不能进行分割返回的也是 一个数组 (数组里面只有一个元素))

  • replace(瑞 普类似) 替换 (根据对应的正则表达式进行字符串的替换返回一个新的字符串 如果不能完成替换 返回原本的字符串)

十七).es5和es6

JavaScript的构成

  • DOM 操作文档的

  • BOM 操作浏览器

  • ECMAScript (基础语法)

ECMAScript

ECMAScript简称为ES,(ECMA 欧洲计算机协会),ES分为不同的版本(常用的版本 ES3(常用的所有 浏览器都支持 都可以直接解析)、ES5 (大部分的高版本的浏览器都能支持)、ES6(部分浏览器支持 但存 在兼容问题 不能直接解析 必须先编译成ES3才能解析 bable.js(es的版本编译的)、ES7...(一般把ES6 以后都称为ES6)))

  • ES3 (常用版本)

  • ES5 (更加规范化和更加简易化 2015.10)

  • ES6 (模块化编程 基于ES5更加简易化 2016.4)

ES5新增内容

严格模式

概述:我们普通书写的js代码 并没有特别多的规范,甚至在某些时候可以随意发挥,这种模式被称为 异模式。相对而言一种更加规范化的模式被称为严格模式。

严格模式的声明

使用 use strict 进行声明,他的声明位置必须在第一行 (常用于源码书写)

严格模式的特性
  • 声明一个变量必须具备关键词

  • 函数内this不能指向window

  • 函数内arguments形参和实参不同步

  • 禁止八进制方法

  • 函数声明必须在对应的位置进行声明

//怪异模式
// var a = 10
// b = 20
// console.log(a);
// console.log(b);
//严格模式
"use strict"; //声明必须在第一行
//声明一个变量必须要有关键词
c = 30
console.log(c);//c没有被定义
//严格模式中函数内的this不能指向window
function fn(){
console.log(this);
}
fn()// this 是 undefined
数组新增的高阶函数
高阶函数概述:
以函数作为参数的函数叫做高阶函数
新增的相关函数
遍历的函数
forEach (没有返回值)
map (返回值是一个数组)
过滤和对应计算的
filter (返回一个数组)
//在严格模式中不能使用八进制相关的方法
//严格模式中argument的形参和实参不同步
//严格模式中函数只能在对应的声明函数地方进行声明

数组新增的高阶函数

高阶函数概述:

以函数作为参数的函数叫做高阶函数

新增的相关函数

遍历的函数

  • forEach(没有返回值)

//高阶函数
var arr = ['a','b','c']
//forEach
//传入的函数里面的三个参数分别是 第一个值 第二个下标 第三个遍历的数组
//forEach没有返回值 js函数里面参数如果不需要可以不传
arr.forEach(function(v,i,arr){
// console.log(v); //里面值
// console.log(i); //下标
console.log(arr); //遍历的数组
})
//省略对应参数
arr.forEach(function(v){
console.log(v);
})
  • map(返回值是一个数组)

//map
//map跟forEach使用是一样的 但map有返回值 返回一个数组
var mapArr = arr.map(function(v,i,arr){
console.log(v);
})
console.log(mapArr);//[undefined,undefined,undefined]
var mapArr = arr.map(function(v,i,arr){
console.log(v);
return v+i+'你好'
})
console.log(mapArr);

过滤和对应计算的

  • filter(返回一个数组)

var arr = ['abc','a','bc']
//filter 过滤 传入对应的函数 通过函数来返回条件进行过滤 返回的值为数组
//第一个为值 第二位下标 第三为当前遍历的数组
var filterArr = arr.filter(function(v,i,arr){
//里面包含a返回true 里面没有a返回false
//如果返回值是false 就不会加入到对应的数组 如果返回值为true就会加到对应的数组
return /a/.test(v)
})
console.log(filterArr);//包含a的一个数组 ['abc','a']
var arr = [10,9,8,23,15]
//返回所有大于10的
var filterArr = arr.filter(function(v){
return v>10
})
console.log(filterArr);//[23,15]
var arr = [10,9,8,23,15]
  • reduch(从前到后 返回一个值)

  • reduceRight(从后往前 返回一个值)

//reduce 计算的 计算所有的和
//prev表示前面的总和 第一次prev值为10 第二次为19 current默认从第二个开始
var sum = arr.reduce(function(prev,current,i,arr){
console.log(prev);
console.log(current);
console.log(i);//1
return prev+current
})
console.log(sum);
//调整到第1个开始 current的下标为0
arr.reduce(function(prev,current,i,arr){
console.log(i);//0
console.log(prev);//0
},0)
//求乘积
var v = arr.reduce(function(prev,current){
return prev * current
})
console.log(v);
//reduceRight 从右边开始 从后到前 prev值就是倒数第一个 i从倒数第二个开始
arr.reduceRight(function(prev,current,i,arr){
console.log(i);
})

判断是否存在

  • some(只要有一个就返回true)

// 查询的相关 条件在于传入的函数的返回的值
var arr = [0,1,2,3]
//根据里面返回的内容来进行判断的 里面只要有true返回 他就是true
var is = arr.some(function(v,i,arr){
return v>2
})
console.log(is);
  • every(所有都满足条件返回true 不然返回false)

//every 每一个都要满足条件
var is = arr.every(function(v,i,arr){
return v>=0
})
console.log(is);

注意事项

  • forEach和map的区别(forEach没有返回值 map有返回值)

  • reduce是用于计算的 reduce传入的函数有四个参数(前面的和(默认为第一个) 当前的值(默认 是第二个开始) 当前的下标(默认从1开始) 当前遍历的数组)如果需要调整在后面进行传参。

  • some和every的区别 (some只要有一个满足条件就返回true every要每个都满足条件才返回 true)

手写实现数组的高阶函数

  • forEac(遍历没有返回值)

var arr = [1,2,3]
//forEach 传入的fn里面要有三个参数分别是值 下标 遍历的数组
function myForEach(fn){
//使用for循环
for(var i=0;i<arr.length;i++){
//调用传入的函数
fn(arr[i],i,arr)
}
}
//调用
myForEach(function(v,i,arr){
console.log(v);//1,2,3
console.log(i);//0,1,2
console.log(arr);//[1,2,3]
})
  • map(返回一个数组)

var arr = [1,2,3]
//map 返回一个数组 数组里面值就是当前的函数执行后的返回结果
function myMap(fn){
//准备一个返回的数组
var returnArray = []
//使用for循环遍历
for(var i=0;i<arr.length;i++){
//将传入的函数的返回结果接收
var result = fn(arr[i],i,arr)
//添加到对应的返回数组中
returnArray.push(result)
}
//将数组返回
return returnArray
}
//调用
var mapArray = myMap(function(v,i,arr){
return v+'你好'
// console.log(v);
})
console.log(mapArray);//['1你好','2你好'],'3你好']]
  • filter(返回数组)

var arr = [1,2,3]
//filter 过滤 返回一个数组
function myFilter(fn){
//准备返回的数组
var returnArray = []
//遍历
for(var i=0;i<arr.length;i++){
//接收传入函数的返回值
var flag = fn(arr[i],i,arr)
//判断当前的函数是否返回值为true 为true加入到对应的数组
if(flag){
returnArray.push(arr[i])
}
}
return returnArray
}
//调用
var filterArray = myFilter(function(v){
return v>1
})
console.log(filterArray);//[2,3]
  • reduce(返回的是值)

var arr = [1,2,3]
//reduce实现
function myReduce(fn,index){
//默认情况下第一次的prevoiseValue值为第一个
var prevoiseValue = arr[0]
//默认的index从1开始
if(typeof index != 'number'){
index = 1
}else{//传入了对应的index就是0 prevoiseValue值也是0
index = 0
prevoiseValue = 0
}
//遍历
for(var i=index;i<arr.length;i++){
//调用方法 将对应前面计算的值给到对应的prevoiseValue
prevoiseValue = fn(prevoiseValue,arr[i],i,arr)
}
return prevoiseValue
}
//调用
var result = myReduce(function(prev,current){
return prev*current
})
console.log(result);//6
  • some (返回boolean类型 有true返回true 没有返回false)

var arr = [1,2,3]
//some 里面有一个return true 他就返回true 否则返回false
function mySome(fn){
//遍历
for(var i=0;i<arr.length;i++){
//接收对应的函数执行的返回值
var flag = fn(arr[i],i,arr)
//如果返回值有一个为true
if(flag){
//直接返回true
return true
}
}
//默认返回false
return false
}
//调用
var is = mySome(function(v){
return v>2
})
console.log(is);//true
  • every (返回boolean类型 如果有false就返回false 没有返回true)

var arr = [1,2,3]
//every
function myEvery(fn){
//遍历
for(var i=0;i<arr.length;i++){
//接收对应的函数执行的返回值
var flag = fn(arr[i],i,arr)
//如果返回值有一个为false
if(!flag){
//直接返回false
return false
}
}
//默认返回true
return true
}
//调用
var is = myEvery(function(v){
return v>0
})
console.log(is);//true

字符串新增

字符串模板

var username = '李丹'
var str = `hello${username}`
console.log(str) //hello李丹

改变this指向新增方法

this指向
  • this在函数内的this指向当前函数的调用的者(全局调用的函数this指向window)

  • this在对应的对象内的函数指向当前的对象的

  • this在事件对应的函数内指向当前事件的目标元素

如果需要更改this指向那么我们可以给对应的函数调用的方法

相关方法
  • bind (手动调用)

  • apply (自动调用 传递一个数组)(* 如果传递的是数组)

  • call (自动调用 一个个的数据传递)(* 一般常用)

function fn(arg,arg1){
console.log(this);
console.log(arg,arg1);
}
fn() //打印window
var obj = {name:'jack'}
//调用bind方法来更改this指向
//将对应的函数绑定给obj这个对象 bind不会执行函数
//函数调用的bind方法返回的是一个函数 这个函数不会自动调用 需要你手动调用
var v = fn.bind(obj)
//执行函数
v()
//bind方法运用 需要手动调用需要()来调用 传参传入到对应的()里面
fn.bind(obj)('你好','世界')
//apply 自动调用 第一个参数是绑定的对象 第二个参数是传递的参数(以数组进行传递)
fn.apply(obj,['hello','world'])
//call 自动调用 第一个参数是绑定的对象 第二个参数是传递的参数 (以一个个的元素进行传递)
fn.call(obj,'吃饭了吗','睡觉了吗')
apply方法和call方法的区别
  • apply方法传递的参数以数组形式

  • call方法传递的参数以元素形式

共同点
  • apply方法和call方法都会自动调用对应的函数

ES6新增内容

ES6对于字符串,数组,函数,对象,循环,变量修饰关键词、基础数据类型等都有新增

字符串新增方法

  • startsWith 是否开头

  • endsWith 是否结尾

  • includes 是否包含

  • repeat 平铺

var str = 'abc'
//是否开头 返回boolean类型
//是否以a开头
console.log(str.startsWith('a'));//true
// 是否结尾 返回boolean类型
//是否以a结尾
console.log(str.endsWith('a'));//false
//是否包含 返回boolean类型
//是否包含ac
console.log(str.includes('ac'));//false
//平铺 返回的是一个新的字符串
var newStr = str.repeat(2) //将对于的abc平铺俩遍
console.log(newStr);//abcabc

数组新增方法

普通方法
  • find 根据条件查找对应的值

  • findIndex 根据条件查找对应的下标

var arr = [1,2,3]
//普通方法(高阶函数) find findIndex
//根据条件查找 返回找到的第一个内容 没有找到返回undefined
//第一个是值 第二个是下标 第三个是数组
var value = arr.find(function(v,i,arr){
return v>1
})
console.log(value);//2
//根据条件返回找到第一次出现的下标 没有找到返回-1
var index = arr.findIndex(function(v,i,arr){
return v>1
})
console.log(index);//1

手写实现find及findIndex

//find查找值 根据传入函数的返回值作为条件来查找 没有找到返回undefined
var arr = [1,2,3]
function myFind(fn){
//遍历 调用函数
for(var i=0;i<arr.length;i++){
var flag = fn(arr[i],i,arr)
if(flag){
return arr[i]
}
}
}
//findIndex查找下标 根据传入函数的返回值作为条件来查找 没有找到返回-1
function myFindIndex(fn){
//遍历 调用函数
for(var i=0;i<arr.length;i++){
var flag = fn(arr[i],i,arr)
if(flag){
return i
}
}
return -1
}
var value = myFind(function(v){
return v>1
})
var index = myFindIndex(function(v){
return v<1
})
console.log(value,index);
静态方法
  • Array.from 将对应的伪数组转为数组

  • Array.of 将对应的传入的元素组成一个新的数组返回

//新增的俩个静态方法
//将伪数组转为数组
var btns = document.querySelectorAll('button') //NodeList 伪数组
// btns.map(function(v){
// console.log(v);
// }) 出错 伪数组没有map方法
//转成数组以后再调用
Array.from(btns).map(function(v){
console.log(v);
})
//将传进去的内容组成一个新的数组出来 返回对应的数组
var newArray = Array.of('a','b','c')
console.log(newArray);

变量修饰关键词

es3存在
  • var 声明伪全局变量

新增的
  • let 声明块状作用域变量(不允许重复声明 只在当前的代码块内起作用(块级作用域))

  • const 声明常量 (声明必须赋值 不允许修改 块级作用域)

// let 声明块级作用域
let a = 10
// let a = 20 不允许重复声明
a = 20
console.log(a);
// 块状作用域及伪全局作用域
for(var i=0;i<5;i++){
setTimeout(function(){
console.log(i);//重复打印五次5
},1000)
}
//块级作用域 只在当前的作用范围内容 进入
for(let i=0;i<5;i++){
setTimeout(function(){
console.log(i);//打印0 1 2 3 4
},1000)
}
// const 声明常量
const b = 10
// b = 20 //不允许二次赋值
console.log(b);
//声明必须要赋值
// const c 错的
//const也是块级作用域

基础数据类型

ES3的基础值类型
  • String

  • Number

  • Boolean

  • null

  • undefined

新增的值类型
  • symbol 独一无二的值

  • BigInt 大的整型

// 新增的类型
//独一无二的值(栈上面)
var sym = Symbol()
var sym1 = Symbol()
console.log(sym);
console.log(sym == sym1);//false
var sym2 = Symbol('你好') //传入的参数是说明
var sym3 = Symbol('你好')
console.log(sym2 == sym3);//false
//一般用于对象的key key不能重复的
var obj = { sym:10}
console.log(obj.sym);
//获取详情
console.log(sym2.description);
//BigInt 大的整型 容纳大的数
//Number中的整型Int容纳范围是有限的 10^16
var bi =
BigInt('111111111111111111111111111111111111111111111111111111111111111')
console.log(bi);

循环

  • for in(遍历对象)

  • for of (遍历数组的 只要实现了iterator迭代器就可以用for of遍历)

var obj = {
name: '张三',
age: 18
}
for (var key in obj) { //遍历的是key (数组的key是下标)key的类型为字符串
console.log(key)
}
var arr = ['a', 'b', 'c']
for (var value of arr) { //for of遍历的是值
console.log(value)
}

函数的新增

新增函数的默认参数
// 函数新增 默认参数
//默认a为0 b也为0 如果你传递了对应的参数 那么默认值就会被覆盖
function fn(a=0,b=0){
console.log(a+b);
}
fn(1,3)//打印4
fn()//打印0
fn(1)//打印1
新增箭头函数

箭头函数的写法

(参数1,参数2,...参数n)=>{代码}

示例

//新增箭头函数 是一个匿名函数
var fn1 = ()=>{
console.log('我是箭头函数');
}
fn1()

箭头函数的简化写法

  • 如果当前的参数只有一个 ()可以省略的

  • 如果当前的{}里面的代码只有一句的话 {}也可以省略

  • 如果当前的{}里面只有一句代码 且你需要返回数据的时候 可以省略return

//简化写法
// 如果当前的参数只有一个 ()可以省略的
// 如果当前的{}里面的代码只有一句的话 {}也可以省略
// 如果当前的{}里面只有一句代码 且你需要返回数据的时候 可以省略return
var fn2 = arg =>arg+'你好' //当前fn2 传递一个arg 返回一个arg+你好
var result = fn2('hello')
console.log(result);

箭头函数的特性

  • 没有this (根据作用域链向上找)

  • 没有arguments

//箭头函数的特性
var obj = {
say:function(){
console.log(this);
console.log(arguments);
}
}
obj.say() //打印obj
var obj = {
say:()=>{
console.log(this);
console.log(arguments);
}
}
obj.say() //打印window
//箭头函数里面没有this 所以他会去上面找 根据作用域链找离他最近的
//箭头函数里面没有arguments

对象新增

新增的对象方法(静态方法)
  • object.js判断两个对象是否是一个返回true和false

// Object.is 用于判断俩个对象是否是同一个对象
console.log({} == {});//false 比较地址值
console.log({} === {});//false 比较地址值比较类型
console.log(Object.is({},{}));//false 比较地址值同时还要比较类型
//特殊值 (可以解决NaN不等于NaN问题)
console.log(NaN == NaN); //false
console.log(NaN === NaN);//false
console.log(Object.is(NaN,NaN));//true
  • object.assign将后面对象的内容拷贝到前面的第一个对象

//Object.assign 将后面的内容拷贝到前面 返回一个对象 而这个对象的地址和第一个对象的地址完全一
样
var obj = {name:'jack'}
var targetObj = Object.assign(obj,{age:18},{sex:'男'},{height:189})
console.log(targetObj);//{name:jack,age:18,sex:男,height:189}
console.log(targetObj == obj);//true
//只拷贝第一层的值 剩下的拷贝的是引用地址 (浅拷贝实现方式之一)
var obj1 = {age:18,arr:[1]}
var targetObj = Object.assign({name:'tom'},obj1,{sex:'男'},{height:189})
console.log(targetObj);//{name:tom,age:18,arr:[1],sex:男,height:189}
//让第二层的内容发送变化
obj1.arr[0] = 10
obj.age = 20
//原本拷贝对象的值也会变化
console.log(targetObj);//{name:tom,age:18,arr:[10],sex:男,height:189}
新增对象简化写法
  • 对于对象中的函数可以省略对应的function

  • 对于对象中value变量名和key的名字一致的时候可以省略key

//简化写法
var name = 'jack'
var obj3 = {
// name:name,
//当你的key和value是一样的时候省略key value一定要是变量
name,
// say:function(){
// console.log('hello');
// }
//对象中函数的简化写法
say(){
console.log('hello');
}
}

解构赋值和扩展运算符

  • 解构赋值(解除构造进行赋值)

{}对于对象的解构 (使用key来匹配)

[]对于数组的解构 (使用下标来匹配)

var obj = {name:'jack',age:18,sex:'男'}
var arr = ['a1','b2','c3']
//采用解构赋值快速提取 对象
//{} 里面填写的是键 按照填写的key去匹配对象里面key取出他的值
var {name,sex,age} = obj
console.log(name,sex,age);
//[] 对于数组的解构 按照对应的顺序 下标匹配的
var [a,c,b] = arr
console.log(a);//a1
console.log(c);//b2
  • 扩展运算符(将内容一个个扩展出来)

//扩展运算符 ... 针对于数组的
console.log(...[1,2,3,4,5]);//1 2 3 4 5
//组成一个新的数组
console.log(Array.of(1,2,3,4,5));
console.log(Array.of(...[1,2,3,4,5]));//如果需要逗号隔开他会默认自己加上逗号

新增的集合类型(伪数组)

伪数组其实他也可以使用for of进行遍历(实现迭代 器)

set

概述:set是一个不允许重复的集合

set的构建

  • 不传参构建是一个空的set集合

  • 传入数组由数组的元素构建一个集合(数组去重)

//使用new关键词进行操作
//里面可以传入对应实现迭代器iterable的元素
//构建一个空的set集合
var set = new Set()
console.log(set);
//传入一个数组 提取数组中的元素构建一个set集合
var set = new Set([1,2,22,22,2,3,1]) //完成数组去重的功能
console.log(set);//1,2,22,3
set的方法及属性

属性

  • size

//set的属性 size
console.log(set.size);//size表示长度

方法

  • add添加方法

  • delete删除方法

  • clear清空

  • has判断是否存在

  • forEach遍历

  • keys获取所有的key返回迭代器

  • entries获取所有的k:v键值对 返回一个迭代器

//相关方法
//增加 add
set.add(22) //如果添加的是重复元素将不能添加进去
set.add('hello') //set中可以存储任意类型数据
console.log(set);
//删除 delete 传入对应的值进行删除
set.delete(22)
console.log(set);
//清空方法 clear
// set.clear()
console.log(set);
//改
// 将对应的内容删除再添加就是改
//查
//has 判断值是否存储 返回boolea类型 如果值存在返回true 不存在返回false
console.log(set.has(1));//false
//forEach 遍历的
set.forEach((v1,v2,s)=>{
//他的key就等于他的value
//v1表示key
console.log(v1);
//v2表示value
console.log(v2);
//s表示当前遍历的set
console.log(s);
})
//keys 获取所有的key values 获取所有的value entries获取所有的key:value键值对 返回的
iterable迭代器(数组)
console.log(set.keys());
console.log(set.values());
console.log(set.entries());//键值对

weakSet专门存储对象的set

//专门用于存储对象的set set并不能完成对象数组的去重
//无参构建
var ws = new WeakSet()
//有参构建 传入一个只读的对象数组
var ws = new WeakSet([{name:'张三'},{name:'张三'}])
console.log(ws);
//weakSet只有三个方法 has add delete
ws.add({age:18})
console.log(ws.has({age:18}));//false
ws.delete({age:18}) //删除不了 {age:18} != {age:18} 不是一个对象
console.log(ws);
map

概述:map他是一个key:value对 key不允许重复的 value可以重复(抽取所有的key他可以组成一个 set,所有的value抽取就是一个数组)

map的构建
  • 无参构建

  • 传入一个二维数组构建

//无参的方式
var map = new Map()
console.log(map);//构建一个空的map
//传递一个二维数组
//里面的元素数组 第一个是key 第二个是值
var map = new Map([['name','jack'],['age',18],['sex','男'],['sex','女']])
console.log(map);
map的属性及方法

属性

size

// 属性 size map里面的元素个数
console.log(map.size);

方法

  • set设置

  • get通过key来获取value

  • delete通过key来删除

  • clear判断当前的ley是否存在

  • keys返回所有的key

  • values返回所有的value

  • entires返回所有的k:v

  • forEach遍历

//相关方法
// 增 set
map.set(108,'yes')
console.log(map);
//修改就是对应的本身已经存在的key进行重新的设置
map.set('sex','girl')
console.log(map);
//删除 根据key来删除
map.delete(108)
console.log(map);
//清除所有的
// map.clear()
console.log(map);
//查询
//根据key判断是否存在
console.log(map.has('sex'));
//forEach 第一个值为值 第二个为键 第三个当前遍历map
map.forEach((v,k,map)=>{
console.log(k);
console.log(v);
})
//keys values entries
console.log(map.keys());
console.log(map.values());
console.log(map.entries());
//通过key来获取value
console.log(map.get('sex'));

WeakMap(基于weakSet构建的map)

//weakmap 基于 weakSet之上构建 也就是对应的key是对象 抽取所有的key其实就是weakSet
var wm = new WeakMap()
var wm = new WeakMap([[{name:'hello'},'你好']])
console.log(wm);
// wm.delete
// wm.get
// wm.set
// wm.has

class类

概述:类是对象的模板,对象是类的实例。(类相当建房子的设计图,对象相当于建好的房子) 相关知识点

  • class是用于修饰类的

  • 类名首字母必须大写

  • class里面的constructor 是一个函数 他会在每次new的时候调用

  • class中constructor的this关键词指向当前的实例对象

  • extends关键词是class之间的继承的

  • 如果使用了extends关键词,还想要在construct中使用this 那么必须先调用super方法

  • 对应的super关键词指向父类的constructor

  • 静态方法使用static关键词修饰,调用使用类名.方法名调用

// class是用于修饰类的 类是对象的模板(设计图)
//class修饰的类名必须首字母大写
// class 类名{
// 构造器
// }
//class里面的this 指向当前构建的对象实例
class Person{
//构造器 他是用于构建对象的方法
constructor(name){
this.name = name //this指向你对应构建的对象实例
this.sex = '男'
}
//静态方法 使用类名. 使用static关键词修饰的方法
static say(){
console.log('hello');
}
}
//构建对象使用new关键词
//new 操作符会做那些操作 开辟内存空间(建立一个对象) 存放对应的对象 自动执行对应的构造函数 将
对应的设置设置给到当前对象 然后将对象返回
var person = new Person('jack')
console.log(person);//{name:jack}
//静态方法调用
Person.say()
//继承 除了私有的其他全部可以继承
// extends关键词实现class的继承
class Child extends Person{ //child这个类继承Person类
constructor(name,age){
//如果使用extends关键词那么在对应的构造器里面不能直接使用this 要调用super函数
super(name) //相当于父类的构造函数 Person.constructor
this.age = age
}
}
var child = new Child('tom',88)
console.log(child);//{name:tom,sex:男,age:88}

generator函数

//异步阻断变同步的内容
function* fn(){
yield setTimeout(()=>{console.log('你好');},2000)
yield setTimeout(()=>{console.log('你好世界');},1000)
yield setTimeout(()=>{console.log('hello');},0)
}
var a = fn()
a.next()//打印你好
a.next()//打印你好世界
a.next()//打印hello

十八).运动(上)

概述:

运动(动画),操作对应的dom元素发生变化(这个变化要持续多次(修改样式)),每次间隔的时间是你肉眼察觉不到的(时间比较短)。当到达目标位置就停止。这个就是所谓的动画

主要实现原理

  • 利用定时器定时操作dom的样式

  • 当对应的设置目标值到达以后清除对应的定时器

运动三大要素

  • 当前值(current)

  • 变化值 (step)

  • 目标值 (target)

主要动画

匀速运动(每次变化的值是一样的)

示例(操作div的宽度变化)

// 操作div的宽度变化
//匀速运动每次变化的值是固定的
//获取div
var div = document.querySelector('div')
//当前值 通过获取当前的样式来获取对应的宽度
var current = parseFloat(getStyle(div).width)
//变化值
var step = 10
//目标值
var target = 500
//点击div 匀速变化他的宽度
div.onclick = function () {
var timer = setInterval(()=>{
//每次将当前值去加上变化量 完成变化
current += step
//设置给对应的div
div.style.width = current + 'px'
//到达目标位置清除定时器
if(current == target){
clearInterval(timer)
}
},40)
}
//封装一个方法获取对应的样式(获取所有的样式)
function getStyle(element){
if(window.getComputedStyle){
return window.getComputedStyle(element,'')
}else{
return element.currentStyle
}
}

封装的匀速运动代码(如果设置值不一样那么动画将不会一起完成)

//匀速运动的方法
//element表示移动的元素 target 目标位置{width:100,left:500}
function uniformVelocityAnimation(element, targetObj) {
//遍历target得到他的样式
for (let key in targetObj) {//key是字符串
//获取移动的目标样式的值
let current = parseFloat(getStyle(element)[key])
//得到目标值
let target = targetObj[key]
//步长设置 如果目标值是小于我们当前的值那么对应变化的量为负值 如果目标值大于我们当前值
那么变化量为正值
let step = target>current?10:-10
//定时更改
let timer = setInterval(() => {
//判断是否到达目标位置
if (current >= target) {
clearInterval(timer)
} else {
//每次将对应的left值更改
current += step
//重新设置div的left值
element.style[key] = current + 'px'
}
}, 40)
}
}
//封装一个方法获取对应的样式(获取所有的样式)
function getStyle(element){
if(window.getComputedStyle){
return window.getComputedStyle(element,'')
}else{
return element.currentStyle
}
}
缓冲运动(每次变化的值是越来越小的)

示例 (操作div的位置变化)

//div位置变化 以缓冲运动进行变化
//获取div
var div = document.querySelector('div')
//运动三要素
//当前值
let current = parseFloat(getStyle(div).left)
//目標值
let target = 500
//给div添加点击事件
div.onclick = function () {
var timer = setInterval(() => {
//如果到了就清除定时器
if (current >= target) {
clearInterval(timer)
} else {
//变化值 (目标值-当前值)/10 因为每次的值都会除10 那么对应的小数就会无限增加 就
会导致永远到不了(取整)
let step = Math.ceil((target - current) / 10)
//控制current的变化
current += step
//设置给对应的div
div.style.left = current + 'px'
}
}, 40)
}
//封装一个方法获取对应的样式(获取所有的样式)
function getStyle(element) {
if (window.getComputedStyle) {
return window.getComputedStyle(element, '')
} else {
return element.currentStyle
}
}

缓冲封装

//缓冲运动封装
//element表示当前的元素 target表示目标对象
function bufferAnimation(element, targetObj) {
//如果element为undefined就直接报错
if(!element){
throw new Error('元素不能缺少')
}
//给元素对象添加一个属性为timer他是一个定时器
element.timer = setInterval(() => {
var flag = true
//遍历对象
for (let key in targetObj) {
//取出当前值
let current = parseFloat(getStyle(element)[key])
//取出目标值
let target = targetObj[key]
//判断当前如果是位置的变化及对应的宽度高度的变化
if(key=='width' || key == 'height' || key == 'left' || key == 'top')
{
//步长 负值向下取整 正值向上取整
var step = target-current>0?Math.ceil((target - current) /
10):Math.floor((target - current) / 10)
current += step
element.style[key] = current + 'px'
}
//如果是透明度的变化
if(key == 'opacity'){
//步长 负值向下取整 正值向上取整
var step = target-current>0?Math.ceil((target - current) * 1000
/ 10):Math.floor((target - current)*1000 / 10)
current += step / 1000
element.style[key] = current
}
//如果是层高直接赋值
if(key == 'zIndex'){
element.style[key] = target
}
//如果没有完成就是false
if(current != target){
flag = false
}
}
//如果全部走完了就清除
if(flag){
clearInterval(element.timer)
}
},80)
}
//封装一个方法获取对应的样式(获取所有的样式)
function getStyle(element) {
if (window.getComputedStyle) {
return window.getComputedStyle(element, '')
} else {
return element.currentStyle
}
}

十九).运动(下)

链式动画

动画执行完接另一个动画不断进行链接。

封装进行(通过传入回调函数完成链式运动)

//缓冲运动封装
//element表示当前的元素 target表示目标对象 callbackFn表示传入的回调函数
function bufferAnimation(element, targetObj,callbackFn) {
    //如果element为undefined就直接报错
    if(!element){
        throw new Error('元素不能缺少')
    }
    //给元素对象添加一个属性为timer他是一个定时器
    element.timer = setInterval(() => {
        var flag = true
        //遍历对象
        for (let key in targetObj) {
            //取出当前值
            let current = parseFloat(getStyle(element)[key])
            //取出目标值
            let target = targetObj[key]
            //判断当前如果是位置的变化及对应的宽度高度的变化
            if(key=='width' || key == 'height' || key == 'left' || key == 'top'){
                //步长 负值向下取整 正值向上取整
                var step = target-current>0?Math.ceil((target - current) / 10):Math.floor((target - current) / 10)
                current += step
                element.style[key] = current + 'px'
            }
            //如果是透明度的变化
            if(key == 'opacity'){
                //步长 负值向下取整 正值向上取整
                var step = target-current>0?Math.ceil((target - current) * 1000 / 10):Math.floor((target - current)*1000 / 10)
                current += step / 1000
                element.style[key] = current
            }
            //如果是层高直接赋值
            if(key == 'zIndex'){
                element.style[key] = target
            }
            //如果没有完成就是false
            if(current != target){
                flag = false
            }
        }
        //如果全部走完了就清除
        if(flag){
            clearInterval(element.timer)
            //调用回调函数 如果传入的参数是函数
            if(typeof callbackFn == 'function'){
                callbackFn()
            }
        }
    },20)
}

示例(先变化div的宽度和高度 再变化他的位置)

<div></div>
<button></button>
<script src="./move.js"></script>
<script>
    //先让div宽度和高度变化 再改变他的位置
    //获取div
    var div = document.querySelector('div')
    document.querySelector('button').onclick = function () {
        //调用缓冲动画 (俩个异步操作 你不知道对应的异步什么时候走完)
        //可以进行二次封装 通过回调函数来解决异步的问题 (使异步变同步代码)
        bufferAnimation(div, {
            width: 200,
            height: 300
        }, () => {
            //变化位置
            bufferAnimation(div, {
                left: 500
            }, () => {
                //变化位置
                bufferAnimation(div, {
                    left: 100
                })
            })
        })
    }
</script>

轮播图


核心

  • 对应图的切换

  • 滚动切换 (更改的是对应大盒子的位置)

  • 透明度切换 (更改的时候对应图片的透明度)

  • 简单图片切换 (直接变化对应的图片)

滚动切换

  • 对应大盒子里面容纳多个图片

  • 图片的大小和显示盒子的大小一致

  • 对应的大盒子的大小超出了对应的显示盒子(溢出隐藏)

  • 根据对应的切换的下标来控制对应的大盒子的位置

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
        <style>
            *{
                margin: 0;
                padding: 0;
            }
            .showBox,li{
                width: 400px;
                height: 250px;
            }
            li>img{
                width: 100%;
            }
            .showBox{
                margin: 200px auto;
                /* 溢出隐藏 */
                /* overflow: hidden; */
            }
            ul{
                width: 700%;
                position: relative;
            }
            ul>li{
                list-style: none;
                float: left;
            }
        </style>
    </head>
    <body>
        <div class="showBox">
            <ul class="wrap">
                <li><img src="./images/slidepic1.jpg" alt=""></li>
                <li><img src="./images/slidepic2.jpg" alt=""></li>
                <li><img src="./images/slidepic3.jpg" alt=""></li>
                <li><img src="./images/slidepic4.jpg" alt=""></li>
                <li><img src="./images/slidepic5.jpg" alt=""></li>
                <li><img src="./images/slidepic6.jpg" alt=""></li>
                <li><img src="./images/slidepic7.jpg" alt=""></li>
            </ul>
        </div>
        <script src="./move.js"></script>
        <script>
            //获取显示盒子
            var showBox = document.querySelector('.showBox')
            //获取显示盒子的宽度
            var showWidth = parseFloat(getStyle(showBox).width)
            //获取ul(需要切换位置的容器)
            var wrap = document.querySelector('.wrap')
            //封装一个autoMove的函数
            function autoMove(){
                //切换图片的定时器
                var i = 0
                var timer = setInterval(()=>{
                    i++ //这个i就是控制对应的位置的下标
                    //到达最后 切换到第一个
                    if(i==wrap.children.length){
                        i = 0
                    }
                    //移动对应的ul 0 -400 -800
                    //距离等于 当前下标*对应显示盒子的宽度*-1
                    var distance = i * showWidth * -1
                    bufferAnimation(wrap,{left:distance})
                },2000)
                }
            //调用自动移动的方法
            autoMove()
        </script>
    </body>
</html>

透明度切换

  • 所有的图叠在一起

  • 将前面的图片的透明度设置0 当前图片透明度设置1

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        .showBox,li{
            width: 400px;
            height: 250px;
        }
        li>img{
            width: 100%;
        }
        .showBox{
            margin: 200px auto;
            /* 溢出隐藏 */
            /* overflow: hidden; */
        }
        ul>li{
            list-style: none;
            position: absolute;
            opacity: 0;
        }
        ul>li:nth-child(1){
            opacity: 1;
        }
    </style>
</head>
<body>
    <div class="showBox">
        <ul class="wrap">
            <li><img src="./images/slidepic1.jpg" alt=""></li>
            <li><img src="./images/slidepic2.jpg" alt=""></li>
            <li><img src="./images/slidepic3.jpg" alt=""></li>
            <li><img src="./images/slidepic4.jpg" alt=""></li>
            <li><img src="./images/slidepic5.jpg" alt=""></li>
            <li><img src="./images/slidepic6.jpg" alt=""></li>
            <li><img src="./images/slidepic7.jpg" alt=""></li>
        </ul>
    </div>
    <script src="./move.js"></script>
    <script>
        //获取ul(需要切换位置的容器)
        var wrap = document.querySelector('.wrap')
        //封装一个autoMove的函数
        function autoMove(){
            //切换图片的定时器 
            var i = 0
            var timer = setInterval(()=>{
                //改透明度 将之前的全部设置为0 
                bufferAnimation(wrap.children[i],{opacity:0},function(){
                    i++ //这个i就是控制对应的位置的下标
                    //到达最后 切换到第一个
                    if(i==wrap.children.length){
                        i = 0
                    }
                    // 再将自己的透明度改为1
                    bufferAnimation(wrap.children[i],{opacity:1})
                })
            },2000)
        }
        //调用自动移动的方法
        autoMove()
    </script>
</body>
</html>

图片切换

  • 根据下标的变化 切换对应的图片

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        .showBox{
            width: 400px;
            height: 250px;
            margin: 200px auto;
        }
        img{
            width: 100%;
        }
    </style>
</head>
<body>
    <div class="showBox">
        <img src="./images/slidepic1.jpg" alt="">
    </div>
    <script src="./move.js"></script>
    <script>
        //获取图片
        var img = document.querySelector('.showBox>img')
        //封装一个autoMove的函数
        function autoMove(){
            //切换图片的定时器 
            var i = 1
            var timer = setInterval(()=>{
                i++
                if(i==7){
                    i = 1
                }
                //设置图片的src
                img.src = `./images/slidepic${i}.jpg`
            },2000)
        }
        //调用自动移动的方法
        autoMove()
    </script>
</body>
</html>

无缝轮播(滚动轮播的进阶)

  • 拷贝第一张到最后

  • 判断是否到达最后一张 如果到达立马设置对应的位置为第一张的位置

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        .showBox,
        li {
            width: 400px;
            height: 250px;
        }

        li>img {
            width: 100%;
        }

        .showBox {
            margin: 200px auto;
            /* 溢出隐藏 */
            overflow: hidden;
        }

        ul {
            width: 800%;
            position: relative;
        }

        ul>li {
            list-style: none;
            float: left;
        }
    </style>
</head>

<body>
    <div class="showBox">
        <ul class="wrap">
            <li><img src="./images/slidepic1.jpg" alt=""></li>
            <li><img src="./images/slidepic2.jpg" alt=""></li>
            <li><img src="./images/slidepic3.jpg" alt=""></li>
            <li><img src="./images/slidepic4.jpg" alt=""></li>
            <li><img src="./images/slidepic5.jpg" alt=""></li>
            <li><img src="./images/slidepic6.jpg" alt=""></li>
            <li><img src="./images/slidepic7.jpg" alt=""></li>
        </ul>
    </div>
    <script src="./move.js"></script>
    <script src="./carousel.js"></script>
    <script>
        //获取显示盒子
        var showBox = document.querySelector('.showBox')
        //获取显示盒子的宽度
        var showWidth = parseFloat(getStyle(showBox).width)
        //获取ul(需要切换位置的容器)
        var wrap = document.querySelector('.wrap')
        //拷贝第一张图加到最后
        //克隆第一个li
        var firstLi = wrap.children[0].cloneNode(true)
        var i = 0
        //将克隆的元素加入到ul
        wrap.appendChild(firstLi)
        //封装一个autoMove的函数
        function autoMove() {
            //切换图片的定时器
            var timer = setInterval(() => {
                move(wrap, showWidth)
            }, 2000)
        }
        // //调用自动移动的方法
        autoMove()
        //抽取方法 x轴移动 移动的元素 变化i值 移动的宽度 对应的方向(默认为正方向)
        function move(element, showWidth, isAsc = true) {
            if (isAsc) {
                i++
            } else {
                i--
            }
            //移动对应的ul 0 -400 -800
            //距离等于 当前下标*对应显示盒子的宽度*-1
            var distance = i * showWidth * -1
            //执行动画
            bufferAnimation(element, {
                left: distance
            }, () => {
                //到达最后下标
                if (i == element.children.length - 1 && isAsc) {
                    //i的值重新变成0
                    i = 0
                }
                if (i == 0 && !isAsc) {
                    //立即设置位置 到第一张设置位置为最后一张
                    i = element.children.length - 1
                }
                //设置位置
                element.style.left = i * showWidth * -1 + 'px'
            })
        }
    </script>
</body>

</html>

升级版带焦点的轮播

html代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        .showBox,
        li {
            width: 400px;
            height: 250px;
        }

        li>img {
            width: 100%;
        }

        .showBox {
            position: relative;
            margin: 200px auto;
            /* 溢出隐藏 */
            overflow: hidden;
        }

        ul {
            width: 800%;
            position: relative;
        }

        ul>li {
            list-style: none;
            float: left;
        }

        .cirList {
            position: absolute;
            right: 20px;
            bottom: 10px;
            width: 150px;
        }

        .cirList>li {
            width: 10px;
            height: 10px;
            background-color: #fff;
            border-radius: 50%;
            margin: 0 5px;
        }

        .cirList .selected {
            background-color: red;
        }
        .arrow{
            display: none;
        }
        .arrow>a{
            display: block;
            width: 50px;
            height: 50px;
            position: absolute;
            top: 50%;
            margin-top: -25px;
        }
        .arrow>.prev{
            background: url(./images/prev.png) no-repeat center;
            background-size: contain;
            left: 0;
        }
        .arrow>.next{
            background: url(./images/next.png) no-repeat center;
            background-size: contain;
            right: 0;
        }
    </style>
</head>

<body>
    <div class="showBox">
        <ul class="wrap">
            <li><img src="./images/slidepic1.jpg" alt=""></li>
            <li><img src="./images/slidepic2.jpg" alt=""></li>
            <li><img src="./images/slidepic3.jpg" alt=""></li>
            <li><img src="./images/slidepic4.jpg" alt=""></li>
            <li><img src="./images/slidepic5.jpg" alt=""></li>
            <li><img src="./images/slidepic6.jpg" alt=""></li>
            <li><img src="./images/slidepic7.jpg" alt=""></li>
        </ul>
        <!-- 焦点 -->
        <ul class="cirList">
        </ul>
        <div class="arrow">
            <a href="" class="prev"></a>
            <a href="" class="next"></a>
        </div>
    </div>
    <script src="./move.js"></script>
    <script src="./carousel.js"></script>
</body>

</html>
js代码
主体代码
//获取显示盒子
var showBox = document.querySelector('.showBox')
//获取显示盒子的宽度
var showWidth = parseFloat(getStyle(showBox).width)
//获取ul(需要切换位置的容器)
var wrap = document.querySelector('.wrap')
//获取焦点的盒子
var cirList = document.querySelector('.cirList')
//获取箭头的盒子
var arrow = document.querySelector('.arrow')
//变化的下标
var i = 0
//焦点取值的下标
var index = 0
//定时器
var timer
//调用init的方法
init()
//调用自动移动的方法
autoMove()
//给showBox添加移入事件
showBox.onmouseenter = function () {
    //清除定时器
    clearInterval(timer)
    //显示箭头的盒子
    arrow.style.display = 'block'
}
//移出继续执行
showBox.onmouseleave = function () {
    //调用轮播
    autoMove()
    //隐藏
    arrow.style.display = 'none'
}
//给箭头添加事件
Array.from(arrow.children).forEach((v, i) => {
    v.onclick = (e) => {
        e = e || window.event
        //禁止a标签默认行为
        e.preventDefault();
        move(wrap, showWidth, i)
    }
})
//给cirList下面的li添加点击事件 事件委托机制
cirList.onclick = function (e) {
    e = e || window.event
    //判断当前操作的元素是否为li
    if (e.target.tagName == 'LI') {
        //获取当前点击的li的下标
        let clcikIndex = Array.from(cirList.children).findIndex((v) => {
            //如果当前操作的对象是遍历的对象那么返回出去
            return v == e.target
        })-1
        //改变对应位置及下标
        index = clcikIndex
        i = clcikIndex
        //调用move移动
        move(wrap, showWidth)
    }
}
//初始化生成焦点的方法
function init() {
    //拷贝之前生成对应的焦点
    Array.from(wrap.children).forEach((v, i) => {
        //在第一个焦点添加一个class名字为selected
        if (i == 0) {
            cirList.innerHTML += `<li class='selected'></li>`
        } else {
            //生成对应的焦点加入到cirList里面
            cirList.innerHTML += `<li></li>`
        }
    })
    //拷贝第一张图加到最后
    //克隆第一个li
    var firstLi = wrap.children[0].cloneNode(true)
    //将克隆的元素加入到ul
    wrap.appendChild(firstLi)
}
//封装一个autoMove的函数
function autoMove() {
    //切换图片的定时器
    timer = setInterval(() => {
        move(wrap, showWidth)
    }, 2000)
}
//封装一个焦点变化的函数 传入的index表示当前要设置的焦点
function changeCir(index) {
    //排他思维
    //先将所有的class清除
    Array.from(cirList.children).forEach((li) => {
        li.className = ''
    })
    //再给自己添加对应的样式
    cirList.children[index].className = 'selected'
}
//抽取方法 x轴移动 移动的元素 变化i值 移动的宽度 对应的方向(默认为正方向)
function move(element, showWidth, isAsc = true) {
    //先检索区间
    rangeCheck(element, isAsc)
    //再进行i值变化
    if (isAsc) {
        i++
        index++
    } else {
        i--
        index--
    }
    //焦点切换及区间检索
    rangeCheckCir(element)
    //执行动画
    //移动对应的ul 0 -400 -800
    //距离等于 当前下标*对应显示盒子的宽度*-1
    var distance = i * showWidth * -1
    bufferAnimation(element, {
        left: distance
    }, () => {

    })
}
//区间检索i下标
function rangeCheck(element, isAsc) {
    //到达最后下标
    if (i >= element.children.length - 1 && isAsc) {
        //i的值重新变成0
        i = 0
    }
    if (i <= 0 && !isAsc) {
        //立即设置位置 到第一张设置位置为最后一张
        i = element.children.length - 1
    }
    //设置位置
    element.style.left = i * showWidth * -1 + 'px'
}
//区间检索对应的焦点下标值
function rangeCheckCir(element) {
    //判断焦点下标
    if (index < 0) { //8张图 下标为 0-7 7个焦点 下标 0-6
        index = element.children.length - 2
    }
    if (index > element.children.length - 2) {
        index = 0
    }
    //更改焦点
    changeCir(index)
}

辅助代码

//缓冲运动封装
//element表示当前的元素 target表示目标对象 callbackFn表示传入的回调函数
function bufferAnimation(element, targetObj,callbackFn) {
    //如果element为undefined就直接报错
    if(!element){
        throw new Error('元素不能缺少')
    }
    //清除上一个定时器影响 (保证只有一个定时器)
    clearInterval(element.timer)
    //给元素对象添加一个属性为timer他是一个定时器
    element.timer = setInterval(() => {
        var flag = true
        //遍历对象
        for (let key in targetObj) {
            //取出当前值
            let current = parseFloat(getStyle(element)[key])
            //取出目标值
            let target = targetObj[key]
            //判断当前如果是位置的变化及对应的宽度高度的变化
            if(key=='width' || key == 'height' || key == 'left' || key == 'top'){
                //步长 负值向下取整 正值向上取整
                var step = target-current>0?Math.ceil((target - current) / 10):Math.floor((target - current) / 10)
                current += step
                element.style[key] = current + 'px'
            }
            //如果是透明度的变化
            if(key == 'opacity'){
                //步长 负值向下取整 正值向上取整
                var step = target-current>0?Math.ceil((target - current) * 1000 / 10):Math.floor((target - current)*1000 / 10)
                current += step / 1000
                element.style[key] = current
            }
            //如果是层高直接赋值
            if(key == 'zIndex'){
                element.style[key] = target
            }
            //如果没有完成就是false
            if(current != target){
                flag = false
            }
        }
        //如果全部走完了就清除
        if(flag){
            clearInterval(element.timer)
            //调用回调函数 如果传入的参数是函数
            if(typeof callbackFn == 'function'){
                callbackFn()
            }
        }
    },0)
}
//封装一个方法获取对应的样式(获取所有的样式)
function getStyle(element) {
    if (window.getComputedStyle) {
        return window.getComputedStyle(element, '')
    } else {
        return element.currentStyle
    }
}

重点

  • 逻辑的掌握

  • dom操作

  • 定时器操作

  • 错误的解决

  • 定时器里面套定时器 ( 如果外面的执行时间要比里面的短 会导致里面的定时器无法被清除 (定时器的作用范围内要清除对应的定时器) )

  • 知识点的运用

  • 封装 (函数封装 抽取共同点)

  • 同步和异步 (通过回调函数解决 定时器是异步的 事件也是异步的)

第三方动画的js

move.js

github地址

<div class="box"></div>
<div class="mybox"></div>
<script src='https://cdn.bootcdn.net/ajax/libs/move.js/0.5.0/move.js'></script>
<script>
      move('.box')
            .to(500, 200) //位置改变 x为500 y为200
            .rotate(180) //旋转180deg
            .scale(.5) //缩小0.5倍
            .set('background-color', '#888')//背景颜色
            .set('border-color', 'black')//边框颜色
            .duration('2s')//执行时间
            .skew(50, -10) //偏移
            .then() //成功
            // .set('opacity', 0) //透明度为0
            .duration('0.3s') //时间
            .scale(0.1) //缩小
            .pop() //加上他才能执行颜色设置相关的内容
            .end(); //动画结束
        //背景颜色变成绿色
        // 位置 变成 300 300
        // 旋转 180deg
        // 上面做完 放大2倍
        move('.mybox')
        .to(300,300)
        .set('background-color', '#f0f')
        .rotate(180)
        .duration('3s')
        .then().scale(2).duration('10s').pop().end()
</script>
animated.css (封装了css3的动画效果)
cdn
https://cdn.staticfile.org/animate.css/4.1.1/animate.min.css

官网地址

swiper.js (封装轮播图)

swiper官网

二十).面向对象

概述:

面向对象是一种编程思想(oop),面向对象相对于面向过程的一个抽取和简化。主要是以类

来构建对象,以对象来存储对应的行为属性,抽取对应的行为方法,抽取对应的属性作为属性。面向对象核心万物皆对象)

(所有的内容都可以抽取为一个对象)关键点:找有这个行为的对象去完成这个行为

面向对象和面向过程

面向过程以过程为核心

示例(去饭店吃饭)

1,先找饭店

2,找服务员点餐

3,等待上菜

4,吃饭

5,结账

面向对象以对象为核心

示例

  • 我找饭店(我这个对象 饭店这个对象)

  • 结账 吃饭 点餐属于我的行为

  • 饭店提供的相关内容就是饭店的行为

对象的构建

调用new关键词 去执行对应的构造函数来构建对象

通过类来构建对象(构造器)es6
class person{
    constructor(username)
    this.name=username
    }
}
var person = new Person('jack')
通过构造函数来构建(es3)
//以构造函数来构建 首字母大写
function     Dog(name){
    this.name = name//函数里面的this指向它的调用者 在new的时候指向对应的对象实例
}
var dog = new Dog(‘旺财’)
上述的两种构建方式其实核心上是一种 都是通过构造函数(构建对象的函数)来构建。

通过构造函数构建做了什么操作

  • 自动构建对象

  • 手动设置属性

  • 自动返回对象

通过工厂模式来构建对象

  • 手动创建对象

  • 手动设置属性

  • 手动返回对象

//以工厂模式来构建
//工厂里面要传入对应的属性 返回对应的对象(不能区分类型)
function factory(name){
    //Object是最大的对象 手动构建对象
    var  obj=new Object()
    //手动给obj设置相关的属性
    obj.name = name
     //手动返回对应的对象
    return obj
}
//调用
var obj = factory('调用')
var obj1 = factory(‘jack’)
console.log(obj,obj1)
工厂模式的特性
  • 可以构建所有的对象

  • 在构建对象的时候会忽略细节

  • 构建出来的对象都是Object(instanceof全部指向Object)

console.log(obj instanceof object);//true
console.log(obj1 instanceof object);//true

面向对象的三大特征

  • 封装(将对应的行为抽取为方法 属性抽取为属性)

  • 继承(子类继承父类的属性和方法)

  • 多态(继承关系的体现 重写(子类重写父类的方法)重载(在一个 类有两个同名的方法js不允许 没有重载))

封装示例

示例:有一个猫会喵喵叫 很胖800斤 吃的很多 名字叫咪咪

属性: 体重800 名字咪咪

方法:喵喵叫 吃的很多

继承及对应的重写

class Person{
    constructor(){
        // var sex = '男'
        this.name = 'jack'
        this.say = ()=>{
            console.log('哈哈哈哈');
        }
    }
}
class Son extends Person{
    constructor(age){
        super() //调用Person的constructor
        this.age = age
        //重写
        this.say = ()=>{
            console.log('嘻嘻嘻嘻');
        }
    }
}
var son = new Son()
console.log(son);//name say age 
son.say() //嘻嘻嘻嘻

面向对象tab栏切换

属性:上面的tab栏 下面的显示框

方法: 上面的点击事件 切换下面的显示栏的方法

//构建一个类
class Tab{
    constructor(nav,contents){
        this.nav = nav //上边的点击栏
        this.contents = contents //下面的切换的内容
        this.selectIndex = 0
        this.handlerClick()
    }
    //切换的方法
    toggle(selectElement){
        //选中内容变成对应的样式为selected 其他排他
        Array.from(this.nav).forEach((item)=>{
            item.className = ''
        })
        selectElement.className = 'selected';
        // this.nav[this.selectIndex].className = 'selected'
        //下面切换内容 样式为show
        Array.from(this.contents).forEach((item)=>{
            item.className = ''
        })
        //根据你传入的元素来查找下标
        let i = Array.from(this.nav).findIndex((v)=>{
            return v == selectElement
        })
        this.contents[i].className = 'show'
    }
    //点击事件处理
    handlerClick(){
        let _this = this
        Array.from(this.nav).forEach((item,i)=>{
           item.onclick = ()=>{
            // _this.selectIndex = i
             _this.toggle(item)
           }
        })
    }
}

面向对象拖拽实现

// 拖拽实现
// 属性  包含拖拽盒子的大盒子  拖拽的盒子  盒子的坐标位置
class Touch {
    constructor(outerBox, move) {
        this.outerBox = outerBox //包含的盒子
        this.move = move //移动的盒子
        this.point = { //坐标位置
            x: parseInt(this.getStyle(move).left) || 0,
            y: parseInt(this.getStyle(move).top) || 0
        } //基础坐标为0,0
        this.handlerDown()
    }
    //获取样式的方法
    getStyle(element) {
        return window.getComputedStyle ? window.getComputedStyle(element, '') : element.currentStyle
    }
    //按下
    handlerDown(){
        this.move.onmousedown = (e)=>{
            e = e || window.event
            //获取第一次按下的位置
            let currentX = e.offsetX
            let currentY = e.offsetY
            //调用移动的方法
            this.handlerMove(currentX,currentY)
            //调用弹起的方法
            this.handlerUp()
        }
    }
    //弹起
    handlerUp(){
        document.onmouseup = ()=>{
            this.outerBox.onmousemove = null
        }
    }
    //移动
   handlerMove(currentX,currentY){
         //给大盒子添加移动事件
         this.outerBox.onmousemove = (e) => {
            e = e || window.event
            //大盒子在页面上的位置
            let { x, y } = this.getPagePoint(this.outerBox)
            //获取移动的位置 - 大盒子在页面上的位置 - 当前按下位置
            let { targetX, targetY } = {
                targetX: e.pageX - x - currentX,
                targetY: e.pageY - y - currentY
            }
            let { maxX, maxY } = {
                maxX: this.outerBox.offsetWidth - this.move.offsetWidth,
                maxY: this.outerBox.offsetHeight - this.move.offsetHeight
            }
            //区间判断
            if (targetX < 0) {
                targetX = 0
            }
            if (targetX > maxX) {
                targetX = maxX
            }
            if (targetY < 0) {
                targetY = 0
            }
            if (targetY > maxY) {
                targetY = maxY
            }
            //将对应的位置设置进去
            this.point = { x: targetX, y: targetY }
            this.setMovePoint()
        }
   }
    setMovePoint() {
        //设置
        this.move.style.left = this.point.x + 'px'
        this.move.style.top = this.point.y + 'px'
    }
    getPagePoint(element) {
        let x = 0
        let y = 0
        while (element.offsetParent) {
            x += element.offsetLeft
            y += element.offsetTop
            element = element.offsetParent
        }
        return { x, y }
    }
}

基于面向的拖拽实现放大镜

//放大镜功能和区间拖拽的实现有类似
class Magnifier extends Touch {
    constructor(smallBox, moveBox, bigBox, bigImage) {
        //传给父类
        super(smallBox, moveBox) //outerBox move
        this.bigBox = bigBox
        this.bigImage = bigImage
        this.handlerEnter()
        this.handlerLeave()
    }
    handlerEnter() {
        this.outerBox.onmouseenter = () => {
            this.move.style.display = 'block'
            this.bigBox.style.display = 'block'
            this.init()
            //调用移动的方法
            this.handlerMove(this.move.offsetWidth / 2, this.move.offsetHeight / 2)
        }
    }
    handlerLeave() {
        this.outerBox.onmouseleave = () => {
            this.move.style.display = 'none'
            this.bigBox.style.display = 'none'
        }
    }
    init() {
        //将移动的move的大小初始化
        this.move.style.width = this.outerBox.offsetWidth / (this.bigImage.offsetWidth / this.bigBox.offsetWidth) + 'px'
        this.move.style.height = this.outerBox.offsetHeight / (this.bigImage.offsetHeight / this.bigBox.offsetHeight) + 'px'
    }
    setMovePoint() {
        //根据对应的坐标来设置对应的位置
        //设置
        this.move.style.left = this.point.x + 'px'
        this.move.style.top = this.point.y + 'px'
        //设置大图片的位置
        // 外面的盒子/移动的盒子 = 大的图片/大的盒子
        // 350 / ? = 800 / 540 
        // 350 = (800 / 540 ) * ? ==> ? = 350 / (800 / 540)
        // 150 / 350  ==  大的图片移动多少 ?/800
        //   150 / 350 = ?/800 ===> ? = 150/350*800
        let x = this.point.x / this.outerBox.offsetWidth * this.bigImage.offsetWidth * -1
        let y = this.point.y / this.outerBox.offsetHeight * this.bigImage.offsetHeight * -1
        //设置大图片的定位
        this.bigImage.style.left = x + 'px'
        this.bigImage.style.top = y + 'px'
    }
}

二十一).原型和继承

面向对象回顾

通过new构造函数的方式

class的constructor (es6)

class Person{
    constructor(name){ //构造器
        this.name = name
    }
}
var person = new Person('tom')

function的方式 (es3)

function Person(name){
    this.name = name
}
var person  = new Person('jack')
步骤
  • 自动构建对象

  • 手动设置属性

  • 自动返回对象

通过工厂模式
function factory(name){
    var obj = new Object()
    obj.name = name
    return obj
}
步骤
  • 手动构建对象

  • 手动设置属性

  • 手动返回对象

构造函数的缺陷

function Person(name){
    this.name = name
    this.age = 18
    this.sayHello = ()=>{
        console.log('hello');
    }
}
var person = new Person('jack')
var person1 = new Person('tom')
console.log(person == person1);//false 对应的存储地址不一样
console.log(person.age == person1.age);//true
console.log(person.sayHello == person1.sayHello);//false 函数也是引用数据比较的是地址、
  • 因为上面的俩个sayHello方法不是一个方法 证明每次去new一个对象的时候对应的sayHello就会重新在对应的堆上开辟一个内存

  • 如果对应的new的操作很多那么对应的sayHello占用的内存就很大 (效率就低了)

  • 按照我们的思考 sayHello方法做的事情是一样 那么我们其实只需要一个sayHello就满足了

  • 构造函数的缺陷 就是里面声明的函数 会在每次new的时候重新开辟内存

  • 解决方式 找一个共享的空间(对象)(只声明一次) 将这个函数放进去)

  • 那么现在这个共享空间应该属于谁 构造函数

  • 这个属于构造函数的空间就被称为原型

原型


prototype

概述:prototype是属于函数的一个空间,它是一个对象。因为构造函数也是函数所以它也具备。而这个prototype属性我们称为显式原型。

函数的prototype
function fn(){

}
console.log(fn.prototype);
构造函数的prototype
function Person(){

}
console.log(Person.prototype);
//获取当前的构造函数
console.log(Person.prototype.constructor);
//将函数存储在原型上
Person.prototype.sayHello = ()=>{
    console.log(this);
}
//新建对象
var person = new Person()
var person1 = new Person()
console.log(person == person1);//false
//person.sayHello ===>   Person.prototype.sayHello
//对于prototype上存储的内容 通过实例对象.属性名访问
console.log(person.sayHello == person1.sayHello);//true
///对于prototype上存储的内容 通过实例对象.属性名访问
console.log(person.constructor);
  • 从上可得构造函数的prototype是一个对象,第二个里面有个属性 constructor指向当前的构造函数。

  • 实例对象访问对于的prototype上的内容可以通过实例对象.属性名访问

  • 一般将对应的函数存储在对应prototype上(这个函数只会声明一次)。将函数存储在原型,将属性放在构造函数里面。

  • 在prototype里面声明的函数的this指向当前的调用的实例对象

__proto__

概述:

__proto__称为隐式原型,它是属于对象的一个空间,每个对象都存在这个空间,那么对应的实例对象也是一个对象,所以它也有这个空间。这个空间指向对应的构造函数的prototype。

var obj = new Object()
//每个对象都存在的一个空间 它指向对应的构造函数的prototype
console.log(obj.__proto__);
//对象的__proto__指向对应的构造函数的prototype
console.log(obj.__proto__ == Object.prototype);
function Person(){

}
var person = new Person()
console.log(person.__proto__ == Person.prototype);
// Person.prototype.sayHello = ()=>{

// }
person.__proto__.sayHello = ()=>{
    console.log('hello');
}
person.sayHello()
__proto__的指向问题

__proto__指向对应的构造函数的prototype

构造函数也是一个对象 那么它的__proto__指向谁 指向对应的父类的构造函数的prototype

Object的__proto__指向null

//指向Person.prototype
console.log(person.__proto__);
//指向构造函数的原型的原型 构造函数的原型是啥 是一个对象  Object.prototype
console.log(person.__proto__.__proto__);
//指向构造函数的原型的原型 构造函数的原型是啥 是一个对象  Object.prototype 的__proto__是null
console.log(person.__proto__.__proto__.__proto__);

原型链

概述:

对象在__proto__上找属性的链式结构被称为原型链。

从上面的指向问题来看 对象在原型上找属性的过程为
  • 先找自己的__proto__ (对应的构造函数的prototype),

  • 再找对应的自身构造函数的原型的__proto__ 找到父类构造函数的原型 ,再找对应的父类的原型的__proto__,直到找到object为止

  • Object的原型的__proto__(null)上还找不到返回undefined

Object.prototype.hello = '你好'
class Person{
    constructor(){
        this.name = '张三'
    }
}
Person.prototype.sex = '女'
Person.prototype.username = 'rose'
//对象赋值 有就重新赋值 没有就添加属性赋值
Person.hi = '你好吗'
var person = new Person()
person.age = '109'
class Son extends Person{
    constructor(){
        super()
        this.age = '李四'
    }
}
Son.prototype.sex = '男'
//实例化对象
var son = new Son()
console.log(son.age);//李四
console.log(son.name);//张三
console.log(son.username);//rose
console.log(son.sex);//男
console.log(son.hello);//你好 
console.log(son.hi);//undefined
注意事项
  • 原型链不包含对象赋值

  • 对象赋值的操作是找到这个属性了重新设置值

  • 没有找到这个属性进行 添加这个属性进行赋值操作

总结
  • 构造函数的原型prototype

  • 实例对象的原型__proto__

  • 实例对象的__proto__指向构造函数的prototype

  • 原型链通过对应的对象的__proto__去找对应的属性 直到找到Object为止

  • 原型一般上面写函数,可以保证函数只声明一次。对应的属性写在构造函数内。

  • 原型上的方法/属性。通过实例对象.属性名直接访问(平常通过对象去点的方法都称为原型方法)

  • 在对应的原型上的函数里面的this指向当前调用的实例对象

通过原型来实现数组的高阶函数

forEach实现 map实现
//数组的原型
//在数组的原型上添加一个myForEach的方法
//在对应的原型上的函数里面的this指向当前调用的实例对象
Array.prototype.myForEach = function(fn){
    //遍历
    for(let i=0;i<this.length;i++){
        fn(this[i],i,this)
    }
}
Array.prototype.myMap = function(fn){
    let returnArr = []
    //遍历
    for(let i=0;i<this.length;i++){
        returnArr.push(fn(this[i],i,this)) 
    }
    return returnArr
}
reduce实现
//回调函数  defaultValue初始值
Array.prototype.myReduce = function(fn,defaultValue){
    //默认请求 前面的值为第一个开始下标为第二个
    let previousValue = this[0]
    let index = 1
    //如果传了初始值 那么对应的初始值为传入的值 开始的下标从0开始
    if(typeof defaultValue != 'undefined'){
        previousValue = defaultValue
        index = 0
    }
    //遍历
    for(let i=index;i<this.length;i++){
        previousValue = fn(previousValue,this[i],i,this)
    }
    return previousValue
}
//回调函数  defaultValue初始值
Array.prototype.myReduce = function(fn,defaultValue){
    //默认请求 前面的值为第一个开始下标为第二个
    let previousValue = this[0]
    let index = 1
    //如果传了初始值 那么对应的初始值为传入的值 开始的下标从0开始
    if(typeof defaultValue != 'undefined'){
        previousValue = defaultValue
        index = 0
    }
    //遍历
    for(let i=index;i<this.length;i++){
        previousValue = fn(previousValue,this[i],i,this)
    }
    return previousValue
}

面向对象的三大特性

封装 (函数的抽取 属性的抽取)

继承 (子类继承父类)

多态 (重写 子类重写父类方法)

继承

概述:

子类继承父类的属性和方法(非私有的属性和方法 非静态的方法)

继承的实现
使用extends关键词实现继承(es6新增 类的继承)
// es6新增类的继承 extends关键词实现
class Person{
    constructor(){
        this.name = 'jack'
    }
}
class Son extends Person{
    constructor(age){
        super()
        this.age = age
    }
}
var son = new Son(18)
console.log(`son`, son);
原型链继承 (覆盖之前原型上的所有方法 显示的时候不会显示继承来的属性 (在原型上重复出现一样的属性))
  • 核心 在子类的原型上创建父类的对象

function Person(){
    this.name = 'jack'
}
function Son(age){
    this.age = age
}
Son.prototype.say = ()=>{
    console.log(`你好`);
}
//原型继承
// 将要继承的类放在继承的子类的原型上
//原型链会覆盖原本原型上的私有的方法及属性
Son.prototype = new Person()
var son = new Son(18)
console.log(`son`, son);
console.log(son.name);
// son.say() 
对象冒充 (会显示继承的属性 不能继承原型上的方法)
  • 核心 在子类中调用父类的构造函数 更改this指向

function Person(){
    this.name = 'jack'
}
function Son(age){
    //改this指向 执行父类的构造函数
    Person.call(this)
    this.age = age
}
var son = new Son(18)
console.log(`son`, son);

组合继承 (使用原型链继承和对象冒充结合)

// 组合继承 原型链继承加对象冒充
function Person(){
    this.name = 'jack'
}
Person.prototype.say = ()=>{
    console.log(`hello`);
}
function Son(age){
    //改this指向 执行父类的构造函数
    Person.call(this)
    this.age = age
}
//原型链继承
Son.prototype = new Person()
var son = new Son(18)
console.log(`son`, son);
son.say()
组合寄生继承 (对象冒充 + 原型链继承(创建一个原型对象放在原型链上))
  • 对象冒充

  • 在子类的原型上创建父类的原型对象

//组合寄生继承
function Person(){
    this.name = 'jack'
}
Person.prototype.say = ()=>{
    console.log(`hello`);
}
function Son(age){
    //改this指向 执行父类的构造函数
    Person.call(this)
    this.age = age
}
//寄生
Son.prototype = Object.create(Person.prototype)
var son  = new Son(18)
console.log(`son`, son);

轮播图面向对象

<!DOCTYPE html>
<html lang="en">

    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
        <style>
            * {
                margin: 0;
                padding: 0;
            }

            .showBox,
            li {
                width: 400px;
                height: 250px;
            }

            li>img {
                width: 100%;
            }

            .showBox {
                position: relative;
                margin: 200px auto;
                /* 溢出隐藏 */
                overflow: hidden;
            }

            ul {
                width: 800%;
                position: relative;
            }

            ul>li {
                list-style: none;
                float: left;
            }

            .cirList {
                position: absolute;
                right: 20px;
                bottom: 10px;
                width: 150px;
            }

            .cirList>li {
                width: 10px;
                height: 10px;
                background-color: #fff;
                border-radius: 50%;
                margin: 0 5px;
            }

            .cirList .selected {
                background-color: red;
            }

            .arrow {
                display: none;
            }

            .arrow>a {
                display: block;
                width: 50px;
                height: 50px;
                position: absolute;
                top: 50%;
                margin-top: -25px;
            }

            .arrow>.prev {
                background: url(./images/prev.png) no-repeat center;
                background-size: contain;
                left: 0;
            }

            .arrow>.next {
                background: url(./images/next.png) no-repeat center;
                background-size: contain;
                right: 0;
            }
        </style>
    </head>

    <body>
        <div class="showBox">
            <ul class="nav">
                <li><img src="./images/slidepic1.jpg" alt=""></li>
                <li><img src="./images/slidepic2.jpg" alt=""></li>
                <li><img src="./images/slidepic3.jpg" alt=""></li>
                <li><img src="./images/slidepic4.jpg" alt=""></li>
                <li><img src="./images/slidepic5.jpg" alt=""></li>
                <li><img src="./images/slidepic6.jpg" alt=""></li>
                <li><img src="./images/slidepic7.jpg" alt=""></li>
            </ul>
            <!-- 焦点 -->
            <ul class="cirList">
            </ul>
            <div class="arrow">
                <a href="" class="prev"></a>
                <a href="" class="next"></a>
            </div>
        </div>
    </body>

</html>
<script src='https://cdn.bootcdn.net/ajax/libs/move.js/0.5.0/move.js'></script>
<script>
    //需要存储所有的图片的盒子 nav
    // 焦点  左右切换的盒子
    class Carousel {
        constructor(box) {
            this.box = box
            //根据大盒子获取存储图片的盒子
            this.nav = box.querySelector('.nav')
            //根据大盒子获取存储焦点的盒子
            this.cirList = box.querySelector('.cirList')
            //控制的下标
            this.index = 0
            this.init()
            this.handlerClick()
        }
        init() {
            //根据对应的原本的图片个数
            //将this.nav.children 当作数组调用forEach方法
            Array.prototype.forEach.call(this.nav.children, (v, i) => {
                let htmlCode = '<li></li>'
                if(i == 0){
                    htmlCode = "<li class='selected'></li>"
                }
                //给对应的焦点进行添加
                this.cirList.innerHTML += htmlCode
            })
            //生成焦点和在对应的nav后添加第一张图
            var cloneNode = this.nav.children[0].cloneNode(true)
            //加给最后
            this.nav.appendChild(cloneNode)
        }
        move(direction = true) {
            //区间判断
            if (this.index < 1 && !direction) {
                this.index = this.nav.children.length - 1
                //切换位置
                this.nav.style.transform = `translate3d(${this.index * this.box.offsetWidth * -1}px, 0px, 0px)`
            }
            if (this.index > this.nav.children.length - 2 && direction) {
                this.index = 0
                this.nav.style.transform = 'translate3d(-0px, 0px, 0px)'
            }
            //反方向为false
            if (direction) {
                this.index++
            } else {
                this.index--
            }
            //移动
            var x = this.index * this.box.offsetWidth * -1
            this.focusElement(this.index)
            move('.nav').to(x, 0).duration('1s').end()
        }
        autoMove() {
            let _self = this
            this.timer = setInterval(() => {
                _self.move(true)
            }, 2000)
        }
        focusElement(index){
            //取值为0-6
            if(index==7){
                index = 0
            }
            //排他思想
            Array.prototype.forEach.call(this.cirList.children,(v,i)=>{
                v.className = (i == index ? 'selected' : '')
            })
        }
        handlerClick(){
            var _self = this
            Array.prototype.forEach.call(this.cirList.children,(v,i)=>{
                let index = i
                v.onclick = function(){
                    _self.index = index-1
                    _self.move()
                }
            })
        }
    }
    var box = document.querySelector('.showBox')
    var carousel = new Carousel(box)
    carousel.autoMove()
</script>

ES6的模块化

模块的思想,将对应的功能代码封装为一个模块(js代码 css代码 html代码)。

想要使用别人就导入,想要给别人用就导出。复用。

模块化的常用的模式

amd (在对应的加载之前导入)

cmd (在用的时候导入)

common.js (基于amd和cmd之上)

es6的模块化的关键词(要想导入想要导出)

import 导入

export 导出

export的使用

第一种 export default (只能声明一次)

// //默认导出只有一个 如果使用export default 导出的可以使用对应的一个名字来接
export default {
   obj,
   str,
   say
}

接收

import obj from './test.js'
console.log(obj) //{obj,str,say}

第二种导出

//如果直接使用的对应的export 导出那么必须通过{键}来接
export const obj = {
    name:'jack',
    age:18
}
//如果导出的是值类型 一定要写变量名
export const str = "你好世界"
export const say = ()=>{
    console.log('hello world');
}

接收

import {obj,str,say} from './test.js'

第三种导出

const name = 'tom'
//第三种导出
export {
    //只能写变量
    name
}

接收

import {name} from './test.js'
import使用
import 名字 from '地址'

二十二).闭包和promise

闭包

概述:

闭包就是在函数内部返回一个函数,内部函数有外部函数的引起。这个结构就称为闭包。

函数的生命周期

function fn(){
    var i=0
    i++
    return i
}
console.log(fn())//1 第1个i变量
console.log(fn())//1 第二个i变量
console.log(fn())//1 第三个i变量
函数在预编译阶段
  • 开辟一个内存空间

  • 将对应的代码块放到这个内存空间

函数的执行阶段
  • 将对应的函数开辟的这个空间放在执行栈上

  • 执行栈就开始对应的函数对应的空间的代码块

  • 这个代码如果需要开辟空间 它就在这个函数的内存空间上开辟

  • 当你不需要使用这个函数了 对应的函数的内存空间就会被回收 那么里面的代码开辟的空间也就被回收了

如果我们需要保持需要i的状态 那么我们可以将这个i放到这个引用数据类型里面然后保证这个引用数据类型对象的引用,这个时候gc就不会回收对应的这个i了
function fn(){
    var i = 0
    i++
    retrun {
        i
    }
}
var obj = fn()
console.log(obj.i) //1
  • 通过上述代码 我们可以保持对应i的引用,保证i不会被回收,以返回一个引用数据类型来保持对应i的引用。 那么对应的函数也是一个引用数据类型,那么我们是不是也可以通过返回函数的形式来做到保证i的唯一性。

function fn(){
  var i = 0
  return function(){
      i++
      console.log(i)
  }
}
var f = fn()
f() //1
f() //2 
f() //3

而上面这种保证i不被回收的机制就叫做闭包。(返回一个引用数据类型 这个里面保证对应的对应i的引用 从而不被回收)

闭包的优劣
优势
  • 内部函数拥有外部函数参数和变量的引用,使用我们的参数和变量的作用范围被扩大。

  • 对应的参数不被回收,在使用的时候就不需要重新开辟空间,速度更快。

  • 作为缓存

劣势
  • 内部函数要一直保持对外部函数里面参数和变量的引用。

  • 因为不会被回收那么对应的内存空间就会一直占用。

闭包的应用
防抖(在规定时间内只执行一次 执行最后一次)
//第一个参数是做的操作 第二个参数是等待时间
function debounce(fn,delay){
    var timer = null
    return function(){
        clearTimeout(timer) //清除上一次的等待
        //开始新的等待
        timer = setTimeout(fn,delay)
    }
}
节流 (在规定时间内执行第一次 减少执行次数)
示例(红绿灯)
//操作  执行一次的时长
function throttle(fn,delay){
    var timer = null
    return function(){
        //判断上一次是否走完
        if(timer) return
        //上一次走完了开始下一次
        timer = setTimeout(()=>{
            fn()
            //走完了要将节流阀设置为false
            timer = null    
        },delay)
    }
}
防抖和节流的区别
  • 防抖执行最后一次 节流执行第一次

  • 防抖在规定时间内只执行一次 节流是在规定时间内减少对应的执行次数

  • 防抖对应的开始下一次先要清除上一次 节流开始下一次先要判断上一次是否执行完毕

函数柯里化 (将多个参数的函数拆分为多个单参数的函数 可以自由的组合)
示例
function sum(a,b){
 return a+b
}
sum(1,2)

简单的函数柯里化

function sum(a){
    return function(b){
        return function(c){
            return a+b+c
        }
    }
}
console.log(`sum(1)(2)(3)`, sum(1)(2)(3));//6
console.log(`sum(1)(2)`, sum(1)(2));//fn

核心就是参数没有够返回对应的函数 参数够了返回结果

高阶函数柯里化
//传递一个函数 (参数没到返回函数 参数到了返回结果)
function currying(fn) {
    //获取currying传递的参数
    let args = Array.prototype.slice.call(arguments,1)
    return function () {
        //将对应的函数的参数和curry传递参数做连接
        let arg = Array.from(arguments).concat(args)
        //判断参数个数是否一样
        if(arg.length < fn.length){
            //参数没到返回函数
            return currying.call(this,fn,...arg)
        }else{
            //参数到了 调用方法返回结果
            return fn.apply(this,arg)
        }
    }
}

调用

 function sum(a,b,c){
    return a+b+c
}
console.log(`sum(1,2,3)`, sum(1,2,3));
let fn = currying(sum)
console.log(`fn(2)`, fn(2));//函数
console.log(`fn(2)(3)`, fn(2)(3));//函数
console.log(`fn(2)(3)(1)`, fn(2)(3)(1));//6
console.log(` fn()()()(2)()()(3)()()()(1)`, fn()()()(2)()()(3)()()()(10));//15

Promise


概述:promise是es6新增的一个类,这个类翻译为承诺,它有三种状态 等待状态,成功状态,拒绝状态。它被设计为异步的,它里面的内容是异步的(方法为异步的)

promise的三种状态
  • 等待状态(没有处理) pending

  • 成功状态 (有对应的处理) fulfilled (里面resolve方法调用)

  • 失败状态 (有对应的处理)rejected (里面代码报错 或者 调用reject)

构建promise对象
new Promise((成功的函数,失败的函数)=>{
    代码块
})
//里面传递的参数是一个函数
//这个传递的函数里面有俩个参数 这个俩个参数也是一个函数
//这个函数里面的第一个参数为成功的函数 resolve  第二个参数为失败的函数 reject (这个俩个函数都是异步的)
var promise = new Promise((resolve,reject)=>{
    //包含异步的代码
    console.log('hello promise')
})
promise的方法
原型方法
  • then 执行成功的回调
var promise = new Promise((resolve,reject)=>{
    //成功的函数调用 传递对应的参数
    resolve('成功')
})
promise.then((res)=>{
    console.log(`第一次then`, res);
    return 'hello'
}).then((res)=>{
    console.log(`第二次then`, res);
    return 'world'
}).then((res)=>{
    console.log(`第三次then`, res);
    return 'abc'
}).then() //值穿透 当你的then没有处理它会给到下一个处理
    .then((res)=>{
    console.log(`第n次then`, res);
})
  • catch 执行失败
var promise = new Promise((resolve,reject)=>{
    reject('失败')
    // throw new Error('失败了')
})
//.catch默认请求下只执行第一个 如果需要走下面的 那么需要你报错
promise.catch((error)=>{
    console.log(`第一次`, error);
    throw new Error('失败了')
}).catch((error)=>{
    console.log(`第二次`, error);
    throw new Error('失败了')
}).catch((error)=>{
    console.log(`第三次`, error);
    throw new Error('失败了')
}).catch()
    .catch((error)=>{
    console.log(`第n次`, error);
})
  • finally 执行完成调用的
示例
//promise  promise只能满足于一种状态 进入到成功它就成功了 进入失败就失败了
var success = new Promise((resolve,reject)=>{
    //成功的函数调用 传递对应的参数
    resolve('成功')
    reject('失败')
    // throw new Error('失败了')
})
//成功的回调 俩个参数 成功的函数 失败的函数
success.then((res)=>{//res会接收resolve传递的参数
    console.log(`res`, res);
},(error)=>{//error 接收reject传递的参数
    console.log(`error`, error);
})
//失败的回调 参数1个 传递为一个函数 这个函数可以接收rejected传递的参数
success.catch((error)=>{
    console.log(`rejected`, error);
})
//完成就能调用的函数 (成功  失败)
success.finally(()=>{
    console.log(`完成了`);
})
静态方法
  • resolve (返回成功状态的promise)

  • reject (返回失败状态的promise)

  • all (并行执行所有的promise 如果遇到rejected就返回reject的promise 如果全部成功就返回所有的结果(promiseresult))

  • allSettled (互不影响执行对应的promise 返回所有的结果(状态一定是成功))

  • race (返回最快执行完成的promise)

//静态方法 Promise.方法名
//resolve方法
//返回一个成功的promise对象
var promise = Promise.resolve('hello')
console.log(`promise`, promise);
promise.then(res=>{
    console.log(res);
})
// reject返回一个失败的promise对象
var promise1 = Promise.reject('错误')
console.log(`promise1`, promise1);
promise1.catch(error=>{
    console.log(error);
})
//all 传入一个promise数组 并行执行promise数组里面的promise (如果有一个是rejected 那么整体都是rejected)
var promise2 = Promise.resolve('hello')
var promise3 = Promise.reject('错误')
// var promise3 = Promise.resolve('成功')
var promise4 = Promise.resolve('world')
var promise5 = Promise.resolve('你好')
//all方法返回的是一个promise对象 如果全部成功对应的promiseResult里面的结果就是所有成功 否则就是错误的结果
var promise6 = Promise.all([promise2,promise3,promise4,promise5])
console.log(promise6);
//传入一个promise数组 返回一个promise对象  不会互相影响 返回所有结果(状态为成功)
var promise7 = Promise.allSettled([promise2,promise3,promise4,promise5])
console.log(promise7);
//竞速 race  传入promise数组 返回最快走完的promise
var  promise8 =  Promise.race([promise2,promise3,promise4,promise5])
console.log(promise8);
promise的三种状态图
回调地狱

概述:回调函数的无限嵌套导致当前代码失去了对应的维护价值及对应的可读性。

示例
//传入一个函数作为回调函数 在对应代码执行完成调用
function fn(fn) {
    setTimeout(function () {
        console.log('10');
        //走完了以后回调函数执行
        fn()
    }, 1000)
}
fn(() => {
    console.log(1);
})
//多个回调函数嵌套 回调地狱 (回调函数的无限嵌套 代码的可读性 可维护性 已经失去了)
fn(() => {
    console.log(1);
    fn(() => {
        console.log(2);
        fn(() => {
            console.log(3);
            fn(() => {
                console.log(4);
                fn(() => {
                    console.log(5);
                    fn(() => {
                        console.log(6);
                        ....
                    })
                })
            })
        })
    })
})
promise来解决回调地狱(链式调用)

在.then里面返回一个新的promise对象 在对应的异步代码执行完后调用resolve

//利用promise来解决回调地狱的问题
new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log(1);
        resolve()
    });
}).then(() => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log(2);
            resolve()
        });
    })
}).then(()=>{
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log(3);
            resolve()
        });
    })
}).then(()=>{
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log(4);
            resolve()
        });
    })
}).then(()=>{
    console.log(5);
})
async await (es7新增的)

概述:async和await是对应的俩个连用的关键词,async是修饰函数的await是修饰promise的await只能在async内使用async修饰的函数返回一个promise对象await修饰的promise对象会占用当前的线程 直到对应的promise执行完成才会释放。

async function fn() {
    await new Promise((resolve,reject)=>{ //如果没有放行后面的不会执行
        setTimeout(() => {
            console.log('hello');
            resolve()
        })
    })
    console.log('world');
}
//async修饰完函数执行会返回一个promise对象
console.log(fn());
//async修饰的函数返回的promise对象 
//里面的报错 会使当前的promise对象的状态为rejected 
//如果里面return内容那么内容将会传递给对应的then
async function fn1(){
    throw new Error('错误')
    // return '我是fn1'
}
fn1().then(res=>{
    console.log(res);
},error=>{
    console.log(error);
})
//await会使用当前的函数的线程占用 直到对应的修饰的promise执行完成
// await Promise.reject() 报错
利用async和await来解决回调地狱
function fn(v,delay) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log(v);
            resolve()
        },delay);
    })
}
async function fn1(){
    await fn(1,1000)
    await fn(2,2000)
    await fn(3,100)
    await fn(4,0)
    console.log(5);
}
fn1()
总结
  • async修饰函数的

  • await修饰promise对象

  • async里面使用await 那么如果这个await修饰的promise没有执行完,那么对应的async修饰的函数返回promise状态时pending。

  • 如果async修饰的函数内什么都没有那么对应返回的promise状态是成功(默认函数返回undefined)

  • async修饰的函数 返回值就是成功 返回的值传递给then方法

  • async修饰的函数如果里面报错 那么返回的是失败 传递的值为报的错

  • await只能在async里面使用 await会使当前的函数陷入等待

代码执行机制


同步代码执行比异步代码快

同步代码的执行是利用对应的js引擎解析的

异步代码执行是利用事件轮询机制执行的

事件轮询机制

  • 先找script标签里面的微任务

  • 按照微任务队列执行完对应的微任务

  • 进入下一个宏任务 执行对应的宏任务代码

  • 进行宏任务对应微任务队列 执行对应微任务

  • 再进行到下一个宏任务 执行对应的微任务

  • 直到对应的宏任务队列和微任务队列被清空

宏任务

script 定时器(setInterval setTimeout) 事件...

微任务

promise.then promise.catch nextTick ....

二十三).内容回顾及总结

前端体系结构及知识点

一阶段(html+css)

  • 基础的页面布局(div 弹性盒子布局)

  • 动画效果

  • seo优化(搜索引擎优化)

  • html5和css3

  • 多端适配(rem)

二阶段(JavaScript)

  • DOM(文档操作)

  • BOM(浏览器操作(路由实现))

  • ES(ecmaScript 基础语法)

  • node.js(后端)

三阶段(应用)

  • MVVM(双向绑定 数据和视图之间实现绑定 数据驱动视图)

  • 框架 (vue(封装的使用),react(原生js))

  • 组件 (抽取功能代码 复用)

  • uniapp(利用vue来书写代码 可以打包多端)及小程序(微信小程序)

基础内容回顾


第一周内容
  • 1、JavaScript入门(变量定义 常见关键词 基础语法 基础数据类型及相关转换)

  • 2、运算符及表达式(算术运算 逻辑运算 比较运算 位运算 赋值运算 三目运算)

  • 3、条件判断语句 (if else switch case)

  • 4、循环 (while do_while for 时间复杂度 O1>OlogN>On>OnLogN>On^2>On^3)

  • 5、函数 (函数作用域 全局作用域 作用域链 (函数的基本使用 抽取公共代码形成达到复用) arguments(参数数组))

第二周内容
  • 1、数组(数组的定义 数组的增删改查 (push pop shift unshift concat sort reverse slice splice join...))

  • 2、字符串 (字符串特性 字符串的相关方法(charAt charCodeAt indexOf lastIndexOf search slice substring substr toUppercase toLowerCase concat split replace macth..))

  • 3、日期及对象讲解 (Date 的相关方法 对象的声明(键值对象)对象的属性获取 (对象名.属性名 对象名[属性名字符串]))

  • 4、BOM (window对象及底下的子对象(location(属性 hash search href... 方法 assign replace reload) history (属性 length state 方法 go back forward pushstate replacestate)screen navigator frames document))

  • 5、DOM(文档对象 元素 属性 文本 对应的关于元素操作的增删改查的方法(元素自带的属性) 对应的相关的节点获取相关属性 属性操作的相关方法 )

第三周内容
  • 1、DOM回顾 (DOM的相关节点的操作以及属性节点的获取)

  • 2、事件(常用事件 鼠标事件(click dblclick mouseup mousedown mouseenter mouseleave mouseover mouseout mousemove..) 键盘事件(keydown keyup keypress) html事件(change select input reset submit focus blur load close unload...))

  • 3、事件(事件模式(冒泡 捕获)事件对象event 事件委托(event.target)阻止默认行为 (e.preventDefault || e.returnValue = false)阻止事件冒泡 (e.stoppagation || e.canclebubble = true))

  • 4、cookie (cookie的诞生(解决http 无状态问题(存储sessionID)cookie的格式(key=value;expires=过期时间;path=地址;domain=跨域地址;secure 安全))cookie和localstroage的区别 sessionStroage)

  • 5、正则表达式 (正则的声明方式(// new Regex)元字符(+ ? . * [] {} () \w \d \s...)

第四周内容
  • 1、ES5和ES6(es5新增 (严格模式、模板字符串、数组高阶函数(forEach map reduce filter some every)、this指向更改(bind call apply)) es6新增(字符串新增、数组新增、对象新增、函数新增、声明关键词新增、基础值类型新增、解构和扩展运算符、class新增 extends class的继承 set 和 map新增的数据解构 模块化(import export)))

  • 2、运动(匀速(改变的步骤不变) 缓存(步长越来越小) 链式(回调函数来实现)setInterval + dom操作)

  • 3、面向对象 (对象构建方式(对象的封装) 利用面向对象思想来书写案例)

  • 4、原型和继承 (构造函数的原型prototype 对象的原型__proto__(对象的原型指向构造函数的原型) 原型链(对象在原型(__proto__)找属性的过程) 继承实现(class extends 继承、原型链继承、对象冒充、组合继承、寄生组合继承))

  • 5、闭包和promise (闭包的特性 闭包的应用(防抖 节流 函数柯里化) promis(es6新增的一个类 它有三种状态 pending fulfiling rejected)promis的相关方法(then catch finally resolve reject all allsettled race))

  • 6、事件轮询机制(先执行同步代码 再处理异步 宏任务(script 定时器 IO...)微任务(promise.then promise.catch nextTick))

二十四).ajax

关键词

  • 线程(线程和进程的最小单位 JavaScript是单线程的语言(单线程解析 渲染线程也是单线程))

  • 进程(正在运行的程序)

  • 同步(一个线程执行(同步阻塞))

上一个没有做完 下一个不能执行

  • 异步(多个线程)

上一个和这一个没有关系((

概述

AJAX(asynchronous JavaScript and xml)异步的JavaScript和xml。他是用于发送http请求的,他可以发送异步请求。他可以完成页面的局部刷新功能(在整个页面不刷新的前提下 发送对应的请求改变对应的部分的dom),他的核心对象为XMLHttpRequest(xhr)。

请求流程

ajax的代码实现

 var xhr = new XMLHttpRequest()
        //以对应的请求方法来打开对应的请求地址
    xhr.open('get', 'http://jsonplaceholder.typicode.com/todos')
        //发送请求
    xhr.send()
        //监听请求状态的变化 readystate(1-5 1准备发送 2 发送完成 3 发送完成数据准备接收 4数据接收完毕 5 错误)
    xhr.onreadystatechange = () => {
        //进行判断对应的状态 4是数据接收完毕
        if (xhr.readyState == 4) {
            //responseText  表示返回的文本(字符串)
            console.log(xhr.responseText)
        }
    }

XMLHttpRequest对象的相关属性及方法

属性
  • readyState状态码

  • status http状态码

  • timeout超时时间

  • responseText响应文本

方法
  • open 打开一个请求

  • send发送请求

  • setRequestHeader设置请求头

  • getResponseHeader获取响应头

事件

onreadystatechange监听状态的改变

var xhr = new XMLHttpRequest()
//属性
//readyState 对应的xhr对象的一个状态码(这个状态码只有xhr才有)
// 0 没有请求  1准备发送 2请求已经发送 3请求发送完毕 准备接收响应数据 4响应接收完毕
console.log(xhr.readyState);
//status http状态码 (只要发送http请求都会有)
// 取值为100 - 599
// 1开头(表示成功 但是需要后续操作) 
// 2开头 (成功 200) 
// 3开头 (重定向 304) 
// 4开头 (表示客户端错误 404(找不到页面) 403(权限不足))
// 5开头 (表示服务器错误 500)
console.log(xhr.status);
//responseText 响应的文本
console.log(xhr.responseText);
//responseXML 响应的xml
console.log(xhr.responseXML);
//响应类型
console.log(xhr.responseType);
//响应的地址
console.log(xhr.responseURL);
//设置请求的超时时间 
console.log(xhr.timeout );
//方法
//设置请求 open 请求方式  请求地址
xhr.open('get','')
//发送方法 里面的传递的参数是设置给请求体的内容
xhr.send('')
//请求头相关 设置请求头 通过键值对的方式  键 值 都是字符串
xhr.setRequestHeader('Connection','keep-alive')
//响应头获取 通过key来获取value
xhr.getResponseHeader('Content-type')
//事件 当前状态改变的事件
xhr.onreadystatechange = ()=>{
    //判断readyState为4 http状态码以2开头
    if(xhr.readyState == 4 && /^2\d{2}$/.test(xhr.status)){
        //接收数据进行处理
    }
}

数据渲染案例

<button>请求数据</button>
<ul>
</ul>
<script>
    //获取ul
    var ul = document.querySelector('ul')
    // http://jsonplaceholder.typicode.com/todos?_limit=10&_page=2
    //get请求的传参使用?和&做拼接 search传参(query传参)
    //第一个前面要添加? 对应的多个参数使用&连接
    //get传参时使用地址的拼接来传递参数
    // _limit表示 个数  _page表示页数
    document.querySelector('button').onclick = function(){
        //请求数据
        var xhr = new XMLHttpRequest()
        //设置请求地址
        xhr.open('get','http://jsonplaceholder.typicode.com/todos')
        //发送请求
        xhr.send()
        //接收响应数据
        xhr.onreadystatechange = ()=>{
            if(xhr.readyState == 4 && /^2\d{2}$/.test(xhr.status)){
                //接收数据
                var str = xhr.responseText
                //将字符串转为对应的对象
                var result = JSON.parse(str)
                //渲染
                //遍历result进行渲染
                result.forEach(item => {
                    ul.innerHTML += ` <li>
                        id: ${item.id}
                        ${item.title}
                        ${item.completed?'💘':'💔'}
                    </li>`
                });
            }
        }
    }
</script>

get请求封装

//封装一个对应的get请求的方法
//请求地址   参数 (以对象传递)  对应的处理不一样
export function get(url,params={},callback){
    //判断url地址是否传递 如果没有传递直接报错
    if(!url){
        throw new Error('地址必须传递')
    }
    //新建请求对象
    let xhr = new XMLHttpRequest()
    //设置请求地址 (拼接参数到url)
    //遍历对象中所有的属性
    for(let key in params){
        // {_limit:10,_page:1}
        // http://jsonplaceholder.typicode.com/todos?_limit=10&_page=1
        //判断url里面是否存在? 如果没有就加上
        if(!url.includes('?')){
            url+=`?${key}=${params[key]}`
        }else{
            url+=`&${key}=${params[key]}`
        }
    }
    xhr.open('get',url)
    //发送请求
    xhr.send()
    //监听请求状态改变
    xhr.onreadystatechange = ()=>{
        //判断是否成功
        if(xhr.readyState == 4 && /^2\d{2}$/.test(xhr.status)){
            //成功调用回调函数传递参数出去
            //先转为对象再传递出去
            callback(JSON.parse(xhr.responseText))
        }
    }
}

分页渲染示例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <button>1</button>
    <button>2</button>
    <button>3</button>
    <button>4</button>
    <button>5</button>
    <ul>
    </ul>
    <script type="module">
        import {get} from './ajax.js'
        //获取ul
        var ul = document.querySelector('ul')
        //获取所有按钮
        var btns = document.querySelectorAll('button')
        //遍历按钮添加事件
        Array.from(btns).forEach((v,i)=>{
            v.onclick = ()=>{
                get('http://jsonplaceholder.typicode.com/todos',{
                    _limit:10,
                    _page:i+1
                },(res)=>{
                    ul.innerHTML = ''
                    res.forEach(item => {
                        ul.innerHTML += ` <li>
                            id: ${item.id}
                            ${item.title}
                            ${item.completed?'💘':'💔'}
                        </li>`
                    });
                })
            }
        })
    
    </script>
</body>
</html>

简单的post请求

 <!-- action 提交的地址  method 提交的方式 -->
 <form action="https://jsonplaceholder.typicode.com/posts" method="post">
        <!-- 如果使用action进行提交 那么提交的内容在对应的输入框中 提交的时候以key value提交
        name属性表示的是key  对应的value是输入框的值 
        <input type="text" name="uname">
        <input type="password" name="upwd">        
        <button type="submit">登录</button>
    </form>
    <!-- 通过form发送的post请求 需要指定请求方式 这个请求的数据以表单形式发送的 
    没有指定请求方式 默认以get方式提交 
        数据暴露在地址栏  他是通过?和&进行拼接 (对应的get请求提交的数据通过字符串的形式)
        get请求提交的数据有限 (2kb)
        get请求因为在地址栏 所以他会有历史记录
        get请求是不安全
    只有指定为post才会以post请求提交
        post请求 数据是会封装成表单对象存储在对应的请求体中进行提交
        数据提交的容量 要远远大于get请求的
        而post请求没有历史记录
        post安全性要高于get请求
    -->
    <!-- 异步的post请求 -->
    <script>
        //获取form表单
        document.forms[0].onsubmit = ()=>{
            //获取input框的数据
            var inputs = document.querySelectorAll('input')
            var username = inputs[0].value
            var password = inputs[1].value
            //新建请求对象
            var xhr = new XMLHttpRequest()
            //打开连接
            xhr.open('post','https://jsonplaceholder.typicode.com/posts')
            //设置请求头 告诉后台的我的内容为表单形式
            //内容的类型为表单形式
            xhr.setRequestHeader('Content-type','application/x-www-form-urlencoded')
            //发送数据 key=value&key=value send里面的数据放入在请求体
            xhr.send(`username=${username}&password=${password}`)
            //监听状态改变
            xhr.onreadystatechange = ()=>{
                if(xhr.readyState == 4 && /^2\d{2}$/.test(xhr.status)){
                    console.log(xhr.responseText);
                }
            }
            //禁止默认行为
            return false
        }
    </script>

post请求封装

//封装post请求
export function post(url,params={},callback){
    //判断url地址是否传递 如果没有传递直接报错
    if(!url){
        throw new Error('地址必须传递')
    }
    //新建请求对象
    let xhr = new XMLHttpRequest()
    //设置请求地址 (拼接参数到url)
    xhr.open('post',url)
    //设置请求头
    xhr.setRequestHeader('content-type','application/x-www-form-urlencoded')
    //数据拼接发送
    let paramsStr = ""
    for(let key in params){
        // {_limit:10,_page:1}
        // _limit=10&_page=1
        paramsStr += `&${key}=${params[key]}`
    }
    //删除最前面的&
    paramsStr = paramsStr.substring(1)
    //发送请求
    xhr.send(paramsStr)
    //监听请求状态改变
    xhr.onreadystatechange = ()=>{
        //判断是否成功
        if(xhr.readyState == 4 && /^2\d{2}$/.test(xhr.status)){
            //成功调用回调函数传递参数出去
            //先转为对象再传递出去
            callback(JSON.parse(xhr.responseText))
        }
    }
}

ajax封装

//将post请求和get请求抽取 
export const ajax = (url,option,callback)=>{
    //判断是否具备url
    if(!url){
        throw new Error('地址必须传递')
    }
    //你传什么就改什么 不传为默认值
    let defaultOption = {
        method:'get',
        data:{},
        contentType:'application/json',
        timeout:'3000'
    }
    //遍历对象里面key
    for(let key in option){
        //默认的选项里面存在这个key
        if(defaultOption[key]){
            //用option里面对应key的值来替换默认值
            defaultOption[key] = option[key]
        }
    }
    //新建请求对象
    let xhr = new XMLHttpRequest()
    //判断是get请求还是post请求
    if(defaultOption.method == 'get'){
        //设置请求地址 (拼接参数到url)
        //遍历对象中所有的属性
        for(let key in defaultOption.data){
            // {_limit:10,_page:1}
            // http://jsonplaceholder.typicode.com/todos?_limit=10&_page=1
            //判断url里面是否存在? 如果没有就加上
            if(!url.includes('?')){
                url+=`?${key}=${defaultOption.data[key]}`
            }else{
                url+=`&${key}=${defaultOption.data[key]}`
            }
        }
    }
    xhr.open(defaultOption.method,url)
    //设置请求头
    xhr.setRequestHeader('content-type',defaultOption.contentType)
    //判断是否为post请求
    if(defaultOption.method == 'post'){
        //数据拼接发送
        let paramsStr = ""
        for(let key in defaultOption.data){
            // {_limit:10,_page:1}
            // _limit=10&_page=1
            paramsStr += `&${key}=${defaultOption.data[key]}`
        }
        //删除最前面的&
        paramsStr = paramsStr.substring(1)
        //发送请求
        xhr.send(paramsStr)
    }else{
        //发送请求
        xhr.send()
    }
    //监听请求状态改变
    xhr.onreadystatechange = ()=>{
        //判断是否成功
        if(xhr.readyState == 4 && /^2\d{2}$/.test(xhr.status)){
            //成功调用回调函数传递参数出去
            //先转为对象再传递出去
            callback(JSON.parse(xhr.responseText))
        }
    }
}

ajax promise封装

//将post请求和get请求抽取 
export const ajax = (url,option)=>{
    //判断是否具备url
    if(!url){
        throw new Error('地址必须传递')
    }
    //你传什么就改什么 不传为默认值
    let defaultOption = {
        method:'get',
        data:{},
        contentType:'application/json',
        timeout:'3000'
    }
    //遍历对象里面key
    for(let key in option){
        //默认的选项里面存在这个key
        if(defaultOption[key]){
            //用option里面对应key的值来替换默认值
            defaultOption[key] = option[key]
        }
    }
    //新建请求对象
    let xhr = new XMLHttpRequest()
    //判断是get请求还是post请求
    if(defaultOption.method == 'get'){
        //设置请求地址 (拼接参数到url)
        //遍历对象中所有的属性
        for(let key in defaultOption.data){
            // {_limit:10,_page:1}
            // http://jsonplaceholder.typicode.com/todos?_limit=10&_page=1
            //判断url里面是否存在? 如果没有就加上
            if(!url.includes('?')){
                url+=`?${key}=${defaultOption.data[key]}`
            }else{
                url+=`&${key}=${defaultOption.data[key]}`
            }
        }
    }
    xhr.open(defaultOption.method,url)
    //设置请求头
    xhr.setRequestHeader('content-type',defaultOption.contentType)
    //判断是否为post请求
    if(defaultOption.method == 'post'){
        //数据拼接发送
        let paramsStr = ""
        for(let key in defaultOption.data){
            // {_limit:10,_page:1}
            // _limit=10&_page=1
            paramsStr += `&${key}=${defaultOption.data[key]}`
        }
        //删除最前面的&
        paramsStr = paramsStr.substring(1)
        //发送请求
        xhr.send(paramsStr)
    }else{
        //发送请求
        xhr.send()
    }
   return new Promise((resolve,reject)=>{
     //监听请求状态改变
     xhr.onreadystatechange = ()=>{
        //判断是否成功
        if(xhr.readyState == 4){
            //成功调用resolve传递数据
            //先转为对象再传递出去
            if(/^2\d{2}$/.test(xhr.status)){
                resolve(JSON.parse(xhr.responseText))
            }
            if(/^4\d{2}/.test(xhr.status)){
                reject()
            }
        }
    }
    //如果xhr对象有错也调用reject
    xhr.onerror = ()=>{
        reject()
    }
   })
}

二十五).JSONP讲解

同源策略(浏览器的一种机制)

概述:

浏览器为了安全,他产生一种同源策略,这个策略是为了防止一些恶意的请求,保护对应的隐私。

同源策略主要是对应三个内容 分别为
  • 同协议 (http/https)

  • 同ip地址 (127.0.0.1本机)

  • 同端口号 (80 HTTP的 443 HTTPS的)

如果不同源就会产生一个问题就是跨域问题

import {ajax} from './ajax_promise.js'
//请求一个地址 https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd=miqi&cb=fn
ajax('https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd=miqi&cb=fn',{}).then(res=>{
    console.log(res);
})
跨域问题的产生(以下任意一点不同就会跨域)
  • 协议不一样

  • 端口不一样

  • ip地址不一样

  • 以文件访问(不同的文件资源也会跨域)

跨域问题的解决

1.前端解决 (JSONP)(需要后端给的是JSONP的接口)

2.后端解决 (在对应的响应头进行设置)

respones.setHeader('Access-Control-Allow-Origin','*')//所有的地址都可以访问我
respones.setHeader('Access-Control-Allow-Origin-Method','*')//所有的请求都可以访问我

3.使用服务器代理(proxy)

4.使用websocket(走的不是http协议)

JSONP

概述:

他是通过script标签不受跨域的影响的特性来解决跨域问题的。(核心就是对应的请求不是我去请求而是服务器自己请求自己将数据通过回调函数传给我),他的核心还是一个get请求

简单的JSONP
<!-- wd表示关键词 cb表示回调函数 -->
<script>
    //他会自动执行你传入的回调函数并且将结果传递给这个函数
    //res里面就会执行完的数据
    function callback(res){
        console.log(res);
    }
</script>
<script src="https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd=miqi&cb=callback"></script>
jsonp封装
//请求地址 参数 回调函数 回调函数
export const jsonp=(url,param,callbackName,callback)=>{
    //提取param参数加入到url中
    for(let key in param){
        if(url.includes('?')){
            url += `&${key}=${param[key]}`
        }else{
            url += `?${key}=${param[key]}`
        }
    }
    //将回调函数加给window
    //随机生成一个名字
    let fnName = 'fn'+Date.now()+Math.ceil(Math.random()*30)
    window[fnName] = callback
    //将回调的函数及相关名字拼接到url
    url += `&${callbackName}=${fnName}`
    console.log(url);
    //创建一个script标签
    let script = document.createElement('script')
    //给这个script标签设置src地址
    script.src = url
    document.body.appendChild(script)
    //(必须等待script标签加入到页面才会触发)
    script.onload = ()=>{
        // 将script标签加入到对应的页面
        // 将window的函数删了
        delete window[fnName]
        //再将script标签删除
        script.remove()
    }
}

案例:百度搜索

<input type="text" />
<ul>
</ul>
<script type="module">
    // 导入jsonp
    import {jsonp} from './JSONP.js'
    //https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd=miqi&cb=fn
    document.querySelector('input').oninput = function(){
        jsonp('https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su',{
            wd:this.value
        },"cb",(res)=>{
            document.querySelector('ul').innerHTML = ""
            res.s.forEach(v=>{
                document.querySelector('ul').innerHTML +=  `
                    <li>${v}</li>
`
            })
        })
    }
</script>
JSONP的promise封装
//请求地址 参数 回调函数 回调函数
export const jsonp=(url,param,callbackName)=>{
    return new Promise((resolve,reject)=>{
        //提取param参数加入到url中
        for(let key in param){
            if(url.includes('?')){
                url += `&${key}=${param[key]}`
            }else{
                url += `?${key}=${param[key]}`
            }
        }
        //将回调函数加给window
        //随机生成一个名字
        let fnName = 'fn'+Date.now()+Math.ceil(Math.random()*30)
        window[fnName] = resolve
        //将回调的函数及相关名字拼接到url
        url += `&${callbackName}=${fnName}`
        console.log(url);
        //创建一个script标签
        let script = document.createElement('script')
        //给这个script标签设置src地址
        script.src = url
        document.body.appendChild(script)
        //(必须等待script标签加入到页面才会触发)
        script.onload = ()=>{
            // 将script标签加入到对应的页面
            // 将window的函数删了
            delete window[fnName]
            //再将script标签删除
            script.remove()
        }
        script.onerror =()=>{
            reject()
        }
    })
}
测试
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        ul{
            list-style: none;
        }
    </style>
</head>
<body>
    <input type="text" />
    <ul>
    </ul>
    <script type="module">
        // 导入jsonp
        import {jsonp} from './JSONP_promise.js'
        //https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd=miqi&cb=fn
        document.querySelector('input').oninput = function(){
            jsonp('https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su',{
                wd:this.value
            },"cb").then((res)=>{
                document.querySelector('ul').innerHTML = ""
                res.s.forEach(v=>{
                    document.querySelector('ul').innerHTML +=  `
                        <li>${v}</li>
                    `
                })
            })
        }
    </script>
</body>
</html>

二十五).设计模式

概述:

设计模式是一种固定的解决某个问题的一种方式,他是一个固定的模式(原理都是一样的),他不区分语言。常用的设计模式有23种,他分为三类(主要针对的是类和对象)。

设计模式分类

  • 创建型 (创建对象)单例模式、工厂模式

  • 结构型(将多个小结构变成一个大结构)组合模式 代理模式 装饰器模式 适配器模式

  • 行为型 (对应类和对象的行为进行相关处理)观察者模式

设计模式的原则

  • 开闭原则

  • 里式置换原则

  • 单一原则

  • 依赖倒置原则

  • 接口隔离原则

  • 迪米特原则

工厂模式讲解

  • 根据对应的工厂方法生成对应的对象(生产相关对象)

function factory(name){
    //手动构建对象
    var obj = new Object()
    //给对象进行属性赋值
    obj.name = name
    //手动返回对象
    return obj
}
//调用工厂生产对象
let obj1 = factory('jack')
let obj2 = factory('tom')

单例模式

  • 保证生成的对象实例只有一个

基础类
//基础类
class Person{
    constructor(){
        
    }
}
闭包实现
//闭包的单例模式
function closureSingleton(){
    //接收返回对象的变量
    var instance = null
    return function(){
        //如果当前的变量值为null
        if(!instance){
            //构建一个对象赋值给返回的变量
            instance = new Person()
        }
        return instance
    }
}
let single = closureSingleton()
let person1 = single()
let person2 = single()
console.log(person1==person2)
原型
//原型的单例
function prototypeSingleton(){
    //判断原型上是否具备newPerson这个属性
    if(!prototypeSingleton.prototype.instance){
        prototypeSingleton.prototype.instance = new Person()
    }
    return prototypeSingleton.prototype.instance
}
let person3 = prototypeSingleton()
let person4 = prototypeSingleton()
console.log(person3 == person4)
静态属性
//静态属性单例
function staticSingleton(){
    if(!staticSingleton.instance){
        staticSingleton.instance = new Person()
    }
    return staticSingleton.instance
}
let person5 = staticSingleton()
let person6 = staticSingleton()
console.log(person5 == person6)
全局属性实现
  • window是浏览器global对象 但是其他的global对象不是window

//全局对象实现
function globalSingleton(){
    if(!window.instance){
        window.instance = new Person()
    }
    return window.instance
}
let person7 = globalSingleton()
let person8 = globalSingleton()
console.log(person7 == person8)

组合模式 (*)

  • 将多个对象的相同方法或者属性组合到一块(将不同的方法组合在一块执行)

基础内容
class GoToHome{
    constructor(){
        this.action = ()=>{
            console.log('回家')
        }
    }
}
class OpenComputer{
    constructor(){
        this.action = ()=>{
            console.log('打开电脑')
        }
    }
}
class PlayGame{
    constructor(){
        this.action = ()=>{
            console.log('玩游戏')
        }
    }
}
new GoToHome().action()
new OpenComputer().action()
new PlayGame().action()
将多个的方法组合在一块进行统一调用
class Combiner{
    constructor(){
        this.list = []
    }
    //将对应的对象加入到list中
    push(obj){
        this.list.push(obj)
    }
    //统一执行相关函数
    execute(fnName,...arg){
        //遍历list
        this.list.forEach(item=>{
            //执行对应的函数
            item[fnName].call(this,...arg)
        })
    }
}
let combiner = new Combiner()
//将对象加入
combiner.push(new GoToHome())
combiner.push(new OpenComputer())
combiner.push(new PlayGame())
//执行action方法
combiner.execute('action')
组合模式在vue中的使用
  • Vue.use 方法(自动执行install)

  • install 方法

  • 如果在use里面传入的是一个函数 那么他会把这个函数直接当成install 如果传的是对象 他会自动去找里面的instal方法

模拟实现use和install
class Vue {
    constructor() {
        this.fnList = []
    }
    use(obj) {
        //如果他是一个函数 那么这个函数直接当成install
        if (typeof obj == 'function') {
            this.fnList.push(obj)
        }
        //如果他是对象就找里面的install
        if (typeof obj == 'object') {
            this.fnList.push(obj.install)
        }
        //调用执行
        this.execute()
    }
    execute() {
        //遍历对应的函数列表
        this.fnList.forEach((fn) => {
            fn()
        })
    }
}
var vue = new Vue()
vue.use({
    install() {
        console.log('你好')
    }
})
vue.use({
    install() {
        console.log('世界')
    }
})
vue.use(() => {
    console.log('哈哈哈哈')
})

装饰器模式

场景

我现在有三个车的类,这个三个车他都有对应的跑的方法,现在我需要给这个三个车加一个飞的方法(设计模式开闭原则,一个类一旦写完了不允许内部再进行修改),这个时候就可以使用对应的装饰器模式。

概述

装饰器模式是在不影响原本类的基础上,进行功能扩展。他又叫做包装模式。

实现
基础类
class Car{
    constructor(){
    }
    run(){
        console.log('跑')
    }
}
包装类
class Decorator{
    //将基础类传入包装类
    constructor(car){
        this.car = car
    }
    fly(){
        console.log('飞')
        this.car.run()
    }
}
new Decorator(new Car()).fly()

在typeScript中有个对应的装饰器修饰 @Decorator

观察者模式 (*)

概述:

观察者模式是前端最常用的模式,他又被称为发布者-订阅者模式。他的核心是一个对应的发布者进行发布,以及对应的有个订阅者(对于发布的内容进行监听),发布者将对应的内容发布,订阅者就会收到信息从而进行对应的处理。

示例
  • 李丹喜欢看女主播(他关注一个女主播 冯提莫)

  • 冯提莫开播(发布) ----- 李丹收到开播信息 (订阅)----- 李丹就去看(处理)

  • 发布者 冯提莫

  • 订阅者 李丹

  • 处理 去直播间

观察者模式的核心内容
  • 发布者

  • 订阅者

  • 相关处理

其实我们的事件就是一个观察者模式(事件发布者 事件监听者 事件处理)

element.addEventListener('事件名',处理函数)
element.removeEventListener('事件名',处理函数)
简单的事件示例
<button>点我</button>
<script>
let btn = document.querySelector('button')
btn.addEventListener('click',handler)
function handler(){
    console.log('点击了')
}
</script>
  • 发布者 button

  • 订阅者 JavaScript引擎

  • 处理 handler

分析这个事件的相关内容
  • 发布者

  • 事件名

  • 处理函数

相关关系
  • 事件名和处理函数的关系 一对多 (一个事件可以有多个处理函数)click:[handler1,handler2]

  • 发布者和事件名的关系 一对多 (一个发布者可以发布多个事件) {事件名:[处理函数]}

根据对应的事件监听机制来模拟观察者
事件监听的过程
  • 事件发布 (有对应的事件名)on

  • 事件执行 (根据对应的事件名执行相关的处理函数) emit

  • 事件取消 (将对应的事件移除)off

代码构建
class obServer{
    constructor(){
        //{click:[handler1,handler2],mousemove:[handler1,handler2]}
        this.obj = {}
    }
    //事件发布 事件名  处理函数
    on(eventName,handler){
        
    }
    //事件执行 事件名  参数
    emit(eventName,...arg){
    
    }
    //事件取消 事件名  处理函数
    off(eventName,handler){
        
    }
}
代码完善
on方法
class ObServer{
    constructor(){
        //{click:[handler1,handler2],mousemove:[handler1,handler2]}
        this.obj = {}
    }
    //事件发布 事件名  处理函数
    on(eventName,handler){
        //判断这个eventName是否存在
        //如果当前的事件名不存在 给他赋值一个空数组
        if(!this.obj[eventName]){
            this.obj[eventName] = []
        }
        //将事件添加进去
        this.obj[eventName].push(handler)
    }
    //事件执行 事件名  参数
    emit(eventName,...arg){
    
    }
    //事件取消 事件名  处理函数
    off(eventName,handler){
        
    }
}

emit方法

class ObServer{
    constructor(){
        //{click:[handler1,handler2],mousemove:[handler1,handler2]}
        this.obj = {}
    }
    //事件发布 事件名  处理函数
    on(eventName,handler){
        //判断这个eventName是否存在
        //如果当前的事件名不存在 给他赋值一个空数组
        if(!this.obj[eventName]){
            this.obj[eventName] = []
        }
        //将事件添加进去
        this.obj[eventName].push(handler)
    }
    //事件执行 事件名  参数
    emit(eventName,...arg){
        //判断这个eventName是否存在
        if(!this.obj[eventName]) return
        //获取这个事件里面所有的处理函数 执行这些处理函数
        //遍历对应的处理函数数组
        this.obj[eventName].forEach(handler=>{
            //执行对应的处理函数 传入参数
            handler.call(this,...arg)
        })
    }
    //事件取消 事件名  处理函数
    off(eventName,handler){
        
    }
}

off方法

class ObServer{
    constructor(){
        //{click:[handler1,handler2],mousemove:[handler1,handler2]}
        this.obj = {}
    }
    //事件发布 事件名  处理函数
    on(eventName,handler){
        //判断这个eventName是否存在
        //如果当前的事件名不存在 给他赋值一个空数组
        if(!this.obj[eventName]){
            this.obj[eventName] = []
        }
        //将事件添加进去
        this.obj[eventName].push(handler)
    }
    //事件执行 事件名  参数
    emit(eventName,...arg){
        //判断这个eventName是否存在
        if(!this.obj[eventName]) return
        //获取这个事件里面所有的处理函数 执行这些处理函数
        //遍历对应的处理函数数组
        this.obj[eventName].forEach(handler=>{
            //执行对应的处理函数 传入参数
            handler.call(this,...arg)
        })
    }
    //事件取消 事件名  处理函数
    off(eventName,handler){
        //判断这个eventName是否存在
        if(!this.obj[eventName]) return
        //遍历对应的eventName里面处理函数数组 找到匹配的handler将他删除
        this.obj[eventName].forEach((v,i)=>{
            if(Object.is(v,handler)){
                this.obj[eventName].splice(i,1)
            }
        })
    }
}
注意事项
  • emit执行他可以传参传给对应的on方法里面处理函数(vue中父传子的实现及bus传值的实现)

  • off调用一定要emit之前

  • 观察者模式是vue2底层实现

代理模式(*)

概述:

在不改变原本的类的基础上,对于对象进行功能加强,代理模式代理出来的是对象。(代理模式低耦合)

示例
  • 你家里需要装修 但是你不想动手 这个时候你就会找一个装修队(代替你进行装修)

  • 代理对象和被代理对象是俩个不同的对象,但是实际操作的内容是一个

核心关键词
  • 被代理对象 你

  • 代理对象 装修对象

  • 操作内容 你家的房子

代理的实现
  • 在js中 es7新增了一个Proxy的类 这个类专门是用来做代理的 所以我们只需要掌握这个类的使用就ok了

Proxy的使用
新建代理对象(通过proxy的构造)

let proxy = new Proxy(被代理对象,处理对象)

基础使用
//被代理对象
let obj = {
    name: 'jack',
    age: 18
}
//通过proxy来新建代理对象 
//Proxy的构造函数需要传入被代理对象 以及相关的处理对象
//对于handler他是对于所有的属性进行操作
//get在获取属性的时候调用  他返回的结果就是你接收的结果
//set对于设置属性的时候调用
let proxy = new Proxy(obj,{
    //目标对象(被代理对象) 属性(实际访问的属性)  代理对象(当前proxy)
    get(target,attribute,proxyObj){ //这个里面返回的值就是当前的获取值
        console.log('调用了get');
        if(attribute == 'name'){
            return '姓名为'+target[attribute] 
        }else if(attribute == 'age'){
            return target[attribute]+'岁'
        }
        return target[attribute] 
    },
    //目标对象(被代理对象) 属性(实际访问的属性)值 代理对象(当前proxy)
    set(target,attribute,value,proxyObj){
        console.log('调用了set');
        target[attribute] = value
    },
    //定义属性 赋值新的属性  目标对象  属性名  属性的详情信息
    defineProperty(target,attribute,descriptor){
        console.log('新的属性定义');
    },
    //删除属性 delete的时候
    deleteProperty(target,attribute,proxyObj){
        console.log('删除属性');
        delete target[attribute]
    }
})
proxy的处理对象的四大属性
  • get 访问属性的时候调用

console.log( proxy.name); //调用get
console.log( proxy.age); //调用get

set 设置属性的时候调用

proxy.name = '你好' //调用set

defineProperty 定义属性的时候调用

Object.defineProperty(proxy,'sex',{
    configurable:true,//是否可以删除
    enumerable:true,//是否可以遍历
    value:'男',//属性值
    writable:true//是否可以改变
})

deleteProperty 删除属性的时候调用

delete proxy.name //调用deleteProperty
总结
  • Proxy是一个es7新增的一个类 他返回的是一个对象

  • Proxy里面传入被代理对象和对应的处理对象

  • 处理对象包含4个方法(get set defineProperty deleteProperty)

  • Proxy里面实际操作是被代理对象 (如果在里面操作代理对象会造成栈溢出)

  • 代理对象和被代理对象不是一个对象 但是操作的内容是一个都是被代理对象

  • Proxy是vue3的底层实现之一

适配器模式

概述

将旧的的内容进行新的适配,在原本的基础上做兼容处理。

常用的业务场景
  • 旧的接口替换新的接口(在不改变原本内容的情况下完成替换)

  • 表单验证 (根据场景切换相关的验证)

示例
  • 家庭用电最大的功率为22v

  • 接入的电压220v

  • 中间就需要继电器(适配口)

代码实现
class phone{
    constructor(){
        
    }
    fn(){
        return 22
    }
}
class Adaptive{
    constructor(){
        
    }
    fn(){
        return '220转为'+new Phone().fn()
    }
}
//实际使用的是对于的适配的内容
new Adaptive().fn()

二十六).Object方法讲解

概述:

Object这个类是所有的父类,也就是说他的方法和属性所有的对象都可以使用。Object的方法和属性是提供给其他的对象进行使用的。

相关方法和属性

原型方法及属性(实例方法和属性)

  • hasOwnProperty(判断当前对象是否具备这个属性 不会读取原型)

let obj = {name:'jack'}
function Person(name,age){
    this.name = name
    this.age = age
}
Person.prototype.sex = '男'
let person = new Person('tom',18)
person.address = '湖南长沙'
//hasOwnProperty 判断当前对象是否具备属性的 返回的值为布尔类型 传入对应需要判断的属性名 
console.log( obj.hasOwnProperty('name'));//true
console.log( person.hasOwnProperty('age'));//true
//hasOwnProperty不会读取原型的属性
console.log( person.hasOwnProperty('sex'));//false
console.log(person.hasOwnProperty('address'));//true
  • isPrototypeOf(判断当前是否存在对象的原型链上)

//isPrototypeOf 是否对象存在于原型链上 
//组合继承
function Son(){
    Person.call(this)
}
let person1 = new Person('tom',18)
Son.prototype = person1
//组合继承
function Child(){
    Son.call(this)
}
let son = new Son()
Child.prototype = son
let child = new Child()
//person1是否处在对应son的原型(__proto__)上
//判断当前对象是否处在传入对应对象的__proto__(原型链上)上
console.log(person1.isPrototypeOf(son));//true
console.log(son.isPrototypeOf(child));//true
console.log(person1.isPrototypeOf(child));//true
  • __defineGetter__ 定义获取的方法 (废弃)
  • __defineSetter__定义设置的方法 (废弃)
  • __lookupGetter__ 返回定义的获取方法 (废弃)
  • __lookupSetter__ 返回定义的设置方法 (废弃)
//getter和setter的设置和查看 (实际操作的对象和访问的对象不能是一个)
//get方法里面要返回数据(返回的数据就是显示的数据) set里面要进行相关设置
let obj1 = {name:'tom',age:18}
//存储对应的obj1的值
let model = {name:'tom',age:18}
//属性  相关处理方法
obj1.__defineGetter__('name',function(){
    console.log('get触发了');
    return model.name
})
obj1.__defineSetter__('name',function(v){
    console.log('set触发了');
    // console.log(arguments);
    model.name = v
})
console.log(obj1.name);
obj1.name = '你好'
console.log(obj1.name);
//查看get和set
console.log(obj1.__lookupGetter__('name'));
console.log(obj1.__lookupSetter__('name'));
  • propertylsEnumersble判断当前是否枚举
//propertyIsEnumerable 传入一个属性 判断这个属性的Enumerable属性的结果是什么
//Enumerable 可枚举 可以进行遍历
console.log(obj.propertyIsEnumerable('name'));
  • toLocaleSrting 根据本地格式转为字符串
  • toString转为字符串
Object.prototype.toString.call(需要查找的变量,(v)=>{这个v里面就有对应的类型})
  • valueOf提取对应的value值
//转字符串
console.log(obj.toLocaleString()); //本地格式
console.log(obj.toString());
//valueOf 返回对应的值
console.log(obj.valueOf());

原型属性

  • contructor指向当前的构造函数
  • __proto__对象隐式原型

静态方法(Object.方法调用的)

  • Object 判断两个对象是否是一个对象
Object.is(NaN,NaN)//true
  • assign 将后面的对象拷贝到第一个对象中(浅拷贝)
let obj = {arr:[1,2,3]}
//将后面对象的内容拷贝到前面这个对象里面返回前面这个对象
let obj1 = Object.assign(obj,{name:'jack'},{age:18})
console.log(obj);
console.log(obj1 == obj);//true
//对应的浅拷贝完成 (浅拷贝只拷贝第一层的值 第二层拷贝的都是地址)
let copyObj = Object.assign({},obj)
console.log(copyObj);
console.log(copyObj == obj);//false
copyObj.arr.push(4)
console.log(obj.arr);
  • create 根据传入的内容创建相关的对象
//create方法 
function Person(name){
    this.name = name
}
let person = new Person('tom')
//根据传入对象创建相关的对象 但是这个对象跟传入的对象不是一个对象
//它会将创建对象的原型指向传入对象
let createObj = Object.create(person)
console.log(createObj);
console.log(person == createObj);
console.log(person.isPrototypeOf(createObj));
手写实现Object.create
//手写实现create
function myCreate(o){
    //创建对象
    let newObj = new Object()
    //将传入o加到这个对象的原型上
    newObj.__proto__ = o
    return newObj
}
let myCreateObj = myCreate(person)
console.log(myCreateObj);
  • keys 将它的可枚举的key全部加入到一个迭代器返回
  • values 将它的value全部加入到一个迭代器返回
  • entries 将它的键值队全部加入到一个迭代器返回
//for in 遍历对象 for of 遍历对应的迭代器(必须要实现迭代器)
//keys  values  entries
let obj2 = {name:'jack',age:18,sex:'男'}
console.log(Object.keys(obj2));
console.log(Object.values(obj2));
console.log(Object.entries(obj2));
  • getPrototypeOf 获取原型
  • setPrototypeOf 设置原型
//setPrototypeOf 设置原型
function Child(name){
    this.name = name
}
let child = new Child('tom')
let proto = {address:'北京',city:"河北石家庄"}
Object.setPrototypeOf(child,proto)
console.log(child);
//getPrototypeOf 获取原型
let protoObj = Object.getPrototypeOf(child)
console.log(protoObj);
  • freeze 冻结(只能查询) isFrozen 是否冻结
  • seal 密封 (只能查询和修改 不能删除和添加新的属性)isSealed 是否密封
  • preventExtensions 不可扩展 (不能添加新的属性) isExtensible 是否可扩展
let obj = {
    name:'jack',
    age:18,
    sex:'男'
}
//冻结(只能查询 其他所有操作不能做) 密封(能查询和修改 不能删除和添加)  不可扩展(不能添加新的属性)
Object.freeze(obj) //冻结当前的obj对象
// Object.seal(obj)
// Object.preventExtensions(obj)
console.log(obj.name);//查
obj.age = 20 //改
delete obj.sex //删
obj.address = '长沙' //增
for(key in obj){
    console.log(key);
}
console.log(obj); //冻结 当前能查询 能修改 不能删除 不能添加新的内容 
//判断是否冻结 判断是否密封 判断是否可扩展
console.log( Object.isFrozen(obj));
console.log( Object.isSealed(obj));
console.log( Object.isExtensible(obj));

freeze > seal > preventExtensions (冻结必定密封 密封必定不可扩展)

  • getOwnPropertyNames 获取所有的属性名(除symbol的key以外)
  • getOwnPropertySymbols 获取所有symbol为键的属性名
  • getOwnPropertyDescriptor 获取对应属性的详情(属性对象)
  • getOwnPropertyDescriptors 获取所有属性的详情 (对象)
let symbol = Symbol()
let obj = {
    name:'jack',
    age:18
}
obj[symbol] = '你好'
//获取所有的属性名(除symbol的key以外) 传入的是一个对象 返回的是一个字符串数组
let names = Object.getOwnPropertyNames(obj)
console.log(names);
//获取所有的symbol修饰的属性(获取所有symbol为键的属性) 返回symbol数组
let symbols = Object.getOwnPropertySymbols(obj)
console.log(symbols);
//Descriptor getOwnPropertyDescriptor 获取属性的详情 返回是一个属性对象
let desc= Object.getOwnPropertyDescriptor(obj,'name')
console.log(desc);
//getOwnPropertyDescriptors 获取所有的属性详情 返回的是一个对象 每个的对象key是对应的属性名 值为属性对象
let descs = Object.getOwnPropertyDescriptors(obj)
console.log(descs);
  • defineProperty 定义单个属性
//defineProperty的基础使用
let obj = {
    name:"jack"
}
//基础的属性定义 传入对象 传入属性名  属性对象
Object.defineProperty(obj,'age',{
    configurable:false,//不写默认为false 是否可以删除
    enumerable:false, //是否可枚举 是否可以被遍历
    value:18,
    writable:false //是否可以写 修改
})
console.log(obj);
delete obj.age //configurable
//是否可以遍历 for in只能遍历可枚举的属性
for(key in obj){
    console.log(key);
}
obj.age = 30
console.log(obj);
属性属性(基础的属性定义)
configurable:false,
enumerable:false, 
value:18,
writable:false 
访问器属性(已经存在的属性进行访问)
configurable:false,
enumerable:false, 
get(){},//value
set(){}//writable
//进行浅拷贝 操作一个对象(实际操作对象)
let model = Object.assign({},obj)
//访问器属性是对于已经存在的时候进行访问的时候的相关属性
Object.defineProperty(obj,'name',{
    configurable:true,
    enumerable:true,
    get(){
        //在访问它的值的时候调用 它就替代了__defineGetter__
        //返回的值就是它对应获取的值
        console.log('调用了get');
        return model.name+'加工'
    },
    set(v){
        //设置值的时候调用 它就替代了__defineSetter__
        console.log('调用了set');
        model.name = v
    }
})
obj.name = 'tom' //调用set
console.log(obj.name);//调用get
  • defineProperties 定义多个属性
//定义多个属性
let obj1 = {}
let model1 = {}
Object.defineProperties(obj1,{
    username:{
        configurable:true,
        enumerable:true,
        get(){
            return model1.username
        },
        set(v){
            model1.username = v
        }
    },
    password:{
        configurable:true,
        enumerable:true,
        get(){
            return model1.password
        },
        set(v){
            model1.password = v
        }
    }
})
obj1.username = '张三'
obj1.password = '123456'
console.log(obj1);
console.log(model1);
总结
  • 原型方法重点 hasOwnProperty isPropertypeOf

  • Object是所有类的父类 所以他的原型方法及属性所有的类都拥有

  • Object的静态方法是为所有的对象服务的 里面的参数都是对象

  • defineProperty和观察者模式是vue2的底层 (实现vue2的双向数据绑定)

面试题
for in 和 Object.keys 和 Object.getOwnProertyNames的区别
  • for in 包含继承的属性及自身的属性 (不包含不可枚举的属性)

  • Object.keys 只包含自身的属性 (不包含继承的和不可枚举的属性)

  • Object.getOwnProertyNames 只包含自身的属性 (不包含继承的 包含不可枚举的)

  • 三个内容都不包含symbol的属性

二十七).Vue相关内容及深拷贝和浅拷贝

Vue相关内容


概述:

Vue是前端的一个js库(诞生于2015年 兴起于2016年 尤雨溪 (阿里巴巴)),vue是一个MVVM模式的框架。

MVVM概述

  • model 数据

  • view 视图

  • viewmodel 视图模型(管理 数据驱动视图)

Vue的双向数据绑定

双向数据绑定的概述
  • 视图变化---数据也会变化

  • 数据变化---视图也会变化

双向数据绑定运用表单标签的

代码实现(v-model指令)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./lib/vue.js"></script>
</head>
<body>
    <div id="app">
        <!-- 视图的渲染的地方 -->
        <input type="text" v-model="msg">
        <!-- 插值表达式 可以获取data里面的数据 -->
        {{msg}}
    </div>
    <script>
        //这个vue.js文件导出的是一个构造函数 Vue
        let vm = new Vue({
            el:'#app', //你需要将视图挂载到哪
            data:{ //放数据的
                msg:'你好'
            }
        })
        console.log(vm)
    </script>
</body>
</html>

原生实现vue2的双向数据绑定

流程
  • 获取所有的input框

  • 过滤哪个input有个属性叫v-model

  • 遍历所有加了v-model属性的input

  • 找这个v-model属性的属性值 对应data里面的数据值

  • 给对应的input添加监听 监听input的value变化

  • 设置给对应的data

  • 使用Object.defineProperty 的set来监听数据的变化

  • 在数据变化的时候 重新渲染对应的input的值

  • 渲染应该解析对应{{}} 找到对应的属性名 进行替换

代码实现
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <!-- 视图的渲染的地方 -->
        <input type="text" v-model="msg">
        <!-- 插值表达式 可以获取data里面的数据 -->
        {{msg}}
        {{hello}}
        {{hello}}
    </div>
    <script>
        class Vue{
            constructor(option){
                this.el = document.querySelector(option.el) //找到对应渲染的元素
                this.data = option.data
                //用于Object.defineProperty的使用
                this.model = Object.assign({},option.data)
                //拿到传入的元素的里面显示的所有的内容
                this.template = document.querySelector(option.el).innerHTML
                this.render()
                this. _obServer()
            }
            //观察者 观察data的数据变化
            _obServer(){
                let _this = this
                //遍历所有的属性
                for(let key in this.data){
                    Object.defineProperty(this.data,key,{
                        get(){
                            return _this.model[key]
                        },
                        set(v){
                            _this.model[key] = v
                            //渲染
                            _this.render()
                        }
                    })
                }
            }
            //渲染的方法
            render(){
                let _this = this
                //找到所有的{{}} 括起来的内容 进行替换
                this.el.innerHTML = this.template.replace(/\{\{\w+\}\}/g,(str)=>{
                    //str {{msg}}
                    let propertyName = str.substring(2,str.length-2).trim() //msg
                    return _this.data[propertyName]
                })
                //找到对应el里面的input框
                Array.from(this.el.querySelectorAll('input'))
                .filter((input)=>{
                    //找到input中是否具备v-model属性
                    return input.getAttribute('v-model')
                })
                .forEach((input)=>{
                    let _this = this
                    //遍历所有的input框 (都是有v-model属性的)
                    //获取对应的v-model的属性值
                    let propertyName = input.getAttribute('v-model').trim()
                    //给这个input添加onchange事件或者是oninput
                    input.oninput = function(){
                        //获取他的value 设置给对应的data里面的属性
                        _this.data[propertyName] = this.value
                    }
                    //将对应的input的value 进行设置(对应的data里面属性的数据)
                    input.value = this.data[propertyName]
                })
            }
        }
        //这个vue.js文件导出的是一个构造函数 Vue
        let vm = new Vue({
            el:'#app', //你需要将视图挂载到哪
            data:{ //放数据的
                msg:'你好',
                hello:'hello world'
            }
        })
    </script>
</body>
</html>

深拷贝和浅拷贝


浅拷贝

只拷贝第一层的值 其他拷贝是地址,相对于你拷贝一个拷贝快捷方式,这个新的快捷方式和原本的快捷方式不是一个,但是点进去的内容是一个。

Object.assign实现浅拷贝
let obj = {name:'jack',list:[1,2,3],params:{data:'ok'}}
let copyObj = Object.assign({},obj)
console.log(obj == copyObj)//false
console.log(obj.list == copyObj.list)//true 
Array.prototype.concat (数组的浅拷贝)
//数组的浅拷贝
let arr = [{
    username:'jack',
    password:'123'
},
           {
               username:'tom',
               password:'456'
           }]
//使用concat连接
let copyArr = [].concat(arr)
console.log(copyArr == arr)//false
console.log(copyArr[0] == arr[0])//true
Array.prototype.slice (数组的浅拷贝)
//使用数组的截取来实现浅拷贝
let copyArr1 = arr.slice()
console.log(copyArr1 == arr)//false
console.log(copyArr1[0] == arr[0])//true
扩展运算符 ...
//使用扩展运算符
let copyArr2 = [...arr]
console.log(copyArr2 == arr);//false
console.log(copyArr2[0] == arr[0])//true
自定义函数实现浅拷贝
//函数实现浅拷贝
function copy(obj){
    //创建一个新的对象
    let constructor = obj.constructor
    let copyObj = new constructor()
    for(let key in obj){
        copyObj[key] = obj[key]
    }
    return copyObj
}
let copyArr3 = copy(arr)
console.log(copyArr3 == arr);//false
console.log(copyArr3[0] == arr[0])//true
let copyObj1 = copy(obj)
console.log(obj == copyObj1)//false
console.log(obj.list == copyObj1.list)//true
第三方工具(lodash.js) _.clone
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
<script>
    //浅拷贝 拷贝的对象 返回一个新的对象
    let obj2 = {name:'jack',list:[1,2,3],params:{data:'ok'}}
    let copyObj3 = _.clone(obj2)
    console.log(obj2 == copyObj3)//false
    console.log(obj2.list == copyObj3.list)//true
</script>

深拷贝

概述:

深拷贝是拷贝所有的内容,这个俩个内容相同点只在于里面的值是相同的,其他引用地址都不一样。相当于你用u盘拷贝一份资料,那么这份资料跟拷贝那么那份资料是没有任何关系,但是里面显示的内容是一样的。

序列化和反序列化 (JSON.stringify JSON.parse)
//使用序列化和反序列化
let obj = {
    list:[{
        name:"苹果"
    },
          {
              name:"香蕉"
          }]
}
//进行深拷贝
let copyObj = JSON.parse(JSON.stringify(obj))
console.log(copyObj == obj);//false
console.log(copyObj.list == obj.list);//false
console.log(copyObj.list[0] == obj.list[0]);//false
使用第三方工具 lodash.js (_.cloneDeep)
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
<script>
    let copyObj1 = _.cloneDeep(obj)
    console.log(copyObj1 == obj);//false
    console.log(copyObj1.list == obj.list);//false
    console.log(copyObj1.list[0] == obj.list[0]);//false
</script>
自定义函数实现深拷贝(递归实现)
//自定义函数实现深拷贝(递归)
function deepClone(obj) {
    //判断当前你传入的参数是否是一个对象
    if (!(typeof obj == 'object' && obj)) {
        return obj
    }
    //如果他是数组 我就赋值为数组 如果是对象就赋值为
    let copyObj = obj instanceof Array ? [] : {}
    //遍历传入的对象
    for (let key in obj) {
        //将对应的内容进行赋值
        copyObj[key] = deepClone(obj[key])
    }
    return copyObj
}
let copyObj2 = deepClone(obj)
console.log(copyObj2);
console.log(copyObj2 == obj);//false
console.log(copyObj2.list == obj.list);//false
console.log(copyObj2.list[0] == obj.list[0]);//false

Vue原理介绍


虚拟dom

虚拟dom顾名思义就是虚拟的dom对象(对象),这个虚拟的dom对象跟实体的dom对象没有直接的关系。如果直接操作实体dom会造成大量的重绘和回流(页面渲染次数增加 渲染速度就慢)。所以为了解决这个问题,vue就是先操作对应的虚拟dom,再通过虚拟dom渲染实体dom(只有一次)。虚拟dom存在在内存中的,虚拟dom的形成是抽取对应的实体dom(模仿实体dom创建的对象)。

diff算法(脏检查器)

diff算法是用于比对的算法,他是比对虚拟dom,比对虚拟dom的内容的改变,通过虚拟dom比对的内容变化来控制实体内容变化(用于性能优化的)。diff算法是通过对应的节点来进行比对的(元素节点,属性节点,文本节点 vnode)。

节点比对流程

节点比对流程先比key(下标不能作为key(下标会变)),比对对应的节点名,比对属性名,比对子节点。patch的方法来帮你进行分类比较,比较完进行渲染(模板引擎 模板引擎mustache)。

相关优化

生命周期优化(预加载(挂载的时候)、缓存(组件销毁的时候)、回收资源(组件销毁的时候))

打包优化(webpack/glup(文件流))

二十八).SPA路由实现及SASS讲解


路由


前端路由

根据对应路由地址渲染不同的内容

前端的分类
页面路由(刷新)
  • 根据对应的地址访问不同的页面(location.href location.assign location.repalce)

hash路由 (不会刷新)
  • 根据对应的hash地址来渲染不同的内容(onhashchange)

  • location.hash 来获取对应的hash值 通过onhashchange进行监听

history路由 (不会刷新)
  • 根据对应的history页面的地址来渲染不同的内容(onpopstate)

  • 通过replaceState和pushState来改变state的值和页面的地址

  • 通过history.back history.go history.forward来触发对应的onpopstate事件

后端路由

根据对应的路由地址访问对应的接口

SPA

单页应用程序 (single page application),整一个应用只有一个页面,那么对应的页面调整就没有意义了,所以对应的SPA的路由实现就主要是hash模式和history模式。在后续的vue或者是对应的react中,他主要做的是SPA的应用那么对应的主要采用的模式hash和history,hash的监听能直接触发而history的监听不能直接触发,所以默认的模式就是hash模式。

Vue中的SPA路由

<!DOCTYPE html>

<htmllang="en">

<head>

<metacharset="UTF-8">

<metahttp-equiv="X-UA-Compatible"content="IE=edge">

<metaname="viewport"content="width=device-width, initial-scale=1.0">

<title>Document</title>

<scriptsrc="./lib/vue.min.js"></script>

<scriptsrc="./lib/vue-router.js"></script>

</head>

<body>

<divid="app">

<!-- 路由链接 to指定的地址 router-link会解析成a标签-->

<router-linkto="/">去首页</router-link>

<router-linkto="/user">去用户页</router-link>

<!-- 路由视图 显示的视图 router-view会解析你对应需要渲染的内容-->

<router-view></router-view>

</div>

<script>

//组件 渲染的内容

lethome=Vue.component('home',{

template:'<div>首页</div>'

} )

//渲染的内容

letuser=Vue.component('user',{

template:'<div>用户页</div>'

} )

//路由对象

letrouter=newVueRouter({

//路由配置 router 名词(路由对象) route 动词(路由配置) routes 多个(路由配置)

routes:[

//route规则

{

name:'home',//名字

path:'/', //路由地址

component:home//组件 渲染什么

},

{

name:'user',

path:'/user',

component:user

}

]

});

newVue({

el:'#app',

//传入路由配置 router

router

})

</script>

</body>

</html>

hash模式路由实现

根据对应hash值的变化来控制对应渲染内容的变化(onhashchange)

classVueRouter {

constructor(option) {

//获取传入的配置

this.routes=option['routes']

this.handlerLoad()

}

//处理hash改变的监听 需要渲染的元素

obServer(element) {

this.el=element

window.οnhashchange= () => {

this.render()

}

}

//渲染的方法

render() {

let_this=this

//获取当前hash值 去除#

lethash=location.hash.slice(1)

//去this.routes比较

this.routes.forEach(route=> {

if (hash==route.path) {

_this.el.querySelector('router-view').innerHTML=route.component.template

}

});

}

//a标签变化

handlerLink() {

let_this=this

//获取所有的router-link

letlinkList=this.el.querySelectorAll('router-link')

Array.from(linkList).forEach(link=> {

//找到对应的to属性

letpath=link.getAttribute('to')

//创建a标签 将他的to地址赋值href属性

leta=document.createElement('a')

a.href="#"+path

a.innerHTML=link.innerHTML

_this.el.replaceChild(a, link)

})

}

//在打开的时候前面自动+#/

handlerLoad(){

window.οnlοad= ()=>{

location.hash='/'

}

}

}

classVue {

constructor(option) {

this.el=document.querySelector(option.el)

this.router=option.router

this.router.obServer(this.el)

//第一次渲染

this.router.render()

this.router.handlerLink()

}

}

history模式

核心点 onpopstate 路径变化(location.pathname)

classVueRouter {

constructor(option) {

//获取传入的路由配置

this.routes=option.routes

}

obServer(element) {

//需要挂载的el对象

this.el=element

//监听onpopstate事件

window.onpopstate= () => {

//读取path根据路径匹配渲染不同的内容

this.render(location.pathname)

}

}

render(path) {

let_this=this

//匹配

// 遍历路由配置

this.routes.forEach(route=> {

//判断对应的path地址是否等于route的path地址

if (path==route.path) {

//渲染

letview=_this.el.querySelector('router-view')

view.innerHTML=route.component.template

}

});

}

handlerLoad() {

window.οnlοad= () => {

//替换当前路径

history.replaceState('', '', './')

//第一次渲染

this.render('/')

}

}

hanlderLink() {

let_this=this

letlist= []

//获取所有的router-link

letlinkList=this.el.querySelectorAll('router-link')

Array.from(linkList).forEach(link=> {

//找到对应的to属性

letpath=link.getAttribute('to')

//创建a标签 将他的to地址赋值href属性

leta=document.createElement('a')

a.href=path

a.innerHTML=link.innerHTML

_this.el.replaceChild(a, link)

list.push(a)

})

//给a添加点击事件

//获取所有的a list

list.forEach(a=> {

a.addEventListener('click', function (e) {

e=e||window.event

//禁止a默认行为

e.preventDefault();

history.pushState('', '', a.href)

//渲染

_this.render(location.pathname)

})

})

}

}

classVue {

constructor(option) {

this.el=document.querySelector(option.el)

this.router=option.router

//监听传入当前的el元素

this.router.obServer(this.el)

this.router.handlerLoad()

this.router.hanlderLink()

}

}

Sass


概述

sass是一个预编译的css,核心还是css(css语法它都支持),他最终还是会编译成css,sass的好处在于它可以以js的方式书写css(有跟js原生一样的语法)。跟他同类的预编译的css还有less以及stylus等。sass它是使用ruby语言书写的,后续使用的python所以sass必须具备python环境。sass它和less是一类的产品,但是俩种的书写语言不一样。

官方文档

使用方式

使用node来进行sass的编译

npm i sass -D

sass sass文件名 文件名(css文件名)

使用第三方插件来进行编译
vscode的第三方插件 easy sass 、 sass 、live sass

easy sass的扩展配置

sass的俩种扩展名

  • .sass 以缩进作为区分 (里面是没有;没有{})

div

color:red

p

font-size:13px

  • .scss 跟正常的css一样

div{

background-color: red;

}

sass的相关应用

变量定义 ($变量名:值) *

$color:red;

div{

background-color:$color

}

编译

div {

background-color: red;

}

支持运算符(+ - * / %) *

$size:12px;

p{

font-size: $size%100;

width: $size*10-100;

height: $size+100;

}

编译

p {

font-size: 12px;

width: 20px;

height: 112px;

}

样式关系 *

span,img{

padding: 10px;

}

.box{

background-color: red;

img{

width: 100px;

h1{

height: 100px;

}

}

}

#content{

color: yellow;

&:hover{

color: green;

}

.box{

font-size: 12px;

&:hover{

font-size: 18px;

}

}

}

编译

.box {

background-color: red;

}

.boximg {

width: 100px;

}

.boximgh1 {

height: 100px;

}

#content {

color: yellow;

}

#content:hover {

color: green;

}

#content.box {

font-size: 12px;

}

#content.box:hover {

font-size: 18px;

}

插值表达式

#{变量名} 获取对应的变量值

if判断

$number:10;

.item{

@if $number>10 {

color: red;

}

@else{

color: green;

}

}

编译

.item {

color: green;

}

for循环 (while循环)

@for $i from 0 to 3 {

.item_#{$i}{

size: $i;

}

}

编译

.item_0 {

size: 0;

}

.item_1 {

size: 1;

}

.item_2 {

size: 2;

}

each (for each遍历)

$list:1,2,3,4;

@each$itemin$list {

.item_list_#{$item}{

width: $item;

}

}

函数

@functionf($arg:20px){

@return$arg+10

}

div{

height: f(10px);

width: f();

}

编译

div {

height: 20px;

width: 30px;

}

混入器 *

@mixina{

background:red;

}

@mixinborder($size,$style,$color){

border: $size$style$color;

}

@mixinshadow($offsetleft:10px,$offsettop:20px,$width:30px,$color:green){

box-shadow: $offsetleft$offsettop$width$color;

}

div{

font-size: 18px;

@includea();

@includeborder(1px,solid,red);

@includeshadow()

}

编译

div {

font-size: 18px;

background: red;

border: 1pxsolidred;

box-shadow: 10px20px30pxgreen;

}

导入(模块化思想)*

@import'./test.scss';

div{

@includeshadow()

}

注释

//我是注释 不会显示

/*我是注释 会显示*/

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值