Javascript学习随笔

想要运行js需要运行环境:

  1. 浏览器自带js解释器
  2. node.js需要安装环境

编译型:在程序正式运行之前,会检查有没有报错,如果有报错直接不运行,比如Java、c++、c#...(严格的)

解释型:在程序正式运行之前,不会检查有没有报错,直接运行,碰到错误后就会停止,比如:javascript、node.js、php(自由的)

弱类型:在创建一个变量时,想放什么数据类型的数据就放什么(非常自由)

强类型:在创建变量之前,必须先规定,此变量可以保存什么数据类型,才能往里面放入对应的数据类型的数据(严格)

面向对象(万物皆对象):一切对象都有两个对象(属性和方法)

​ 对象名.属性名

​ 对象名.方法名()

一切一阶段做不到的事,都靠二阶段 原生js(轮播图,购买商品数量的加减....)

<!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>新的一天:加油哦!</title>
    <script type="text/javascript" src="../js/one.js"></script>
</head>


<body>



    <script type="text/javascript">

    </script>
</body>

</html>

目录

引入方式

  1. 直接在html上写一个script的双标签,里面书写js代码 - 临时测试玩法
  2. 在外部创建有个js文件,在其中写入js代码,html进行引入 - 正式开发玩法
    <script src="路径">//此处不可以在写代码</script>

打桩输出

作用:检测代码是否有问题,找错,找bug

  1. 在控制台输出:console.log(想要输出的对象) //console控制台 log日志
  2. 在页面上输出:document.write(想要输出的东西) //document文档 write写入 能够识别标签;但是是个垃圾,如果搭配上点击事件,会替换原页面的内容。
  3. 弹出一个小框:alert(想要输出的内容) //垃圾:卡住html页面,用户不关掉,就只能看白板
  4. 在页面上弹出提示和用户输入框:prompt(提示文字) 卡住html页面,用户不关掉,就只能看白板
  5. 确认框confirm() 返回true和false

注释

作用:方便后期维护

  • 单行注释 //
  • 多行注释 /*内容*/

变量和常量

硬盘:外部存储器:外存,保存文件/数据

CPU:中央处理器,计算速度

内存:内部存储器,保存的是应用在执行过程中,所需要的一些数据(变量就是一个内存),我们取的变量名就是一个内存地址

变量var

可以保存数据,如果数据繁琐,以后可以使用变量名,相当于就是在使用变量的值;变量顾名思义,值以后可以改变。

  • 语法:var 变量名=变量值;

  • 特殊:

    • 变量名不是随意的
      • 不能数字开头,可以数字结尾
      • 见名知意,驼峰命名
      • name是一个关键字,不管你保存的数据类型是什么,最后都会悄悄地变成一个字符串
      • 千万不要拿着关键字当变量名
    • 可以不保存数据,会有一个默认值undefined,undefined是个垃圾,拿来干什么都要不得 - 非常不推荐大家创建变量不赋值
    • 创建多个变量时可以简写,var只写一次,每个变量之间使用,进行分割

常量const

一旦创建,值不允许修改

  • 语法:const 常量名=值;

  • 现实生活中哪些是常量?但是常量其实很少,代码中也会很少使用
    1、一周7天
    2、pi - 3.1415926....
    3、一年12个月
    4、地球的公转速度/自转速度
    5、一年365/366天

数据类型

  • 原始/基础/值类型:5个

    1. number 数字类型,取值有无数个

    2. string 字符串类型,取值有无数个,但是需要加上""或''括起来

    3. boolean 布尔类型,取值只有两个:true(真/对) 或 false(假/错),一般用于做比较判断是才会出现

    4. null 空,释放你不再需要的内存/变量,节约内存空间

    5. undifined 拿来干什么都要不得,这是我们祖师爷犯下的一个错误

  • ***引用/对象类型:11个
    *String Number Boolean -> 为什么这三个人即使原始类型又是引用类型?原因是他们三个具有包装类型?
    *Array *Function Date(日期) Math(数学) *RegExp(正则->表单的验证)
    Error(错误)
    *Object(面向对象开发方式)
    Global(全局对象:保存着全局变量和全局函数) -> 特殊:浏览器中没有global,global被window给代替了,只不过window可以省略不写
    只有JavaScript中global才被代替为了window,而且node.js后端语言中!全局对象就叫global。

      - ***面试题:什么是包装类型?
    
      	  		包装类型:专门将原始类型的值封装为一个引用类型的对象
      	    		    为什么:原始类型的值原本是没有任何属性和方法的,意味着原始类型本身就是不支持.做操作的
      	    			  但是前辈们发现字符串经常会被我们程序员所使用/操作
      	    			  为了方便我们程序员为这三个人提供了包装类型,从值->对象(提供了很多属性和方法)
      	    		何时使用:只要你试图用原始类型的变量去调用属性或方法时,自动包装
      	    		何时释放:方法调用完毕后,包装类型就会自动释放,又变回了一个原始类型的值
    
    
        				为什么undefinednull不能使用. - 没有包装类型
    

数据类型转换

js获取到的页面上的一切东西,一定都是字符串

如果你想要查看数据类型:typeof(查看的东西);

隐式转换

  • 算数运算符的隐式转换(默认:悄悄的将左右两边的东西,转为一个数字,再运算)

    1. +运算,碰上一个字符串,两边都会悄悄的变成一个字符串,+运算,不再是+运算,而是一个拼接操作

    2. 别的数据类型也可以转为数字
      true->1
      false->0
      null->0
      undefined->NaN //只要是转为数字,但是不知道该转为什么数字,结果一定为NaN

    3. 其实- * / %字符串也可以转为数字,前提要是一个纯数字组成的字符才可以,但凡包含了一个非数字字符则为NaN
      "100"->100
      "100px"->NaN

    4. 、NaN:Not A Number,不是一个数字,但是确实是数字类型,不是一个有效数字
      没得优点,但是有2个缺点:
      1、NaN参与任何算术运算,结果仍为NaN
      2、NaN参与任何比较运算,结果永远为false,带来了一个问题:我们没有办法使用普通的比较运算来判断该数据是不是NaN,NaN连自己都不认识自己
      解决:!isNaN(x);
      true->是一个有效数字
      false->是一个NaN

    5. 学完隐式转换,依然没解决一个事(给有单位的变量进行算数运算)
      "100px"*2=NaN
      "100px"+100="100px100"

      ​ 所以接下来学习强制转换 ↓

显示/强制转换

隐式转换出来的结果不是我们想要的,我们就可以手动调用一些方法,强制转为我们需要的数据

  • 转字符串:页面上一切的数据默认都是字符串
  1. var str=x.toString();//x不能是undefined和null。undefined和null不能使用.去访问任何东西
    2. String(其他元素类型)可以将任何元素类型转换为字符串。完全相当于隐式转换,还不如直接用+
  • 转数字:3种

    1. *parseInt(str/num); parse->解析 Int->整型
      原理:专门为字符串和小数转为整数数字准备的,从左向右依次读取每个字符,碰到非数字字符就停止转换,
      如果一来就碰到了不认识的字符,则为NaN。

      ​ !!!不认识小数点!!!

      					console.log(parseInt(100.5));//100
      					console.log(parseInt("100.5"));//100
      					console.log(parseInt("100px"));//100
      					console.log(parseInt("px100"));//NaN
      					console.log(parseInt("1px00"));//1
      					console.log(parseInt(true));//NaN
      					console.log(parseInt(false));//NaN
      					console.log(parseInt(undefined));//NaN
      					console.log(parseInt(null));//NaN
      
    2. 、*parseFloat(str); parse->解析 Float->浮点型,小数
      原理:几乎和parseInt一致,但是认识第一个小数点

       					console.log(parseFloat(100.5))//100.5
       					console.log(parseFloat("100.5"))//100.5
       				console.log(parseFloat("100.5px"))//100.5
       					console.log(parseFloat("px100.5"))//NaN
       					console.log(parseFloat("10px0.5"))//10
       				console.log(parseFloat("10.5.5.5"))//10.5
       					console.log(parseFloat(".555"))//0.555
    
    1. Number(any);//此方法事万能的,任何人都可以转为数字
      //垃圾:完全等效于隐式转换,还不如x-0 *1 /1 %1
  • 转布尔:

    • Boolean(x);任何数据都可以转为布尔
      • 返回值大部分都为true,只有6个(0,空字符串,null,undefined,NaN,false)为false。
      • 在分支,循环条件中,判断其会不会执行
      • 用逻辑运算符非进行隐式转换
        • !!null ---->false

运算符

赋值运算符:

  • = -= += /= %=
  • i++和++i的区别:
    • 单独使用没有区别
    • 参与表达式使用,变量中的值都会+1
      • ++前置 返回的是加了后的新值
      • 后置++ 返回的是加之前的旧值

算数运算符: +-*/%

  • 特殊:1、%:取余,俗称模,两个数相除,不去商,而且除不尽的余数
    5%2 -> 1
    3%5 -> 3
    作用:
    1、最大的作用就是判断奇偶性,num%2,如果等于0说明是偶数,如果等于1说明是奇数
    2、获取某个数字的后几位
    console.log(1234%10);//4
    console.log(1234%100);//34
    console.log(1234%1000);//234
    3、控制一个数字永远不超过某个数字
    num%3 -> 结果永远不会超过3 (1%3=1 2%3=2 3%3=0)
    2、*算数运算都具有隐式转换:悄悄地都会转为数字,再运算
    特殊:+运算,只要碰上一个字符串,则悄悄的两边都会转为字符串,+运算不再是+运算,变成了一个拼接操作
  • 计算机很笨,虽然记忆能力很强(机械硬盘(永久保存)/固态硬盘(有效期10年)):计算机带有摄入误差,解决:num.toFixed(d);//d代表保留的小数位数,会四舍五入的功能
    缺陷:搭配上一个parseFloat()使用最好,返回的结果是一个字符串

比较运算符:> < >= <= == === != !==

  • 结果为布尔值

  • 隐式转换:默认,转换为数字,再比较大小(出口全等和非全等)

  • 如果参与比较的左右两边都是字符串,则按位PK每个字符的十六进制unicode号(十进制ascii码)
    0-9<A-Z<a-z<汉字

    ​ 48<=数字<=57 65<=大写字母<=90 97<=小写字母<=122 19968<=汉字<=40869

  • undefinednull;//true;
    区分:undefined
    =null;//false

    ​ === !== 不带隐式转换的等值比较和不等比较,要求数值相同,并且数据类型也要相同

    function String(x){
    				if(x===null){
    					return "null";
    				}else if(x===undefined){
    					return "undefined";
    				}else{
    					return x.toString();
    				}
    //			}
    

逻辑运算符:

  • &&:与,并且。要求全部条件都满足,结果为true,只要一个条件不满足就会false。

    ​ 全部为true,才为true
    ​ 一个false,则为false

  • ||:或者,要求全部条件都满足,结果为false,只要一个条件满足就true

    ​ 全部为false,才为false
    ​ 一个true,则为true

  • ! :颠倒布尔值

  • 短路逻辑:如果前一个条件,已经能够得出最终结论了,则不看后续

    • &&短路:如果前一个条件满足,则后一个操作执行,如果前一个条件不满足,则后一个操作不执行

      目的:简化【简单的】if分支:1、一个条件一件事,满足就做不满足就不做 2、操作只能有一句话
      简化:条件&&(操作);
      对比:(大于500,打八折)

    ​ 以前:if(total>=500){total=0.8}
    现在:total>=500&&(total
    =0.8) 因为&&的优先级比=的优先级高,因此加个小括号

    • ||短路:如果前一个为true,后一个不看 - 实现浏览器的兼容性,二选一操作
      e=e||window.event;

三目运算

目的:简化分支

  • if(){}else{}------> 简化为:条件?执行代码1:执行代码2;
  • if(){}else if()else{}...------> 简化为:条件?执行代码1:条件?执行代码2:执行代码3;

位运算

左移:m<<n 表示m左移了n位,翻译:m*2的n次方

- 3的4次方----》3<<2------->`(3*2)*2`

右移:m>>n 表示m右移了n位,翻译:m/2的n次方

底数只能为2

ascii码

  • 获取字符串中第一个字符的ascii码:str.charCodeAt(0);
    数字:48-57
    大写字母:65-90
    小写字母:97-122
    汉字:19968(4e00)-40869(9fa5)

符号的优先级

由高往低排序:

  1. ()
  2. ipt.value
  3. ++ --
  4. 逻辑非

分支语句

if()else{}分支

1、只有条件满足是才会执行操作,一个条件一件事,满足就做不满足就不做
if(条件){
操作
}

2、条件满足是才会执行操作,条件不满足则执行默认操作,一个条件两件事,满足就做第一件,不满足就做第二件
if(条件){
操作
}else{
默认操作
}

3、多个条件多件事,满足谁就做谁
if(条件1){
操作1
}else if(条件2){
操作2
}else{
默认操作
}

注意:1、第三种写法:else if可以有多个
2、分支只要走了一条路,就不会再看其他路了
3、else其实可以省略不写,但是不推荐,如果条件都不满足,则什么都不会执行

switch...case分支

  • 语法:
    switch(变量/表达式){
    case 值1:
    操作1;
    case 值2:
    操作2;
    default:
    默认操作
    }
  • 特殊:
    1. case 的比较不带隐式转换
    2. 问题:默认只要一个case满足后,会将后面所有的操作全部做完
      解决:break;
      建议:每一个case的操作后都跟上一个break
      有的地方也可以不加break:
      1. 最后一个操作default可以省略break
      2. 如果中间多个操作做的事儿是一样,可以省略中间部分
    3. default可以省略不写,但如果条件都不满足,则什么都不会执行
  • 面试题:if VS switch ?
    • switch...case...,缺点:必须要知道最后的结果是什么才可以使用,在case中不能做范围判断,只能做等值判断
      优点:执行效率相对较高
    • if:缺点:执行效率相对较低
      优点:可以随意的做范围判断
    • 建议:代码优化时,要尽量的将所有的 if...else... 换成 switch...case...

循环

while循环

  • 语法:
    var 循环变量=几;
    while(循环条件){
    循环体;
    循环变量变化起来
    }
  • 执行原理:首先创建了循环变量,判断循环条件,如果条件满足,则执行一次循环体,并不会结束循环,会回过头,继续判断循环条件,满足的话,则再做一次
    ......
    直到循环条件不满足false时,才会退出循环
    宏观上看循环感觉一瞬间就结束了,但是微观上看其实是一次一次执行的
  • 特殊: 死循环
    • 有的时候真有可能需要使用死循环:永远不会停下来的循环
      何时使用:不确定循环次数的时候
      while(true){
      循环体
      }
  • 退出循环语句:
    • break - 只能用在循环中,多半都是搭配死循环使用的;
    • continue 退出当前次循环。

do while循环

  • 语法:
    ​ var 变量=几;
    ​ do{
    循环体;
    变量的变化
    }while(循环条件)

  • 面试题:while和do...while的区别?
    只看第一次,如果条件都满足,则没区别
    如果条件不满足,则while一次都不会执行,do...while至少会执行一次

for循环

  • *for循环:和while原理是一样的,但是他比while看上去更加的整洁,简单,舒服

    • 语法:
      for(var 循环变量=几;循环条件;循环变量变化起来){
      循环体;
      }
    • 死循环:
      for(;😉{
      循环体
      }
    • 面试题:for 和 while的区别?
      几乎没区别:
      一般来说我们不确定循环次数的时候,会使用while循环 - 死循环
      一般来说我们确定循环次数的时候,会使用for循环 - 大部分情况
  • 猜数字小游戏

    • 随机数 var rodom = parseInt(Math.random() * (max - min + 1) + min);
       function getrodom() {
                var rodom = parseInt(Math.random() * (100 - 1 + 1) + 1);
                var hp = null;
                var user = null  //难度
                for (; ;) {
                    user = prompt('请选择难度 1简单,2普通,3困难')
                    if (user > 0 && user <= 3 && user != "") {
                        break;
                    } else {
                        alert("请重新选择难度")
                    }
                }
    
    
                if (user == 1) {
                    hp = 10
                } else if (user == 2) {
                    hp = 5
                } else if (user == 3) {
                    hp = 3
                }
                // } else {
                //     hp=1
                //     alert('开启地狱模式')
                // }
    
    
                while (true) {
                    if (hp > 0) {
                        var shuru = parseInt(prompt('0-100猜数字,猜猜我是几,剩余次数' + hp))
                        if (!isNaN(shuru) && shuru !== "")
                            if (shuru > rodom) {
                                hp--;
                                alert('大了,剩余生命' + hp);
    
                            } else if (shuru < rodom) {
                                hp--;
                                alert('小了,剩余生命' + hp)
    
                            } else {
                                alert('对了')
                                break;
                            } else {
                            hp--
                            alert("恶意输入,生命值-1,剩余次数" + hp)
                        }
    
                    } else {
                        alert('你的生命值不足,请重新开始游戏,本轮游戏正确答案为' + rodom);
                        break;
                    }
                }
            }
    

function函数对象

函数,也称之为方法,需要【预定义】好的,以后就可以【反复使用】的【代码段】

创建与使用函数

  1. 创建/定义/封装函数

    1. 声明方式:function 函数名(){

      若干代码

      return 返回值;

      };

    2. 直接量方式:var 函数名=function(形参列表){//通过此处看出函数名其实也是一个变量名,衍生出,elem.on事件名这一坨其实就是函数名,无非涨得长了点
      函数体;
      return 返回值;
      }

    3. *构造函数:var 函数名=new Function("形参1","形参2",.....,"函数体,return返回值");
      何时:如果你的函数体不是固定的,而是动态拼接的(函数体现在毕竟是一个字符串)

    4. return的本意,退出函数的意思
      只不过如果return后面跟着一个数据,会顺便将其返回到全局作用域之中,但是只负责返回不负责保存!
      所以再调用函数时,如果有return,记得拿一个变量接住他
      调用函数:var result=函数名(实参列表)

  2. 使用/调用函数

​ 函数名()

​ 注意: 1、调用几次,就会执行几次
​ 2、交给用户来触发,只需要在某个元素上绑定点击事件:
<div onclick="函数名()">内容</div>

  1. 何时使用:
    1、不希望打开页面立刻执行,而在需要时调用,或者由用户来触发
    2、希望能够反复执行,不需要刷新页面
    3、以后任何一个独立的功能体,都要单独封装在一个函数之中(你做的每一个作业)
    4、函数在js中的地位最高:第一等公民地位,以后要有封装的思想,!!!函数里面的变量会自动释放

带参数的函数:

何时使用:如果我的函数体,希望根据传入的实参的不同,做的略微不同

  • 创建:形参:形式参数,其实就是一个变量名,但是不需要写var,而且默认保存的也是undefined
    function 函数名(形参,形参,...){
    若干操作/代码段;
    }

  • 调用:实参:实际参数,真正的值,需要在你调用函数时从外部传入进去
    函数名(实参,实参,...)

  • 特殊:1、传参的顺序一定要一一对应上,并且数量也要对应
    2、不是一定要带参数的函数,才是好函数,具体要不要带参数,要看我们的需求
    如果你的函数体事固定的 - 则普通函数就够了
    如果你的函数体是根据参数的不同,做的略微不同 - 则带参数的函数完成

作用域:2种

  1. 全局作用域:成员:全局变量和全局函数,在任何位置都可以访问|使用.

  2. 函数/局部作用域:成员:局部变量和局部函数,只能在【函数调用时内部可用】

    带来了变量得使用规则:优先使用局部的,局部没有找全局,全局没有就报错
    特殊:
    1、千万不要对着未声明的变量直接赋值:a=1; - 会导致全局污染,全局本来没有,突然就被添加上了一坨内存,建议任何变量使用之前一定要先var
    2、儿子不孝啊,局部的东西全局居然不能用,解决:return 返回值;
    3、哪怕没有写return,其实也会悄悄给你return一个undefined,默认
    4、return一般只会出现再函数的最后,而且只能出现一次
    5、其实前辈们提供给我们的方法,大部分底层都有return操作,因为前辈们考虑到了,调用了他这个方法过后得到的结果可能对我们以后有用

声明提前

​ 在程序正式执行之前
​ 会将var声明的变量和function声明的函数
​ 集中提前到当前作用域的顶部,但是赋值留在原地
​ 变量比函数轻

	我们写代码绝对不会碰到,只要遵守规则:1、先创建后使用 2、变量名和函数名尽量的不要重复
	只会在鄙视中碰到:如果以后你碰到先使用后创建,多半都是在靠你声明提前
	
	面试题:

按值传递(浅拷贝)

  • 如果传递的是【原始类型的值】
    修改一个变量,另一个变量是不会受到影响的,其实是复制了一个【副本】给对方
  • 如果传递的是【引用类型的对象】:Array、Function
    修改一个变量,另一个变量是会受到影响的,因为两者使用的是【同一个地址值】 - 浅拷贝:好像是复制了,但是复制的不全
//深浅拷贝的区别:
// 			var arr1=[1,2,3,4];
// 			// var arr2=arr1   //浅拷贝, 这时改变arr2;arr1也会改变
// 			var arr2=arr1.slice();  //深拷贝
// 			arr2.length--;
			
// 			console.log(arr1);
// 			console.log(arr2);
// // -----------------------------------------------------
// 			var obj1={a:1,b:2};
// 			// var obj2=abj1  ////浅拷贝, 这时改变obj2;obj1也会改变
// 			var obj2={...obj1};  //深拷贝
			
// 			obj2.a=100;
			
// 			console.log(obj1);
// 			console.log(obj2);	

重载

相同的函数名,传入不同的实参,可以自动选择对应的函数执行

  • 问题:JS的语法不支持重载的!
    JS不允许多个同名函数同时存在,如果同时存在,最后的会覆盖之前所有的
  • 解决:在【函数中】有一个对象 - arguments对象
    • 什么是arguments:自动创建的,是一个类数组对象(都有下标,都有length,都可以遍历),不是数组!
    • 作用:接受住所有的实参(以后有没有形参无所谓了,变相的实现了重载,可以在函数内部判断arguments的不同,执行不同的操作)
  • 正式开发中,开发结束后,需要做代码的优化,可能就会将多个函数整合为一个函数
function chongzai(){
            if(arguments.length==2){    ///////可以判断它的长度,也可以判断数据类型
                return arguments[0]+arguments[1]
            }else  if (arguments.length == 1) {
                return arguments[0]**2
            } else  if (arguments.length == 3) {
                return Math.max(arguments[0],arguments[1],arguments[2])
            }
        }
       
        console.log( chongzai(4));
        console.log( chongzai(4,4));
        console.log(chongzai(4,5,6));

匿名函数

没有名字的函数,理论上来说我们无法调用

  1. 自调:函数自己调用自己

    • 特点:自调的代码只能执行一次 === 全局代码(为什么:节约内存,因为匿名函数,没有变量引用着,用完后,就会立刻释放)

    • 语法:
      (function(){
      //代替全局代码
      })();

  2. 回调:将函数作为了实参,传递给其他函数使用

    • 学习回调的目的:让你们知道哪些叫回调,匿名函数,只要不是自调,就是回调
      arr.sort(function(a,b){return a-b})
      str.replace(reg,function(){})
      btn.onclick=function(){}

    • 以后ES6有一个技术:箭头函数:简化一切的【回调函数】

      • 箭头函数口诀:省略function,(形参为1个时,可省略括号,形参没有时,保留括号),return后面为一句话时省略return和{}
       var arr = [1,5,6,8,9,0]
              arr.sort(function (a, b) { return a - b })  //省略function,形参为1个时,可省略括号,return后面为一句话时省略return和{}
              arr.sort((a, b)=>a - b)
              console.log(arr.sort(function (a, b) { return a - b }));
              console.log(arr.sort((a, b) => a - b));
      
    • 了解了以下回调函数的原理 - 其实不是没调,只是我们没调用,前辈们早就帮我们调用好了

预定义全局函数:

前辈们提前创建好的,我们可以直接使用的,在哪里都可以使用

  1. 编码和解码:- 玩玩悄悄话
    问题:url中不允许出现多字节字符,如果出现会乱码
    utf-8编码格式下,一个汉字,占3字节
    解决:发送前,前端将用户输入的多字节字符编码为单字节字符(符号、字母)
    发送后,后端将单字节字符解码为多字节原文

     	编码:var code=encodeURIComponent("原文");
     	解码:var 原文=decodeURIComponent(code);
     	其实百度根本就没做这个操作,这个东西在某次浏览器更新后,就自带此功能了,当场就淘汰了
    
  2. isFinite(num); 判断num是不是无穷大,true->有效数字 false->无穷大
    哪些会为false:NaN,分母为0(java就会错),Infinity

  3. 牛逼的:parseInt/Float/isNaN/eval()
    • eval(str);解析字符串(去掉字符串的引号)
      • console.log(eval(‘1+2+3’))会直接得到结果 6

函数执行原理

  • 程序加载时:

    1. 创建执行环境栈ECS:保存了函数调用的顺序的数组
    2. 首先压入全局执行环境(全局EC):它引用着全局变量window
    3. window中保存着全局变量
  • 定义函数时:

    1. 创建函数对象:封装代码段
    2. 在函数对象中有个作用域scope属性:记录着函数来着作用域是那里
    3. 全局函数的scope都是window
  • 调用前:

    1. 在执行环境栈(ECS)压入一个新的EC(函数的EC)
    2. 创建活动对象(AO):保存着本次函数调用时用到的局部变量
    3. 在函数中的EC有一个属性scope chain(作用域链)引用AO
    4. AO有一个parent的属性是函数的scope引用着的对象
  • 调用时:

    • 正是因为有了前三个步骤,我们才有了变量的使用规则:优先使用局部的,局部没有找全局,全局没有则报错
  • 调用完:

    • 函数的EC会出栈,AO无人引用,垃圾回收器登场,AO自动释放,局部变量也就自动释放
  • 两链一包:
    作用域链:以函数的EC为起点,经过AO,逐级引用,形成一条链式结构,作用:查找变量,带来了变量的使用规则:优先使用局部的,局部没有找全局,全局没有就报错
    原型链:每个对象都有一个属性.__proto__,可以不断的找到自己的父亲/爷爷/祖先...形成的链式结构,作用:查找属性和方法,哪怕自己没有属性或方法可以看看原型有没有
    闭包:保护一个可以反复使用的局部变量的一种词法结构,使用场景:防抖节流

闭包

希望保护一个可以【反复使用的局部变量】的一种词法结构,其实还是一个函数

  • 结构:

    1. 两个函数进行嵌套

    2. 外层函数创建受保护的变量

    3. 外层函数return出内层函数

    4. 内层函数要去操作受保护的变量

    5. 内层函数return出操作后的变量

       function f1() {
                  var a = 0;
                  return function(){
                      a++
                      return a;
                  }
              }
              var yx = f1()
              var xy = f1()
      
              console.log(yx());
              console.log(yx());
              console.log(yx());
              console.log(xy());
              console.log(xy());
      
      
  • 注意:

    1. 判断是不是闭包,有没有两层函数嵌套,返回内层函数,内层函数在操作外层受保护的变量
    2. 外层函数调用了几次,就创建了几个闭包,受保护的变量就有了几个副本
    3. 同一次外层函数调用,返回的内层函数,都是在操作同一个受保护的变量
  • 缺陷:受保护的变量,永远不会被释放,使用过多,会导致内存泄漏

  • 闭包的使用场景:防抖节流

    • 公式:

      function fdjl(){
      				var timer=null;//局部的timer准备保存定时器,定时器的序号
      				return function(){
      					if(timer!==null){clearTimeout(timer)}//停止一次性定时器
      					timer=setTimeout(function(){
      						操作即可!
      					},1000)
      				}
      			}
      			var inner=fdjl();
      
    • 需要防抖节流的地方

      1. elem.onmousemove - 频繁的修改DOM树,影响性能
      2. input.oninput - 每次input的内容修改都会触发,性能不好
      3. window.onresize -补充搭配上innerWidth可以实时获得窗口改变的尺寸
      xxx.以上三个事件=function(){
      					inner();
      				}
      

Object对象

三大特点:封装,继承,多态

  • object封装:

    1. *直接量方式:
      var obj={
      "属性名":属性值,
      ...
      "方法名":function(){操作},
      ...
      };
  1. 预定义构造函数方式:var obj=new Object();//空对象
    //需要自己后续慢慢添加
    obj.属性名=属性值;
    obj.方法名=function(){}

  2. 以上两个方法都有一个缺陷:一次只能创建一个对象

  3. 自定义构造函数:批量创建

    ​ function f1(形参1,形参2.....){

    this.属性名1=形参1;

    this.属性名2=形参2;

    this.属性名3=形参3;

    。。。。

    }

    var one=new f1(实参...)

    var two=new f1(实参...)

    ......

  • 特殊

    1. 其实属性名和方法名的""可以不加的,暂时不推荐,因为以后我们要学习一种数据格式JSON,要求属性名和方法名必须是一个"",单引号都不可以,为了习惯,我们就一直加上使用

    2. 访问对象的属性和方法

      				*obj.属性名	===	obj["属性名"]
        					*obj.方法名()	===	obj["方法名"]();
        					***js有一句话万物皆对象,一切对象的底层都是hash数组
      
    3. 访问到不存在的属性,返回undefined

    4. 可以随时随地的添加新属性和方法

    5. 希望遍历出对象所有的东西,使用for in循环

    6. 如果你希望在对象的方法里使用对象自己的属性:this.属性名

  • 难点:this的指向

    1. 单个元素绑定事件:this->这个元素
    2. 多个元素绑定事件:this->当前触发事件的元素
    3. 函数中使用this:this->当前调用此函数的对象,看看函数前面是谁
    4. 定时器中this->window
    5. 箭头函数this->外部对象
    6. 构造函数中的this->当前正在创建的对象
  • 批量创建对象:【自定义】构造函数方式

    1. 创建自定义构造函数
      function 类名(name,age,hobby){
      this.name=name;
      this.age=age;
      this.hobby=hobby
      }
    2. 调用构造函数创建出对象
      var obj=new 类名(实参列表);
  • 面向对象:

    • 缺点:新手不友好

    • 优点:

      1. 逼格高,所有的属性和方法都保存在一个对象之中 - 更符合现实生活
    1. 每个功能特地分开写 - 便于后期维护,哪怕没有注释,我也能一眼看懂代码

      1. 铁锁链舟 - 一个方法触发多个方法联动
      面向对象开发:
      var fn=function(x){
        	this.x=x;
        	this.btns = parent.getElementsByTagName('button');
       	this.init()
       }
       
      fn.prototype.init=function(){  //初始化
       代码...
       this.bind()
       }
       
      fn.prototype.bind=function(){   //绑定事件
       var me=this;
       for(var i=0;i<this.btns.length;i++){
       	this.btns[i].onclick=function(){
       		if(this.btns[i]==xx){me.animation(a,b)}else{me.animation(a,b)}
       	}
       }
      }
       
      fn.prototype.animation=function(a,b){    //操作
       代码...
       }
       
       .......
       
       
       var nav=new fn(x)
       nav.init()
       
       
       ------------------------------------------------------------------------------
       封装对象:
       var carousel={
       	btns:parent.getElementsByTagName('button'),
       	init:function(){
       	 	代码...
      		 this.bind()
       	},
      	bind:function(){
       		代码...
       		this.animation()
      	 },
      	animation:function(){
       		代码...
       	}
       }
       carousel.init();
      
  • object继承:

    父对象的成员(属性和方法),子对象可以直接使用

    1. 为什么:【代码重用】!节约内存控件,提升网站的性能!
    2. 何时继承:只要多个子对象公用的属性和【方法】,都要集中定义再父对象之中,尤其是方法,方法/函数也是一个对象,创建的函数越多性能越差何时继承:只要多个子对象公用的属性和【方法】,都要集中定义再父对象之中,尤其是方法,方法/函数也是一个对象,创建的函数越多性能越差。
  • 如何去找到父对象(原型对象):保存一类子对象共有属性和共有方法的

    1. 对象名.__proto__;//必须先有一个对象
    2. 构造函数名.prototype;//构造函数名:String、Function、Object、RegExp、Array、Date、h52205...,几乎人人都有,除了undefined,null,Math
  • 原型链:

    • 每个对象都有一个属性:__proto__,可以一层一层的找到每个人的父亲,形成的一条链式结构,我们就称之为叫做原型链
      可以找到所有父对象的成员(属性和方法),作用:查找共有属性和共有方法
      最顶层是Object的原型,上面放着我们眼熟的toString,怪不得人人都可以toString();
      JS万物皆对象
  • 有了原型对象,设置共有属性和共有方法:
    1、原型对象.属性名=属性值;
    2、原型对象.方法名=function(){}

笔试题:

判断自有还是共有
  1. 判断自有:obj.hasOwnProperty("属性名");
    • 如果结果为true,说明是自有属性,如果结果为false,有两种可能:共有或没有
  2. 判断共有:
if(obj.hasOwnProperty("属性名")==false&&"属性名" in obj){//in自动去整条原型链上进行查找
				console.log("共有")
			}else{
				console.log("没有")
			}

组合公式:
if(obj.hasOwnProperty("属性名")){
				console.log("自有")
			}else{
				if("属性名" in obj){
					console.log("共有")
				}else{
					console.log("没有")
				}
			}
修改/删除 自有和共有
  • 自有:修改:obj.属性名=新属性值;
    删除:delete obj.属性名;

  • 共有:修改:原型对象.属性名=新属性值; - 千万不要对着对象直接修改共有属性,这样会导致再本地添加一个同名属性
    删除:delete 原型对象.属性名;

      		      ```
    var peple = function (name, age, sex) {
                this.name = name
                this.age = age
                this.sex = sex
            }
            peple.prototype.eat = '吃饭'
    
            var yx = new peple('杨鑫', 18, '男')
            console.log(yx);
    
            // 修改自有
            yx.sex = '未知'
            console.log(yx);
            //删除自有
            delete yx.age
            console.log(yx);
    
            // 修改共有
            peple.prototype.eat = '喝水'
            console.log(yx);
    
            // 删除共有
            delete peple.prototype.eat
            console.log(yx);
      		      ```
    
如何为老IE的数组添加indexOf

不是固定的,但是是一种类型题,为一类人添加方法

if(Array.prototype.indexOf===undefined){//说明你打开了老IE
				Array.prototype.indexOf=function(key,starti){
					starti===undefined&&(starti=0);
					for(var i=starti;i<this.length;i++){
						if(this[i]==key){
							return i;
						}
					}
					return -1;
				}
			}
*如何判断x是不是一个数组:

千万别用typeof,typeof只能判断原始类型,引用类型显示的都是一个object,这道题也不是固定的

  1. 判断x是否继承自Array.prototype
    Array.prototype.isPrototypeOf(x);

     //方法一
            console.log(Array.prototype.isPrototypeOf(arr));
            console.log(Array.prototype.isPrototypeOf(reg));
            console.log(Array.prototype.isPrototypeOf(date));
            // 方法一扩展
            console.log(RegExp.prototype.isPrototypeOf(reg));
            console.log(Date.prototype.isPrototypeOf(date));
    
  2. 判断x是不是由Array这个构造函数创建的
    x instanceof Array

      console.log(arr instanceof Array);
            console.log(obj instanceof Object);
         console.log(reg instanceof RegExp);
            console.log(date instanceof Date);
    
  3. Array.isArray(x); - ES5提供,只要是ES5+,老IE都不支持!只有数组有

    console.log(Array.isArray(arr));
    console.log(Array.isArray(obj));
    console.log(Array.isArray(reg));
    console.log(Array.isArray(date));
    console.log(Date.isDate(date)); //报错

  4. *输出【对象的字符串】形式,说白了就是转为字符串来判断

    • 在Object的原型上保存着最原始的toString方式

    • 原始的toString输出的形式:[object 构造函数名]

    • ***多态:肤浅理解,一个方法有多种表现形态,子对象觉得父对象提供的成员不好用,在本地定义了同名成员,来覆盖父对象
      ​ 固定套路:借用:Object.prototype.toString.apply(x) === "[object Array]";
      ​ true->数组 false->不是数组

      // console.log(Object.prototype.toString.apply());   // 意思为:在Object的原型上保存着最原始的toString方式、原始的toString输出的形式:[object 构造函数名]
                 
                 console.log(Object.prototype.toString.apply(arr));  //[object Array]
                 // console.log(Object.prototype.toString.apply(date)); 
                 // console.log(Object.prototype.toString.apply(reg));
                 // console.log(Object.prototype.toString.apply(obj));
                 // console.log(Object.prototype.toString.apply());   // [object Undefined]
                 
                 console.log(Object.prototype.toString());   //  [object Object]  万物皆对象
                 // console.log(Object.prototype.toString(date)); //  [object Object]
                 // console.log(Object.prototype.toString(reg)); //  [object Object]
                 // console.log(Object.prototype.toString(obj)); //  [object Object]
             
                 // console.log(Array.apply()); //   []
                 // console.log(Object.apply());		{}
                 // console.log(RegExp.apply());		/^\d$/
      

5.实现自定义继承:

  1. 实现两个对象之间继承:
     `子对象.__proto__=父对象;`
   							var wsc={
   			        				"phone":"威图",
   			        				"car":"三B三",
   			        				"gf":"一堆"
   			        			};
   			        			var wjl={
   			        				"money":10000000000000000
   			        			};
   			        			var dy={
   			        				js:150
   			        			};
   			        			wsc.__proto__=wjl;
   			        			wjl.__proto__=dy;
   			        			console.log(wsc.money);
   			                        console.log(wsc.js);

  2. 实现批量对象继承
     构造函数名.prototype=父对象;
        			但是要小心时机,必须在创建对象之前,确定关系	
	 var dy = {
            "js": 150;
        };
        
        function h52205(name, age, hobby) {
            this.xingming = name;  //hxy.xingming="糖稀"
            this.nianling = age;   //hxy.nianling=30
            this.aihao = hobby;   //hxy.aihao="骗钱"
        };
        
        h52205.prototype = dy;  //必须在创建对象之前,确定关系
        var hxy = new h52205("化学有", 35, "吃饭");
        var tx = new h52205("糖稀", 30, "骗钱");
        var zx = new h52205("招新", 128, "睡觉");
        var lww = new h52205("廖文五", 300, "扣脚")
        
        console.log(tx);
        console.log(hxy);
        console.log(zx);
        console.log(lww);

Array数组对象

数组的基础

  • 概念:什么是数组:一个内存(变量)中保存多个数据的一个集合结构
    何时:只要存储的多个相关的数据,都要用数组集中保存
    为什么:一个好的数据结构,可以极大的提升程序员的开发效率

  • 创建:2种

    • *直接量:var arr=[值1,....]
      • /ES3之前是没有[]这个操作的,只能使用new Array,而为了简化,才提供了[],[]的底层就是new Array
    • 构造函数:var arr=new Array(值1,....);
      • var arr=new Array(1,2,3,4,5);//创建了一个数组,里面装有1,2,3,4,5
      • 有一个坑var arr=new Array(5);//创建了一个长度5的空数组
  • 访问:

    • 数组名[i] - 当前元素
    • 添加/修改:数组名[i]=新值
  • 特殊:

    • 读取元素,下标越界 - 返回undefined
    • 添加元素,下标越界 - 下标不在连续,导致变成一个稀疏数组
  • 数组的三大不限制:长度、类型、下标越界(不好)

  • 数组的唯一的属性:长度:arr.length
    三个固定套路:
    1、末尾添加:arr[arr.length]=新值;
    2、获取倒数第n个:arr[arr.length-n];
    3、缩容:arr.length-=n;

  • 遍历数组:
    for(var i=0;i<arr.length;i++){
    arr[i];//当前次元素
    }

  • 如何释放引用类型:看清楚你的这个引用类型有几个变量引用着,都要释放后才能真正的释放干净
    建议:我们的代码尽量的都要封装在一个函数之中!函数中的内存会自动释放

  • 索引数组 - 下标都是由数字组成的数组。
    *关联(hash)数组 - 下标都是可以自定义的数组
    为什么:索引数组的下标,无具体的意义,不便于理解和查找
    如何使用:

    1. 创建:2步
      1、先创建一个空数组:var arr=[];
      2、为空数组添加自定义下标以及内容:arr["自定义"]=新值;
    2. 访问:arr["自定义下标"];
    3. 强调:hash数组的length永远失效了,永远为0!
      遍历数组,不能再使用for循环,必须使用for in循环 - 纯自动化循环,【专门为遍历数组准备的】
      for(var i in 数组名){
      i;//自动得到下标
      数组名[i];//得到当前次元素
      }
      很棒:不光可以遍历hash数组,还可以遍历索引数组。
      个人:遍历索引数组使用for循环,遍历hash数组再使用for in
    4. hash数组的原理:
      hash算法:将字符串,计算出一个尽量不重复的数字(地址值)
      字符串的内容相同,则计算出来的数字也是相同的
      添加元素:将自定义下标交给了hash算法,得到了一个数字(地址值),直接将你要保存的数据放到了此地址
      获取元素:将指定的自定义下标交给hash算法,得到一个和当初添加上完全相同的数字(地址值),通过地址就可以找到你当初保存的数据
    5. ***js里万物皆对象(除了undefined和null),【一切对象的底层都是hash数组】
  • 二维数组:

    数组的元素,又引用着另一个数组

    • 创建

      • var arr=[
        ["杨鑫",18,3500],
        ["是新建",19,4500],
        ["杨婷婷",20,5500]
        ];
    • 访问:arr[行下标][列下标];

    • 特殊:列下标越界,得到undefined
      行下标越界,会报错,因为只有数组才可以用[下标]。行下标越界已经得到undefined,没有资格再加下标

    • 遍历二维数组:必然需要两层循环,外层循环控制行,内层循环控制列 - 现在都流行扁平化
      公式:for(var r=0;r<arr.length;r++){ for(var c=0;c<arr[r].length;c++){ console.log(arr[r][c]) } }

数组的API

  • *****转字符串 arr.join('自定义连接符号')

    • 固定用法

      • 无缝拼接(将数组里面的内容拼接为一句话/单词)
      • 渲染页面:将数组里面的数据拼接为DOM页面元素
        • //数据
          var arr=["-请选择-","北京","南京","西京","东京","重庆"];
          //将数组转为字符串,只不过是拼接了标签
          var str="

          "+arr.join("

          ")+"

          ";
          //渲染到DOM树上
          bd.innerHTML=str
    • 补充:

      • 二级联动:
        专属事件:select.onchange - 只有选中项发生变化才会触发
        专属属性:select.selectedIndex - 选中项的下标,不需要自定义下标,但是只有select可以
        二维数组:细分分类

  • *拼接数组:添加元素的新方式

    • //根据你传入的实参全部拼接到数组的末尾
      var newArr=arr.concat(新值1,...);
      特殊:1、不修改原数组,只会返回一个拼接后的新数组
      2、支持传入一个数组参数进行拼接,悄悄的将你的数组打散放入

      • 		  var arr = [1, 2, 3]
                  var arr1=[4,5,6]
          
                  var arr2 = arr.concat(arr1,7,8,9)
                  console.log(arr);
                  console.log(arr2);
        
  • *截取子数组:

    • //根据你传入的开始下标一直截取到结束下标
      var subArr=arr.slice(starti,endi+1);
      特殊:1、不修改原数组,只会返回一个截取后的新数组
      2、含头不含尾
      3、endi可以省略不写,会从starti一直截取到末尾
      4、两个实参都可以省略,从头截到尾,复制了一份(深拷贝-两者互不影响)
      5、支持负数参数,-1代表倒数第一个

以上的API都是不会修改原数组的

-------------------------------------------------------------------------------------------------------------------------------------------------

以下的API都会修改原数组

  • *删插替:

    var dels=arr.splice(开始索引,删除个数,新数据);

    • 删除:var dels=arr.splice(starti,n);//n代表删除的个数
      特殊:其实splice也有返回值,返回的是你删除的元素组成的一个新数组
    • 插入:arr.splice(starti,0,新值1,...);
      特殊:1、原starti位置的元素以及后续元素都会被向后顺移
      2、千万不要插入一个数组,会变成二维数组
    • 替换:var dels=arr.splice(starti,n,新值1,...);
      特殊:插入的个数和删除的个数不必相等

  • 反转数组:arr.reverse();

冒泡排序:

强调:排序非常重要,记住:只要以后页面中有排序功能,说明他的底层一定是一个数组,因为js中只有数组才可以排序

  • 手写(鄙视题):

    - ```
        //手写冒泡排序:从第一个元素开始,依次比较相邻的两个元素,如果前一个>后一个,两者就要交换位置
        for(var j=1;j<arr.length;j++){
        				for(var i=0;i<arr.length-j;i++){
        					console.log(1);
        					if(arr[i]>arr[i+1]){
        						var m=arr[i];
        						arr[i]=arr[i+1];
        						arr[i+1]=m;
        					}
        				}
        			}
        
        ```
    
  • 开发中,使用数组的API

    • arr.sort();
      默认会将元素们转为字符串,按位PK每个字符unicode(ascii)

    • 升序arr.sort(function(a,b){a-b})

    • 降序arr.sort(function(a,b){b-a})

      • 原理:

        ```
        arr.sort(function(a,b){//sort传了一个实参:比较奇怪,是一种匿名回调函数,不需要程序员自己调用,前辈们已经帮我们调好了,而且我们不需要管前辈们怎么创建的,只需要记忆固定用法即可
        //					console.log(a);//后一个数字
        //					console.log(b);//前一个数字
        					return a-b;//如果return 是一个正数:说明后一个数>前一个数
        						   		//如果return 是一个负数:说明后一个数<前一个数
        						   		//如果return 是一个0:说明后一个数==前一个数
        						  		//而sort方法会根据你return的正/负数,来自动进行排序操作
        				})
        ```
        

栈和队列:

  • 栈:其实就是数组,只不过要求是一段封闭,只能从另一端进出的数组
    何时:希望优先使用最新的数据时,现实生活中:电梯、旅游公交车...现实生活中少的,代码中用的也少
    如何使用:
    开头进:arr.unshift(新值1,...);//添加元素的新方式,缺陷:导致其余元素的下标发生变化
    开头出:var first=arr.shift();//一次只能删除一个,删除元素的新方式,可以接住你删除的元素,缺陷:导致其余元素的下标发生变化

    		结尾进:arr.push(新值1,...);//添加元素的新方式
    		结尾出:var last=arr.pop();//一次只能删除一个,删除元素的新方式,可以接住你删除的元素
    
  • 队列:其实就是数组,只不过要求是一端进,另一端出
    何时:希望按照先来后到的顺序使用数据时,生活中多个一批
    开头进:arr.unshift(新值1,...);
    结尾出:var last=arr.pop();

      	结尾进:arr.push(新值1,...);
      	开头出:var first=arr.shift();
    
  • 扩展:

    定时器:每过一段时间,就会执行一次的代码
    开启:

    ​ var timer=setInterval(function(){
    操作
    },间隔毫秒数)

      		停止:clearInterval(timer)
    

    事件:

      			鼠标移入:onmouseover
      			鼠标移出:onmouseout
    

String 字符串对象

Sring的概念:

  • 什么是字符串:多个字符组成的【只读】字符【数组】(只读:我们要学习的任何字符串API,都是不会修改原字符串,意味着必须要拿一个变量接住结果)
    和数组有相同的点:
    1、字符串中个数(长度):str.length
    2、获取字符串中的某个字符:str[i];
    3、遍历字符串
    4、所有数组不修改原数组的API,字符串都可以使用(concat、slice)

  • 和数组也有很多不同的地方:
    所有的数组直接修改原数组的API,字符串都不可以使用!比如排序只有数组可以使用,但是
    字符串也有很多很多属于自己的API

String的API

只有字符串可以使用的方法,无需创建,直接使用。

  • 转义字符:\

    		作用:
    			1、将字符串和程序冲突的字符转为原文
    			"\""
    
    			2、包含特殊功能的符号
    				\n -> js字符串换行
    				\t  -> js字符串中的制表符,就是你平时敲键盘的tab键,大空格
    	
    			3、输出unicode编码的字符
    				\uXXXX:第一个汉字:4e00		->ascii:19968
    					最后一个汉字:9fa5		->ascii:   40869```
    
  • *大小写转换:将字符串中的每个英文字符统一的转为大写或小写
    何时:只要程序不区分大小写,就要【先统一】的转为 大写 或 小写,再比较(验证码)
    如何:
    大写:var upper=str.toUpperCase();
    小写:var lower=str.toLowerCase();

  • 获取字符串中指定位置的字符:str.charAt(i); 还不如str[i]

  • *获取字符串中指定位置的字符的ASCII码:
    ​ var ascii=str.charCodeAt(i);

*通过ascii码转回原文
var 原文=String.fromCharCode(ascii);

  • ***检索字符串:检查索引:获取关键字的下标
    var i=str/arr.indexOf("关键字",starti); ========str.macth("关键字")
    starti可以省略,如果省略了,从下标0开始向右查找
    返回值:找到了,返回的是第一个关键字的第一个字符的下标
    *没找到,返回-1,我们不关心下标为多少,我们只关心下标为不为-1
    作用:判断有没有
    强调:不光字符串可用,数组也可用!老IE的数组是没有此方法的
    鄙视题:此方法默认只能找到关键字的第一个下标,找到关键字的所有下标,如何完成?

    ​ var str="no zuo no die no can no bibi";
    ​ var index=-1;//开始位置 -1
    ​ while((index=str.indexOf("no",index+1))!=-1){
    ​ console.log(index);
    ​ }

  • *截取字符串:3个
    *var subStr=str/arr.slice(starti,endi+1);//用法和数组一摸一样
    str.substring(starti,endi+1);//垃圾:用法几乎和slice一致,但是不支持负数参数,而且只有字符串可用
    str.substr(starti,n);//n代表截取的个数,不再需要考虑含头不含尾,也支持负数下标

  • 拼接字符串:var newStr=str.concat(新字符串,...);//还不如+运算

  • *替换字符串:var newStr=str.replace("关键字"/正则表达式,"新内容");

    • 本身这个方法非常强大,但是由于我们现在暂时不会正则表达式,所以只能替换固定的关键字(下周一就牛逼了)
  • *****切割/分割字符串:
    作用:str<=>arr
    var arr=str.split("自定义切割符"/reg);
    特殊:1、切割后,切割符就不存在了,转为一个数组
    2、切割符"",切散每一个字符

  • xx.trim() 去除首尾空格

RegExp正则对象

定义字符串中字符出现规则的表达式

语法:

  1. 最简单的正则:/关键字原文/

    • /no/后缀

      • 后缀:g:匹配全部(字符串里面有一个满足正则的); i:忽略大小写
  2. 正则对象

    • 创建:2种
      1. 直接量方式:var reg=/正则表达式/后缀;
      2. 构造函数方式:var reg=new RegExp("正则表达式","后缀");
    • 方法:1个
      • var bool=reg.test(str); //验证 括号里面的内容是否满足正则
        • true->说明用户输入正确
        • false->说明用户输入错误
  3. 备选字符集:/[备选字符集]/;

  • 强调:
    • 一个中括号,只管一位字符
    • 正则表达式默认只要满足了就不管后续了,我们希望从头到尾完全匹配
    • 解决:前加后加$:/[备选字符集]$/;
      • 特殊:如果备选字符集中ascii码是连续的,那么中间部分可用-省略
      • 比如:
        • 一位数字:/[0-9]/; 输入11,为true (一个中括号,只管一位字符)
        • 一位数字:/^[0-9]$/; 输入11,为false
        • 一位字母:/[A-Za-z]/;
        • 一位数字或字母或下划线:/[0-9A-Za-z_]/+++
        • 一位汉字:/[\u4e00-\u9fa5]/
        • 除了xxx之外的:/[^0-9]/ - 很少使用,范围太广了
  1. 预定义字符集:前辈们提前定义了一些字符集,方便我们程序员 - 简化备选字符集
    - 一位数字:/\d/===/[0-9]/;
    - 一位数字、字母、下划线: /\w/ ===/[0-9A-Za-z_]/;
    - 一位空白字符(空格、回车、制表符): /\s/
    - 一位除了换行外的任意字符:/./ (很少使用,范围太广了)
    - 建议:优先使用预定义字符集,预定义字符集满足不了再用备选字符集补充

(不管是预定义字符集还是备选字符集,一个都只管一位)

  1. 量词:规定了一个字符集出现的次数:
    1、有明确数量:
    字符集{n,m}:前边相邻的字符集,最少n次,最多m次
    字符集{n,}:前边相邻的字符集,最少n次,多了不限
    字符集{n}:前边相邻的字符集,必须n次
    2、无明确数量:
    字符集? 前边相邻的字符集,可有可无,最多一次
    字符集* 前边相邻的字符集,可有可无,多了不限
    字符集+ 前边相邻的字符集,至少一次,多了不限

  1. 选择和分组:
    选择:在两个规则中选一个
    规则1|规则2
    分组:将多个字符集临时组成一组子规则
    (规则1|规则2)

  2. 指定匹配位置:
    开头:^
    结尾:$
    特殊:两者同时出现,前加^后加$,表示从头到尾要求完全匹配 - 只要你做【验证】必须加上

  3. 预判:密码强度验证:4位密码,数字和字母都可以,至少要有一位大写和一位数字。

    (?![xxxx]+$)

    /^(?![0-9]+$)[0-9A-Za-z]{4}$/; - 前面的预判意思是:不能纯由数字组成,可能由大写、小写
    /^(?![A-Za-z]+$)[0-9A-Za-z]{4}$/; - 前面的预判意思是:不能纯由大写字母组成,也不能纯由小写字母组成,也不能纯由大写和小写字母组成
    /^(?![0-9a-z]+$)(?![A-Za-z]+$)[0-9A-Za-z]{4}$/; - //4位密码,数字和字母都可以,至少要有一位大写和一位数字。
    /^(?![0-9a-z]+$)(?![A-Za-z]+$)(?![A-Z0-9]+$)[0-9A-Za-z]{4}$/; - //4位密码,数字和字母都可以,至少要有一位大写和一位数字和一位小写。
    /^(?![0-9A-Za-z]+$)[0-9A-Za-z\u4e00-\u9fa5]{4}$/ - //至少要有一位汉字
    

String支持正则的API

  1. 分割:var arr=str.split(‘str’/reg);

    //身份证获取年月日
          var sfz = '51050420020312181X'
          //方法一:
          var reg = /(\d{6})(\d{4})(\d{2})(\d{2})(\d{3}[\dX])/gi
          var arr=sfz.split(reg)
          console.log(arr);  // ["", "510504", "2002", "03", "12", "1814", ""]
          console.log('生日'+arr[2]+','+arr[3]+','+arr[4]);
          
     // 方法二
       var reg = /^(\d{6})(\d{4})(\d{2})(\d{2})(\d{3}[\dXx])$/  
      // var reg = /^\d{6}\d{4}\d{2}\d{2}\d{4}$/
      var newstr=sfz.replace(reg,function(a,b,c,d,e){
          //                  //正则加括号分组时              没分组时
          // console.log(a);  //整个身份证                 整个身份证
          // console.log(b);  //正则第一个小括号里面的内容   0
          // console.log(c);  //正则第二个小括号里面的内容   整个身份证
          // console.log(d);  //正则第三个小括号里面的内容   undefined
          console.log('生日' + c + ',' + d + ',' + e);
      })
      
      ```
    
  2. 替换:replace

    1. 基本替换法:
       			var newStr=str.replace(str/reg,"新内容");
          			//replace支持正则,搭配上后缀就可以替换所有的关键字,而且替换往往都要加后缀
          			//问题:只能替换为固定的内容
     
    2. 高级替换法:
     
          ```
     var newStr=str.replace(/[卧我握窝沃][槽操草糙艹屮肏你]+/g,function(a,b,c){
                console.log(a);//要替换的关键字 - 正则匹配到的关键字
                console.log(b);//要替换的关键字的下标 - 正则匹配到的关键字的下标
                console.log(c);//原文本身
                return a.length==2?"**":a.length==3?"***":"****";
                        			});
     	\\手机号中间四位马赛克
                  var phn = '17721988849'
                  var reg = /(\d{3})(\d{4})(\d{4})/;
                  phn = phn.replace(reg, function (a, b, c, d) {
                      return phn = b + '****' + d;
                  })
                  console.log(phn);
    

3.格式化

var idCard="500103198602215933";
var reg=/\d{6}(\d{4})(\d{2})(\d{2})\d{4}/;
idCard=idCard.replace(reg,function(a,b,c,d){
//再replace的时候,如果正则中出现了分组,那么我们会得到更多的形参
//再形参a的后就会再出现n哥形参,具体多少个看你有多少个分组
//第一个分组获取到的内容会保存在第二个形参之中
//第二个分组获取到的内容会保存在第三个形参之中
//...
//倒数两个形参分别是下标和原文
   return b+"年"+c+"月"+d+"日";
   })	

Math数学对象

专门提供了数学计算的API,不需要创建,直接使用的,由浏览器的JS的解释器自动创建

  • 取整
    1. 上取整:超过一点点,就会取下一个整数 - 现实生活少
      var num=Math.ceil(num);
    2. 下取整:无论超过多少,都会省略掉小数部分 - 现实生活少
      var num=Math.floor(num);
    3. 四舍五入取整:
      var num=Math.round(num);//进制只看小数的第一位
    4. parseInt(str)+num.toFixed(d)
    5. num.toFixed(d)
      • 优点:可以四舍五入保留指定的小数位数
      • 缺点:返回的结果是一个字符串,建议搭配上parseFloat一起使用
    6. 鄙视题:不允许使用toFixed的情况下,自己封装一个函数,实现toFixed的功能,并且最后返回一个数字?
      原理:看用户提供的数字和保留几位小数,对着用户的这个数字*10几次方(保留几位小数),再使用Math.round四舍五入取整
      对着这个数字/10几次方(保留几位小数)
  • 乘方Math.pow(底数,幂) === 底数**幂
    • 开平方:Math.sqrt(num)
  • 绝对值:把负数转为正数
    Math.abs(num);
  • 最大值和最小值:
    var max/min=Math.max/min(a,b,c,d,e,f,g,....);
    问题:本身不支持数组参数的
    *解决:固定用法:
    var max/min=Math.max/min.apply(Math,arr);
    apply其中一个功能是可以将数组打散为单个元素
  • **随机整数:Math.random() - 在0~1之间取出一个随机的小数,可能取到0,但是绝对不可能取到1,
    意味着可能取到最小数,但是不可能取到最大数
    公式:parseInt(Math.random()
    (max-min+1)+min);
  • 三角函数

Date日期对象

  • 创建日期

    1. 创建一个当前日期时间:
      var now=new Date();
    2. 创建一个自定义时间:
      var birth=new Date("yyyy/MM/dd hh:mm:ss");
    3. 创建一个自定义时间:
      var birth=new Date(yyyy,MM,dd,hh,mm,ss);
      //修正月份:从0开始的,0->11月
    4. 复制一个日期:
      为什么:日期的所有的API都是直接修改原日期的,无法获得修改之前的日期
      所以,在执行API之前先进行复制,然后在对复制的日期进行操作
      var end=new Date(start);
  • 操作日期

    1. 两个日期对象之间可以相减,得到一个毫秒差,通过毫秒差就可以换算出你想要的任何一部分 - 日期的本质其实底层就是保存的毫秒数
      创建日期的最后一种方式:var date=new Date(毫秒数);

    2. 、API:
      分量:时间的单位:
      年月日星期:FullYear、Month、Date、Day
      时分秒毫秒:Hours、Minutes、Seconds、Milliseconds;
      每一个分类都有一对儿getXXX/setXXX
      其中getXXX负责获取某一个分量的值;
      其中setXXX负责设置某一个分量的值;
      特殊:
      1、取值范围:
      FullYear - 当前的年份
      Month - 0~11
      Date - 1~31
      Day:0~6,外国人的眼里星期天是一周的第一天
      Hours:0~23
      Minutes、Seconds:0~59
      2、在设置时间的时候,如果超过了范围,会自动进制
      3、Day是不允许设置的

      ​ 4、设置时分秒 date.setHours(11,0,0)

    3. 如果希望对某个分量进行加减操作,推荐一句话完成
      date.setXXX(date.getXXX()+/-num)

    4. 格式化日期为字符串:
      作用:1、date->string:stringAPI更多更实用
      2、默认的标准国际日期,不是所有人都认识
      date.toLocaleString();//转本地字符串
      问题:存在兼容性问题
      解决:自定义日期格式化format

 daojishi()

        //获取现在的时间,日+1,时分秒清零
        var end = new Date()
        end.setDate(end.getDate()+1)
        end.setHours(00,00,00)

         //计算倒计时函数  
            function daojishi() {
                var start = new Date()
                // var end= new Date('2023')
                var end = new Date('2022/11/2 00:00:00');
                var time = end - start
                // console.log(time);
                var h = parseInt(time / 1000 / 3600)   //h
                h < 10 ? h = '0' + h : h
                var m = parseInt(time / 1000 % 3600 / 60)
                m < 10 ? m = '0' + m : m
                var s = parseInt(time / 1000 % 60)
                s < 10 ? s = '0' + s : s
                box.innerHTML = ('距离明天还有' + h + '时' + m + '分' + s + '秒');
            }


        // 定时器让页面上时间动起来
        setInterval(function () {
            var start = new Date()
            // var end = new Date('2022/11/2 00:00:00');
            var time = end - start
            var h = parseInt(time / 1000 / 3600)   //h
            h < 10 ? h = '0' + h : h
            var m = parseInt(time / 1000 % 3600 / 60)
            m < 10 ? m = '0' + m : m
            var s = parseInt(time / 1000 % 60)
            s < 10 ? s = '0' + s : s
            box.innerHTML = ('距离明天还有' + h + '时' + m + '分' + s + '秒');
        }, 1000);

Error错误对象

  1. 浏览器自带4种错误类型,可以快速找到自己的错误:
    语法错误:SyntaxError - 多半都是语法/符号错了
    引用错误:ReferenceError - 没有创建过就去使用了
    类型错误:TypeError - 不是你的方法/属性/单词写错,你却使用了
    范围错误:RangeError - 只有一个API:num.toFixed(d);//d取值范围的,0~100之间

  2. 只要发生错误,就会报错,会导致后续代码终止执行了(后面代码没效果、如果你做的是APP、软件发生报错,则会闪退,体验感差),我们不希望
    错误处理:就算发现错误,我们也不希望报错,而是给一个错误提示,后续代码依然可以继续执行
    语法:
    try{
    只放可能出错的代码
    }catch(err){
    只有发生错误后才会执行
    }

    ​ try...catch...的性能非常差,几乎是所有代码中效率最差的
    ​ *可用一个技术代替:if...else...
    ​ *开发经验:提前预判用户:记住一切的客户端输入/用户输入都是坏人 - 你也不必担心,只要做好你该做的防护即可(!isNaN、正则);

  3. 、自定义错误:
    throw new Error("自定义错误描述");

DOM:Document Object Model:文档对象模型,专门用于操作html文档的,提供了一些方法

DOM树概念:DOM将我们的HTML看作了是一个倒挂的树状结构,但是树根不是html标签,而是document对象

​ document对象:不需要程序员创建,由浏览器的js解释器自动创建,一个页面只有一个document对象
​ 作用:只要是对象就提供了属性和方法,等待我们学习,【找到】想要的任何DOM元素/对象/节点,甚至进行增、删、改、查(元素、属性、样式、文本)
​ DOM会将页面上的每个元素、属性、文本、注释都当作一个DOM元素/对象/节点

  • 面试题:

    • HTML-网页
    • XHTML -更严格的网页
    • DHTML -动态的网页 D:Dynamic 使我们的网页在离线状态怡然具有动态效果,这个不是新的技术和概念,原理:DHTML=HTML+CSS+JS(dom)
    • XML -数据格式
  • DOM:原本是可以操作一切结构化文档的 HTML 和 XML,后来为了方便各类开发者细分为了3部分:

    1. 核心DOM:【无敌的】,既可以操作HTML,又可以操作XML
      • 缺点:API比较繁琐
        • 比如:getAttribute()
    2. HTML DOM:只能操作HTML,
      • 优点:API非常简单
      • 缺点:比如属性部分,只能访问/设置标准属性,不能操作自定义属性
        • 比如: elem.class
    3. XML DOM:只能操作XML,XML已经淘汰了,现在最流行的数据格式是JSON

    开发建议:优先使用HTML DOM,HTML DOM满足不了的操作我们再用核心DOM进行补充

  • 每个DOM元素都有三大属性:

    1. xx.nodeType - 描述节点的类型
      • document节点:9
      • element节点:1
      • attribute节点:2
      • text节点:3
      • 以前有用:判断xx是不是一个页面元素,因为我们以前没有children找儿子,只有childNodes
    2. xx.nodeValue - 获取元素的属性值的
      • 以前有用:获取一个属性节点的值,因为以前没有getAttribute,只有getAttributeNode
    3. ***xx.nodeName:节点的名称 - 判断xx是什么标签
      注意:返回的是一个全大写的标签名!

查找元素

  1. 通过ID查找元素

    • var elem=document.getElementById('Id值')
      • 页面有重复的ID,只会找到第一个
      • 没找到返回null
      • 其实ID值不用查找,可以直接使用
      • 我们一般用class,不用id,id留给后端使用
  2. 通过 标签名查找元素

    • var elem=document/parent.getElementsByTagName/Name("标签名")
      • 返回的是一个类数组对象,没找到返回空类数组
      • js不能直接操作DOM集合,解决:1.搭配下标得到单个元素;2.使用 遍历得到每个元素
      • parent-->代表你已经找到的某个父元素
  3. 通过class名 查找元素

    • var elem=document/parent.getElementsByClassName('class值')
      • 返回的是一个类数组对象,没找到返回空类数组
      • js不能直接操作DOM集合,解决:1.搭配下标得到单个元素;2.使用 遍历得到每个元素
      • parent-->代表你已经找到的某个父元素
  4. 通过关系查找元素:前提必须先找到一个元素才可以操作

    • 查找该元素的父亲:elem.parentNode
    • 查找该元素的亲儿子:elem.children; ------------返回的是一个集合
    • 查找第一个子元素:elem.firstElementChild
    • 最后一个子元素:elem.lastElementChild
    • 前一个兄弟:elem.previousElementSibling
    • 下一个兄弟:elem.nextElementSibling
  5. *通过css选择器获取元素:

    1. 单个元素:var elem=document.querySelector("任意css选择器");
      强调:1、万一选择器匹配到多个,只会返回第一个
      2、没找到null
    2. *多个元素:var elems=document.querySelectorAll("任意css选择器");
      强调:找到了 返回集合,没找到返回空集合
      更适合用于做复杂查找
  6. 属性选择器:

    <!DOCTYPE html>
    <html>
    	<head>
    		<meta charset="UTF-8">
    		<title></title>
    		<style type="text/css">
    			[name=pwd]{
    				background: red;
    			}
    		</style>
    	</head>
    	<body>
    		<input name="name" />
    		<input name="name" />
    		<input name="name" />
    		<input name="pwd" />
    		<input name="pwd" />
    		<input name="pwd" />
    		
    		<script type="text/javascript">
    			var inps=document.getElementsByName("pwd");
    			console.log(inps);
    		</script>
    	</body>
    </html>
    
  7. 面试题:

    getXXX 和 queryXXX的区别?返回结果不同?

    • getXXX:返回的是一个动态集合HTMLCollection

      • 动态集合:根据DOM树的改变,悄悄的一起跟着变化,每一次修改DOM,都会悄悄的再次查找页面元素。缺点:性能低,而且不能使用forEach
    • queryXXX:返回的是一个静态集合NodeList

      • 静态集合:根据DOM树的改变,不会一起变化,只会认准当时找的时候的数据。优点:复杂查找时简单、性能高、使用forEach

操作元素

前提:找到元素才可以操作。

内容

  • innerHTML获取和设置开始标签到结束标签之间的内容,包含标签

    • 获取:elem.innerHTML;
    • 设置:elem.innerHTML='内容';
  • innerText获取和设置开始标签到结束标签之间的文本内容,不支持标签

    • 获取:elem.innerText

    • 设置:elem.innerText='新文本'

      (原本这个只有老IE可用,但随着各个浏览器的升级发展也支持了,老IE没有将就大家 ,反而大家在将就老IE)

    • 获取:elem.textContent

    • 设置:elem.textContent="新文本"

  • 获取或设置input的值

    • 获取:input.value;
    • 设置:input.value="新值";

属性

  • 设置获取属性: (核心DOM)

    • 获取属性值:elem.getAttribute("属性名");
    • 设置属性值:elem.setAttribute('属性名',"属性值"); 自定义属性名和属性值
  • 简化版: (HTML DOM)

    • 获取属性值:elem.属性名
    • 设置属性值:elem.属性名="属性值"
      • 特殊:
        1. class需要写为className
        2. 只能操作标准属性,自定义属性只能用Attribute进行操作
  • 移除属性:

    • elem.removeAttribute('属性名'); (核心DOM)
    • elem.属性名=""; (HTML DOM)
  • 判断有没有该属性

    垃圾:只能判断有没有,不能判断具体是什么

    • elem.hasAttribute("属性名"); (核心DOM)
    • elem.属性名!=""; (HTML DOM)
  • 强调注意:

    1. class必须写为className
    2. 简化版:只能操作标准属性,不能操作自定义属性(自定义属性只能用Attribute进行操作)
    3. 删除属性时,删不干净,有的属性删不干净依然具有功能:比如:href没有属性值会有默认的刷新功能,disabled没有属性值依然具有禁用的功能

样式

  • *只能操作(内联)行内样式:优先级最高,一定会覆盖其他的样式

    • 获取:elem.style.css属性名;
    • 设置:elem.style.css属性名="css属性值";
      • 特殊:
        1. css属性名,有-的地方,要去掉-,变为小驼峰命名法 border-radius -> borderRadius
        2. 获取样式时,也只能获取到内联样式
  • 操作样式表的css (内部样式style的css,或外部引入的css)

    1. 获取你想要操作的样式表

      • var sheet=document.styleSheets[i];
    2. 获取所有的样式规则

      • var rules=sheet.cssRules;
    3. 所有的规则中挑选出你需要操作的规则

      • var rule=rules[i];
    4. 做获取 或 设置

      • console.log(rule.style.width);
      • rule.style.width="100px"; ……

给元素绑定事件

一切的获取,都是为了做判断
一切的设置,都是在修改|添加

  • elem.on事件名=function(){
    操作;
    *****this关键字:这个
    如果单个元素绑定事件,this->这个元素
    如果多个元素绑定事件,this->当前触发事件的元素
    }

事件

  1. onclick
  2. onchange
  3. onmouseover
  4. onmouseout
  5. onfocus
  6. onblur
  7. onsubmit - 点击提交按钮的前一刻会触发,阻止提交:return false;
  8. oninput
  9. onresize
  10. window.oncontextmenu - 右键菜单事件
  11. window.onkeyDown - 键盘按下事件 ,e.keyCode获取键盘键码

创建和添加元素并且渲染DOM树:3步

  1. JS创建空标签:
    var elem=document.createElement("标签名");
  2. 为其设置必要的属性和事件
    • elem.属性名="属性值";
    • elem.on事件名=function()
  3. 上树
    • 父元素.appendChild(elem); - 将elem追加到父元素里面当了最后一个儿子
    • 父元素.insertBefore(elem,已有子元素); - 将elem追加到父元素里面,并且插入到已有子元素的前面
    • 父元素.replaceChild(elem,已有子元素) - 将elem追加到父元素里面,并且替换已有子元素
  • 补充:
    • elem.remove(); 删除当前元素

递归

简单来说就是函数中,又一次调用了函数自己,迟早有一天也会停下来

  • 何时使用:遍历DOM树,专门用于【层级不明确】的情况,既可以遍历层级不明确的DOM,又可以遍历层级不明确的数据

  • 如何使用:

    function 函数名(root){
    			1、第一层你要做什么操作直接做!
    
    			2、判断他有没有下一级,如果有下一级再次调用此函数,但是传入的实参是下一级的东西
    		}
    		函数名(实际的根);
    
  • 算法:深度优先!优先遍历当前节点的子节点,子节点遍历完毕后才会跳到兄弟节点

  • 缺点:同时开启大量的函数调用,消耗大量的内存,只有一个情况才会使用:【遍历层级不明确】

  • 递归 vs 纯循环

    • 递归:
      • 优点:直观易用
      • 缺点:性能低下
    • 纯循环:
      • 优点:性能高,几乎不占用内存
      • 缺点:难得批爆
遍历层级不明确的API
  • 语法:

    • 创建tw对象

      • var tw=document.createTreeWalker(根元素,NodeFilter.SHOW_ELEMENT);
      • 反复调用tw的nextNode()函数找到每一个元素
        while((node=tw.nextNode())!=null){
        node要干什么操作
        }

      缺点:1、必然跳过根元素,不会对根元素做操作
      2、不可以遍历层级不明确的数据,仅能遍历层级不明确的DOM元素

HTML DOM提供了一些常用对象:

(并且对这些对象进行了简化,但是不是人人都可以简化)

  • image对象:图片对象,仅仅只是简化了创建

    • (注意:不是人人都有构造函数创建方式)
    • 创建:var img=new Image();//完全等效于var img=document.createElement("img");
  • form对象: 简化了表单对象,简化的是查找:

    • 查找form元素:var forms=document.forms;
    • 查找当前这个form元素下面的input:var inps=forms[1].elements;
    • 专属事件:form.onsubmit=function(){//提交事件,只会在提交的一瞬间触发,防止用户输错还能提交
      return false;
      }
  • *select对象:

    • 属性:
      1. select.options;//得到select下面的所有的option,完全等效于xx.children;
      2. select.selectedIndex;//获取到当前选中项的下标,当时我们还说除了select其他元素都需要自定义下标才能完成此操作
    • 方法:*select.add(option); //完全等效于appendChild
    • 专属事件:select.onchange=function()
  • *option对象:仅仅只是简化了创建,但是却非常牛逼!!

    • var opt=new Option("innerHTML","value");

    • 搭配select.add(option);使用。

      ​ 一句话:完成4个操作:创建,设置value值,设置innerhtml
      select.add(new Option("innerHTML","value"));

ES5

只是在ES3的基础上添加了新特性 和 一些简化操作

保护对象:

  • 底层具有四大特性:

    "value": "威图", - 真正实际保存值的地方
    "writable": true, - 开关:控制着是否可以被修改
    "enumerable": true, - 开关:控制着是否可以被for in循环遍历到
    "configurable": true - 开关:控制着是否可以被删除,总开关:一旦设置为false,其他特性不可以再次修改,自己也不可逆
    
    如何修改四大特性:
    				Object.defineProperties(对象名,{
    					"属性名":{四大特性},
    					...
    				})
    
    
  • 三个级别:

    1. 防扩展:防止添加:Object.preventExtensions(obj);

    2. 密封:防止添加、删除:Object.seal(obj);

    3. 冻结:防止添加、删除、修改:Object.freeze(obj);

      缺点:

      1. 如果你不用面向对象开发,你保护个串串
      2. 前辈们都没有保护,你更不用保护了

*数组的新API:3组6个:

  • 判断:

    1. every:

      每一个,理论完全等效于&&,必须全部满足结果才为true,只要一个不满足,结果就为false

      var bool=arr.every(function(val,i,arr){
      					//val - 当前值
      					//i - 当前值的下标
      					//arr - 数组本身,前辈们提供了3个形参,不代表3个形参对我们都有用
      					//如果你没有写return,默认return undefined,那一来就false了
      					return 条件;//必写的
      				})
      
    2. some:

      有一些,理论完全等效于||,必须全部不满足结果才为false,只要一个满足,结果就为true

      var bool=arr.some(function(val,i,arr){
      					return 条件;//必写的
      				})
      
  • 遍历:对数组中的每一个元素执行相同 或 相似的操作

    1. forEach:直接修改原数组

      arr.forEach(function(val,i,arr){
      					操作
      				})
      
    2. map:不修改原数组,返回一个新数组

      var newArr=arr.map(function(val,i,arr){
      					return 操作;
      				})
      
  • 过滤和汇总

    1. 过滤:根据你的条件,筛选出你需要的部分,但是不会修改原数组

      var subArr=arr.filter(function(val,i,arr){
      					return 条件
      				})
      
    2. 汇总:把元素汇总到一起

      arr.reduce(function(prev,val,i,arr){//prev - 我们当初取名字的sum,prev为0,不断的做操作就完了
      		return prev+val;
      },基础值);//基础值会和arr累加后的结果再次相加
      
      
  • 其实以上6个API,都是简化了for循环,一切的回调函数再拥有ES6过后都可以简化为箭头函数,以后我们不要再写for循环了,前提的是一个数组

  • Object.create():根据父对象创建子对象,设置好继承

    var 子对象=Object.create(父对象,{
    			"属性名":{四大特性},
    			...
    		});
    
    
  • 严格模式:

    • 开启:在你的任何作用域的顶部加上一句话:"use strict"
    • 功能
      1. 禁止了给未声明的变量赋值 - 解决全局污染
      2. 静默失败升级为了错误
        一旦报错后续代码就不再运行了

call、apply、bind:借用

​ 不是自己的方法也可以用 - 笔试面试都很容易碰到

  • *call/apply:【临时】替换了函数中的this - 借用
    • 语法:
      • 函数名.call(借用的对象,实参,...) - 单独传入每一个实参
      • 函数名.apply(借用的对象,arr) - 只能传入一个实参,要求是一个数组实参,apply会悄悄的打散数组
    • 强调:call/apply,相当于立刻调用函数,立刻执行
  • bind:【永久】替换了函数中的this - 买
    1. 创建了一个和原函数功能完全相同的新函数
    2. 将新函数中的this永久绑定为了指定对象
    3. 将新函数中的部分参数永久固定
  • 固定套路:
    • Math.max/min.apply(Math,arr);
    • Object.prototype.toString.call/apply(x);
    • ***类数组转为普通数组:
      • var 普通数组=Array.prototype.slice.call/apply(类数组对象);
      • ES5还给出了一种方案:var 普通数组=Array.from(类数组对象)

ES6(ES2015)

不光带了新特性,还带来了新语法,都是简化操作

是一个大版本,也是现今市场上最常用的一个版本,更新了很多的新特性(9个)

1、*模板字符串:

可以在字符串中放入变量 - 不需要再做字符串拼接,在字符串中实现了一个简单的js环境

`我的名字叫${name}`

2、*let关键字:

创建变量,以后要【优先】使用let创建变量

  • let 变量名=值;

  • 作用:

    1. 禁止声明提前
    2. 添加了块级作用域 - 一个{}就是一个块!
    3. 记录着当前触发事件的元素的下标!

3、 *箭头函数:简化一切回调函数

  • 口诀:function删掉,在()和{}之间添加=>,形参如果只有一个,可以省略小括号,函数体只有一句话,删除{},函数体只有一句话并且是return,{}和return都删掉

4、 for of循环:垃圾

  • for(var v of arr){
    v;//当前值
    }
  • 缺点:
    • 因为没有下标,无法修改原数组
    • 无法遍历hash数组,也不能遍历对象

5、*****解构赋值:

解析结构,然后进行赋值 - 赋值的新方式,赋值方式得到了增强;

如果等号左右两边的结构长得一样,则会悄悄的脱掉两边的结构,再一一进行赋值;

  • 语法:

    1. 类似数组一般的解构赋值

      • var [a,b,c]=[1,2,3];
        
    2. 类似对象一般的解构赋值 - 传参的顺序其实无所谓了!

      • var {a,b=默认值,c}={b:2,c:3,a:1};
        没传则为默认值,传了肯定用传的新值
        
    3. 常用方法:

      • //常用一:
        				function zwjs({name,age,hobby}){
        					return `我的名字叫${name},今年${age}岁,喜欢${hobby}`;
        				}
        				console.log(zwjs({age:18,hobby:"打游戏",name:"袍哥"}))
        
        				以后但凡你见到:xxx({
        						配置名:值,
        						...
        					           })
        				他的底层就是解构赋值
        				
        
        //常用二:
        				//return的结果可以不止一个
        				function f1(){
        					return ["袍哥","打游戏"];
        				}
        				var [name,hobby]=f1();
        
        //常用三:
        function f1() {
                    // return "yy";
                    // return "xx";
                    return ["yy", "xx"]
                }
                var [val1, val2] = f1()
                console.log(val1, val2);
                
                
                
        //自己封装juery提供的ajax请求:(验证类似对象可以设置默认值;没传则为默认值,传了肯定用传的新值)
        nodejs部分:
        var http = require('http');
        var url = require('url')
        var fs = require('fs');
        const { log } = require('console');
        
        var app = http.createServer()
        app.listen(81)
        
        app.on('request', (req, res) => {
            var objUrl = url.parse(req.url, true);
            var router = objUrl.pathname
            if (router == '/') {
                // console.log(__dirname);
                fs.readFile(__dirname + '/code/html/解构赋值.html', (err, buf) => {
                    res.end(buf)
                })
            } else if (router == '/ajax'){
                console.log(objUrl.query.talk);
                if (objUrl.query.talk == 1) {
                    res.end("[1,2,3]")
                } else if (objUrl.query.talk == 2) {
                    res.end("[4,5,6]")
                } else if (objUrl.query.talk == 3) {
                    res.end("[7,8,9]")
                } else {
                    res.end("[]")
                }
            }
        })
        ------------------------------------
        js部分:
         var $ = {}
                $.__proto__.ajax = ({ type="GET", data, url, success, dataType }) => {  //type:get/post //验证类似对象可以设置默认值;没传则为默认值,传了肯定用传的新值
                    var xhr = new XMLHttpRequest()
                    if (type =="GET") {
                        xhr.open(type, url + '?' + data)
                        xhr.send(null)
                    } else {
                        xhr.open(type, url)
                        xhr.setRequestHeader("Content-Type", "application?x-www-form-urlencoded")
                        xhr.send(data)
                    }
                    xhr.onreadystatechange = () => {
                        if (xhr.status == 200 && xhr.readyState == 4) {
                            if (dataType == "HTML") {
                                success(xhr.responseText)  //回调函数
                            } else if (dataType == "JSON") {
                                success(JSON.parse(xhr.responseText))  //回调函数
                            }
                        }
                    }
                }
        
                $.ajax({
                    data:"talk=1",
                    url:"/ajax",
                    dataType:"JSON",
                    // type:'GET',
                    success:(data)=>{
                        console.log(data);
                    }
                })
        
        

6、Set和Map类型:它们都属于映射类型(类似于数组/对象)

  1. Set:和数组相似,但是没有length,用size获取自己的长度 - 功能:对数组去重,但是set可用的API很少,功能很少,所以还需要再转回数组
    创建:var s=new Set(arr);
    如何set变回arr:[...s];//...用于展开结构 - 扩展运算符
  2. Map:和对象相似,可以为他设置值,也可以读取值 - 毫无卵用
    创建:var m=new Map();

7、*****class关键字:

简化了面向对象(封装、继承、多态)开发 - 学习react框架时(先学好vue,就算学到react也不会原生的敲这些代码,底层有些API带有此功能)

//创建飞行物类
			class flyer{
				constructor(name,speed){//constructor里面放着的就是自有属性
					this.name=name;
					this.speed=speed;
				}//constructor外面的就是原型 - 共有
				fly(){
					return `${this.name}正在以时速${this.speed}飞行`;
				}
			}
			
			//创建飞机类
			class plane extends flyer{//extends不光继承了自有,也继承了共有
				constructor(name,speed,rl){//constructor里面放着的就是自有属性
					super(name,speed);//就算有一万句,我也只写这一句,super方法回自动调用,你extends继承的人的constructor的函数
					this.rl=rl
				}
				fly(//实现多态
					return `${this.name}正在以时速${this.speed}飞行,可以坐${this.rl}人`;
				}
			}
			
			var j20=new plane("歼20",1000,2);
			console.log(j20);
			console.log(j20.fly());
			
			var bird=new flyer("钟清翰",10);
			console.log(bird);
			console.log(bird.fly());

8、***ES6模块化开发:

作用:分工合作,每个js都是一个模块,每个工程师都可以单独开发自己的模块

  1. 子模块要公开
    export var obj={}
  2. 主模块要引入,刚好巧了,Node也可以用这句话,因为Node只是不支持DOM和BOM而已
    import {obj as 别名} from "./子模块路径.js"
    注意:
    1、用了别名,原名就不可以使用了
    2、./和.js是不可以省略不写的
  3. HTML要引入主模块
    <script src="主模块文件" type="module"></script>
  4. 运行时需要用live本地代理服务方式打开,因为import的引入是动态引入。简单的说,import引入的模块,如果更新了,就会实时显示到网页上。live方式运行特点:修改代码时,页面会自动更新,不需要刷新页面

9、*Promise 承诺:

以后你在使用异步代码时,但是也有希望能够保证执行顺序的时候,使用Promise -- 三阶段不会自己写,但是三阶段学习的API会带有此功能

function ajax1(resolve){//渲染头部
				setTimeout(()=>{
					console.log("11111111111111111111111111111111");
					resolve();//放行
				},Math.random()*5000);
			}
			
			function ajax2(){//渲染身体
				return new Promise((resolve)=>{
					setTimeout(()=>{
						console.log("22222222222222222222222222222222");
						resolve();//放行
					},Math.random()*5000);
				})
			}
			
			function ajax3(){//渲染底部
				setTimeout(()=>{
					console.log("33333333333333333333333333333333");
				},Math.random()*5000);
			}
			
			new Promise(ajax1).then(ajax2).then(ajax3);//是不是5秒?每次都是重新开始的5s内 
			
			//希望,异步(不要卡住后续代码),但是又要保证顺序(1、2、3)
			
			console.log("后续代码");

BOM:Browser Object Model(浏览器对象模型)

专门提供了用于操作浏览器的API - 没有标准,不像DOM由W3C来制定标准,但是大部分浏览器已经统一实现了,唯独老IE与众不同,相当于DOM使用也会较少

(BOM之中只有两个重点:定时器和event,BOM存在大量的兼容性问题)

window对象

  • 扮演着两个角色:
  1. 在前端/浏览器端,他代替了全局对象global,保存着全局变量和全局函数
    window.变量名;
    window.函数名(); - 只不过window可以省略不写

  2. 指代当前窗口本身

    目的:优化用户的体验感,多为用户考虑

    • 网页打开新链接的方式:
      1. 替换当前页面,可后退
        HTML:<a/ href="url">内容
        JS:open("url","_self");
      2. 替换当前页面,禁止后退:使用场景:结账后不允许后退
        history对象:保存着当前窗口打开过的历史记录url,只有产生了窗口历史才能前进后退
        location对象:保存着当前窗口正在打开的url
        JS:location.replace("url") - 替换网址后页面肯定变化,但是替换并不叫跳转,不会产生任何的历史纪录
      3. 在新窗口打开,可以打开多个
        HTML:<a/ href="url" target="_blank">内容
        JS:open("url","_blank");
      4. 在新窗口打开,只能打开一个:使用场景:打开结账页面时
        HTML:<a/ href="url" target="自定义name">内容
        窗口的底层其实是有名字的,如果重新打开相同的名字,新打开的窗口会替换掉旧的窗口
        JS:open("url","自定义name");
      5. 扩展:a标签的其他用处:
        1. 跳转
        2. 锚点
        3. 下载:<a href="xx.exe/zip/rar">下载</a>
        4. 打开:<a href="xx.txt/jpg...">图片/文本</a>
        5. 直接执行js操作:<a href="javascript:js代码;">图片/文本</a>

window窗口的属性和方法:

  • 属性:

    • 获取大小:
      1. 获取浏览器的完整的大小:outerWidth/Height
      2. *获取浏览器的文档内容显示区域的大小:innerWidth/Height
      3. 获取完整屏幕的大小:screen.width/height //没用,我们并不做桌面应用
      4. 获得页面内容的完整长度document.body.scrollHeight=document.body.offsetHeight=document.body.clientHeight
  • 方法:

    1. 打开新窗口:var newW=open("url","自定义name","width=?,height=?,left=?,top=?");
      //第三个参数如果没传入,那么新窗口大小和位置会与浏览器融为一体
      //第三个参数如果传入,新窗口会脱离浏览器独立成为一个小窗口

    2. 关闭新老窗口:window/newW.close();
      //以下两个操作:仅仅只能对脱离浏览器的新窗口可用

    3. 移动新窗口:newW.moveTo(x,y);

    4. 改变新窗口的大小:newW.resizeTo(new宽,new高);

    5. *定时器:

      何时使用:只要过一段时间就希望执行的特效或操作,必须使用定时器

      • 周期性:只要不停止会等待时间,反复不断的执行
        开启:timer=setInterval(callback,间隔毫秒数);
        停止:clearInterval(timer);
      • 一次性:等待时间后只会执行一次,就结束
        开启:timer=setTimeout(callback,间隔毫秒数);
        停止:clearTimeout(timer);
      • 其实以后你不会是周期性还是一次性都无所谓,因为两者的底层是一样的,用哪个停止其实无所谓,甚至可以互换!
    6. 扩展:

      ***获取鼠标的位置:2步

      1. 获取事件对象event:在事件函数中传入一个形参e即可,自动接收到event对象
      2. 获取鼠标的位置:
        1、鼠标相对于屏幕的位置:e.screenX/Y
        2、鼠标相对于文档显示区域的位置:e.clientX/Y
        3、*鼠标相对于网页的位置:e.pageX/Y

history对象

封装着当前窗口打开过的历史记录url。

  • 前进:history.go(1);
  • 后退:history.go(-1);
  • 刷新:history.go(0);
  • 垃圾:浏览器自带此功能,没必要

navigator对象

封装着当前浏览器的基本信息,判断此浏览器是什么浏览器以及版本号。

  • navigator.userAgent; -想办法截取你需要的部分

    • 垃圾:说白了专门为判断是不是IE存在的,但是开发效率慢远远不如css hack来的简单(其实现在做兼容越来越少了:1、微软不推荐我们用IE 2、流行移动端,移动端不存在IE)

    • 补充:css hack

      • <!--[if lte IE 8]>    小于ie8 才引用下面的css
        			<link rel="stylesheet" type="text/css" href="css/ie.css"/>	
        		<![endif]-->
        
        

*location对象

封装着当前窗口正在打开的url。

  • *常识:面试时:一个url由几部分组成?

    https://www.baidu.com/s?wd=%E6%9D%A8%E9%91%AB&rsv_spt=1&rsv_iqid=0xc099ebd40001a15e&issp=1&f=8&rsv_bp=1&rsv_idx=2&ie=utf-8&tn=02003390_82_hao_pg&rsv_dl=tb&rsv_enter=1&rsv_sug3=9&rsv_sug1=4&rsv_sug7=100&rsv_sug2=0&rsv_btype=i&inputT=9283&rsv_sug4=10043
    (https协议)://(www.baidu.com 域名):(443端口号)(/s 相对路径)?(wd=%E6%9D%A8%E9%91%AB&rsv_spt=1&rsv_iqid=0xc099ebd40001a15e&issp=1&f=8&rsv_bp=1&rsv_idx=2&ie=utf-8&tn=02003390_82_hao_pg&rsv_dl=tb&rsv_enter=1&rsv_sug3=9&rsv_sug1=4&rsv_sug7=100&rsv_sug2=0&rsv_btype=i&inputT=9283&rsv_sug4=10043 请求信息)
    
    
    http://127.0.0.1:8020/bom02/new/02%E5%88%A4%E6%96%AD%E6%B5%8F%E8%A7%88%E5%99%A8%E4%BB%A5%E5%8F%8A%E7%89%88%E6%9C%AC%E5%8F%B7.html?__hbt=1668478029615
    (http 协议)://127.0.0.1 域名):(80 端口号)20/(bom02/new 相对路径)/(02%E5%88%A4%E6%96%AD%E6%B5%8F%E8%A7%88%E5%99%A8%E4%BB%A5%E5%8F%8A%E7%89%88%E6%9C%AC%E5%8F%B7.html?__hbt=1668478029615 请求信息)
    
    • 五部分:协议://域名:端口号/文件相对路径?前端告诉后端的话
      1. 协议: https/http/ftp/ws... - 固定的,我们想要免费肯定用http
      2. 域名/主机号/IP: 域名/主机号/IP:域名是需要花钱购买的,ip是免费的,而且自己访问自己可以用主机号127.0.0.1
      3. *端口号: 默认端口号可以省略不写:https默认端口为443,http默认端口为80 (域名冒号 后面的内容)
      4. *文件相对路径/路由:百度加密了,意味着你现在看到的是哪个页面
      5. *查询字符串/请求消息: 前端提交到后端的东西,前端说的话 (?后面的内容)
  • *属性:其实一这块根本不需要记单词属性,你只需要打开控制台输出location对象即可查看

    1. protocol - 协议
    2. hostname - 域名/主机号/IP
    3. port - *端口号
    4. pathname - *文件相对路径/路由
    5. search - *查询字符串/请求消息
  • 方法:

    1. 跳转:location="新url";
    2. 跳转后禁止后退:location.replace("新url");
    3. 刷新:location.reload()

event: 事件对象

用户手动触发的 或 浏览器自动触发的事件

绑定事件:3种
  1. 在html标签中定义处理属性:

    <elem on事件名="js语句"></elem>

    • 缺点:

      1. 不符合内容(HTML)和行为(JS)的分离
      2. 无法动态绑定事件 - 一次只能绑定一个元素
      3. 无法同时绑定多个函数对象
  2. *在JS中使用元素的事件处理函数属性

    elem.on事件名=function(){}

    • 优点:

      1. 符合内容(HTML)和行为(JS)的分离
      2. 动态绑定事件
      3. 没有兼容性问题
    • 缺点:

      1. 无法同时绑定多个函数 - 个人认为不是缺点,我们可以把多个函数汇总为一个函数
  3. 使用API绑定事件

    主流:elem.addEventListener("事件名",callback);
    老IE:elem.attachEvent("on事件名",callback); 
    兼容:
    if(elem.addEventListener){
    	elem.addEventListener("事件名",callback);//老ie不认识箭头函数,因此要用回调函数
    }else{
    	elem.attachEvent("on事件名",callback); 
    	elem.attachEvent("on事件名",callback); //会先执行后绑定的事件
    }
    
    • 优点:
      1. 符合内容(HTML)和行为(JS)的分离
      2. 动态绑定事件
      3. 同时绑定多个函数对象
    • 缺点:
      1. 存在兼容性问题 - 导致代码量巨大
*事件周期:

从事件发生到事件结束的全过程

  1. 捕获阶段:由外向内:记录着要执行的事件有哪些
  2. 目标触发:目标元素:实际发生事件的元素
  3. 冒泡阶段:由内向外执行
*获取事件对象event:
主流:在事件处理函数中传入一个形参,就会自动得到event对象了
老IE:window.event;
兼容:var e=event;
这是我们第二次见到小三上位,老IE的操作,主流也可以使用了,建议:如果不考虑老IE,明显写一个形参e更简单

*****事件对象event可以干什么?

  1. 获取鼠标位置

    • 鼠标相对于屏幕的位置:e.screenX/Y
    • 鼠标相对于文档显示区域的位置:e.clientX/Y
    • *鼠标相对于网页的位置:e.pageX/Y
  2. 阻止冒泡:面试/笔试

    主流:e.stopPropagation();   propagation传播
    老IE:e.cancelBubble=true;   cancel取消  bubble泡泡
    兼容:这是我们第三次见到小三上位,老IE的操作,主流也可以使用了
    divs[i].onclick=function(){
    					var e=event;
    					alert(1);
    					e.cancelBubble=true;
    				}
    
  3. *****事件委托:

    • 优化:如果多个子元素定义了相同的事件处理函数,最好只给父元素定义一次

    • 为什么:每次绑定一个事件处理函数,其实就创建了一个事件监听对象,创建的监听对象越多,那么网页的效率就越低下

    • 获取目标元素:【实际】触发事件的元素

      主流:e.target;
      老IE:e.srcElement;
      兼容:这是我们第4次见到小三上位,老IE的操作,主流也可以使用了
      
    • 优点:

      1. 事件只需要给父元素绑定一次,搭配上nodeName/className等判断则无敌
      2. 不用担心冒泡了,我们就是要利用冒泡
      3. 再也不用使用this这个【当前对象】了,【目标对象】更舒服
  4. 阻止浏览器的默认行为:

    比如:a标签href默认刷新,submit按钮默认提交,比如F5刷新页面,比如F12打开控制台,比如阻止右键的弹出菜单

    主流:e.preventDefault();
    老IE:e.returnValue=false;
    兼容:e.returnValue=false; //不光老IE可用,主流也可以用 - 小三上位 
    
  5. 获取键盘的键码

    • e.keyCode - 不需要你记忆:要么console输出看,要么百度搜索
  6. 补充事件:

    • window.oncontextmenu - 右键菜单事件
    • window.onkeyDown - 键盘按下事件
      • 回车提交表单,Esc返回等快捷键操作—提高用户体验

ES7:

市场上最流行的es6(es2015)- es6是2015年推出的,超级大版本,2015年过后,ECMA组织像吃了春药一样,疯狂的在更新,几乎每年一个版本,但是又没带来多少东西

学了ES7-13有什么用?装逼,你写的代码,别人不一定看得到,但是功能他也会,语法不同。

1、Array API:includes

ES7之前,如果我们想要判断一个数组中是否包含某个元素,通过 indexOf获取结果,判断是否为-1ES7之后,我们可以通过includes来判断一个数组中是否包含一个指定元素

			var arr=[10,20,30];
			
			console.log(arr.indexOf(30)!=-1);//true
			console.log(arr.indexOf(40)!=-1);//false
			
			console.log(arr.includes(30));//true
			console.log(arr.includes(40));//false

2、*指数/乘方运算符:

在ES7之前,如果我们想要计算数字的乘方:Math.pow(底数,幂);
在ES7之后,我们可以通过**运算符,可以实现乘方
		Math.pow(8,6);
		8**6;

ES8:

1、Object.values(obj);对象=>数组

ES8之前,我们想要获得一个对象里所有的属性值,我们只能for in循环遍历对象
 在ES8之后,提供了Object.values(obj),直接把你所有的属性值保存在一个数组之中。
		var wsc={
				"name":"王思聪",
				"age":40,
				"hobby":"吃屎"
			}
			
			wsc=Object.values(wsc);//["王思聪",40,"吃屎"] - 你想用什么循环都无所谓

2、将对象变成有一个二维数组,同时保存着键(下标0)和值(下标1)

	Object.entries(obj)
		var wsc={
			"name":"王思聪",
			"age":40,
			"hobby":"吃屎"
		}
		
		var arr=Object.entries(wsc);
		//输出arr为:[["name","王思聪"],["age",40],["hobby","吃屎"]]

3、String Padding

某些字符串我们需要对其进行前后的填充,来实现某种格式化效果
		var str="hello";

		//在str前面拼接了字符串a,总共填充到15位
		var newStr1=str.padStart(15,"a");
		//在str后面拼接了字符串a,总共填充到15位
		var newStr2=str.padEnd(15,"a");
		
		console.log(str);
		console.log(newStr1);
		console.log(newStr2);

应用场景:比如时间是一位数的时候,需要在前面填充一个

4、任何代码(数组,对象),尾部逗号的添加,都不会导致出错

5、***async 和 await:

async是一个关键字放在函数前面,用于表示函数是一个异步函数,异步函数也就意味着该函数的执行不会阻塞后续代码
		async function f1(){
			return "我是f1函数";
		}
		f1().then(result=>{
			console.log(result);
			console.log("f1函数执行完毕后要干什么")
		})
		console.log("后续代码");

		意味着任何函数都可以变为异步函数


await是等待的意思,他在等什么?他后面跟着什么?其实他后面可以放任何表达式,不过我们更多的是放Promise对象
	注意:await关键字只能放在async的函数之中
		async function f1(){
			return 100;
		}
		async function f2(){
			return 200;
		}
		async function f3(){//f3必须要等待f1和f2
			return await f1()+await f2();
		}
		f3().then(rs=>{
			console.log(rs)
		})
		
		console.log("后续代码");

		同时出现多个异步函数,f3异步函数希望使用f1和f2的结果,可以用上await进行等待

ES9:

1、*扩展运算符:

	...其实ES6就已经引入了,但是仅仅只有【数组可用】 - 脱/穿衣服,关键就看有没有数组外套,如果有就是脱,如果没有就是穿
		function f1(a,b,...c){
			console.log(a);//1
			console.log(b);//2
			console.log(c);//[3,4,5]
		}
		f1(1,2,3,4,5)

	ES9对象也可以使用了:
		var obj={
			a:1,
			b:2,
			c:3,
		}
		
		var {a,...x}=obj;
		
		console.log(a);//1
		console.log(x);//{b:2,c:3}

	数组深拷贝(slice),那对象深拷贝怎么办?
		var obj1={
			a:1,
			b:2,
			c:3,
		}
		var obj2={...obj1};//对象深拷贝,脱了衣服再穿一个新衣服
		obj2.a=100;
		console.log(obj1)
		console.log(obj2)
//关于(arr、obj)深浅拷贝的区别
			var arr1=[1,2,3,4];
			// var arr2=arr1   //浅拷贝, 这时改变arr2;arr1也会改变
			var arr2=arr1.slice(); //深拷贝
			
			arr2.length--;
			
			console.log(arr1);
			console.log(arr2);
// -----------------------------------------------------
			var obj1={a:1,b:2};
			// var obj2=abj1  //浅拷贝, 这时改变obj2;obj1也会改变
			var obj2={...obj1};  //深拷贝
			
			obj2.a=100;
			
			console.log(obj1);
			console.log(obj2);

2、正则表达式-命名捕获组

		var str="500103198602215933";//格式化:1986年2月21日
			
		var reg=/\d{6}(\d{4})(\d{2})(\d{2})\d{4}/;
			
		str=str.replace(reg,(a,b,c,d,e,f)=>{
				return b+"年"+c+"月"+d+"日"
		})
			
		console.log(str);
//代码很难读懂,并且改变正则而表达的结构有可能匹配到的对象有所改变,ES9允许命名捕获组使用?<自定义name>
		var str="500103198602215933";//格式化:1986年2月21日
		
		var reg=/\d{6}(?<年>\d{4})(?<月>\d{2})(?<日>\d{2})\d{4}/;
		
		var rs=reg.exec(str);//查找,支持正则,既可以得到下标,又可以得到所有的内容
		
		console.log(rs.groups.年);
		console.log(rs.groups.月);
		console.log(rs.groups.日);

ES10:

1、flat() 方法会按照一个可指定的深度,递归遍历数组,并将所有元素与遍历到的子数组的元素合并为一个新数组

	例如:一个数组中有很多层,第一层数组中还有第二层,第三层..nvar arr=[10,20,[30,40],[50,[60]],[70,[80,[90]]]];
	 //将数组里面前三层数组进行了扁平化
	var newArr=arr.flat(3);
	console.log(newArr);//[10,20,30,40,50,60,70,80,90]

2、flatMap() 方法首先会映射函数映射每个元素,然后将结果压缩成一个新数组

	注意一:flatMap是先map操作,再做flat操作
	注意二:flatMap的flat相当于深度为1

		var arr=["hello world","hello coder","你好 世界"];
		var arr2=arr.flatMap(str=>str.split(" "));//即带有map遍历操作,又带有flat扁平化操作(不能设置扁平化的级别,只能固定为1层)
		console.log(arr2);

3、trimStart/trimEnd

	ES10之前去掉一个字符串首尾的空白,我么们可以通过trim方法,如果单独去掉前面或后面?
	ES10为我们提供了trimStart/trimEnd

ES11:

全局对象全新写法
比如在浏览器中可以通过过window来获取
比如在Node服务器端中可以通过global来获取
在ES11中对获取全局对象进行了统一的规范:globalThis

for...in循环标准化
在ES11之前,虽然很多浏览器支持for...in来遍历对象类型,但是并没有被ECMA标准化

ES12:

逻辑赋值运算符
		function f1(a){
				a||="默认值";
				console.log(a);
			}	
			f1("我是新值");
			
			var obj={
				"name":"钟sir"
			}
			
			obj&&=obj.name;
			
			console.log(obj);

ES13 :

at() - 获取字符串、数组的元素,跟下标差不多,但是他支持负数参数
		var arr=[1,2,3,4,5];
		
		console.log(arr[0]);
		console.log(arr[arr.length-1]);
		
		console.log(arr.at(0));
		console.log(arr.at(-1));
		
		var str="hello";
		console.log(str.at(0));
		console.log(str.at(-1));

posted on   Elementinner  阅读(52)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

导航

统计

点击右上角即可分享
微信分享提示