JS汇总一

240 阅读54分钟

JavaScript介绍

JavaScript是一个运行在 浏览器端解释型弱类型面向对象 脚本语言。

由三部分构成:ECMAScript(核心语法3/5/6/7/8/9/10/11/12) + DOM和网页关联) + BOM(和浏览器关联)

一、概念

1.1 浏览器端:

环境,自带JavaScript解释器——打开浏览器就可以自动运行
在学习Node.js的时候,会安装一个独立的js解释器

1.2 解释型:

在程序执行之前,不需要先检查语法是否正确,直接运行,碰到错误就停止,比如:js、php...
编译型:
在程序执行之前,需要先检查语法是否正确,如果语法错误,则不运行。相对于解释型比较严格,比如:Java、C#...

1.3 弱类型:

变量保存的数据是可以随意的,数据类型由数据来决定,比如:JavaScript
强类型:
变量保存的数据,是由数据的类型决定的,比如:Java

1.4 面向对象:

很难。面向对象式的写法:对象名.属性名;对象名.方法名();

1.5 特点:

  • 可以用一切编辑工具编写JS代码,编辑器的不同并不代表实力的大小
  • 解释型
  • 弱类型
  • 面向对象
  • 可以做一切css完成不了的效果,比如:轮播、选项卡、购物车、验证...

二、如何使用js

2.1 使用方式:(2种)

  1. 在HTML页面上书写一个script标签,然后在里面书写代码 —— 一般不使用此方法
    <script>js代码</script>
  2. 创建一个xx.js的文件,然后在HTML里面引入 —— 正式开发
    <script src='js路径'>此时不能在这里写代码</script>

2.2 输出方式/打桩输出:(3种)

作用:辅助我们检查代码错误

  1. 在控制台输出日志
    console.log(想要输出的内容);//console - 控制台,log - 日志
    推荐:不会影响到页面的内容
  2. 在页面上输出日志:
    document.write(想要输出的内容);//document - 文档,write - 写。
    支持识别标签,但不推荐:如果搭配点击事件,会把页面上原有的东西全部替换
  3. 在弹出框输出日志:
    alert(想要输出的内容);//警告框
    一般般:有时候会卡住整个页面,导致用户只能看到一个白板

2.3 变量:创建后,可以再次修改

  1. 何时使用:反复用到的数据,提前保存到一个变量中,后续使用变量名就相当于在使用变量的值
  2. 语法:var 变量名=值;
  3. 特殊:
    1. 变量名是不可以随意的: a.不能以数字开头 b.建议驼峰命名法 c.命名尽量见文知意:
    2. 如果你的变量名是name,不管你保存的数据类型是什么,都会默认转为一个字符串
    3. 变量名不能是关键字
    4. 变量可以只创建,不赋值,默认值为undefined,undefined不能拿来做任何操作
    5. 如果多个变量连续创建,中间用,隔开;

2.4 常量:创建后,不能修改

  1. 语法:const 常量名=值;
  2. 特殊:见上文变量的特殊

2.5 数据的类型:(两大类)

  1. 原始/基本/值类型:(5个)
    (1)number - 数字:取值有无数个,数字直接写,不需要加任何东西,颜色是蓝色
    (2)string - 字符串:取值有无数个,写的时候需要加英文状态下的单双引号(" "或' '),颜色是黑色
    (3)boolean - 布尔:取值只有2个,true/false - 一般用于当做条件判断,颜色是蓝色
    (4)null - 空,取值只有1个,null,作用是释放变量释放内存,节约空间,提升网页的性能,颜色是灰色
    (5)undefined - 取值只有1个,undefined做任何操作都不行,颜色是灰色
  • 查看数据类型:typeof(变量);
  1. 引用对象类型:(11个)
    可以理解为有11个对象(属性和方法)

2.6 运算符:(6种)

  1. 算术运算符:+ - * / %
    (1)%:取余,俗称模,两数相除,取余数
      作用:a.判断奇偶,任意数%2;
        b.取出某个数字的后n位:1234%10 ==> 4, 1234%100 ==> 34
    (2)带有隐式转换:会默认将数据类型转化为数字再进行运算:
       true => 1,false => 2,undefined => NaN,null => 0
      "10" => 10,"1000px" => NaN,带单位的要用强制转换
    (3)NaN:Not a Number,不是一个数字,但它属于数字类型,不是有效数字
       缺点:a.参与任何算术运算结果为NaN
        b.参与任何比较运算结果都是false
          解决:!isNaN()判断是否是数字,true是数字,false不是数字

  2. 比较运算符:> < >= <= == != === !==
    结果:是布尔值
    带有隐式转换:默认两边转为数字再比较
    undefined==null为true
         === 全等,不带隐式转换的
         !== 不等,不带隐式转换的

  3. 赋值运算符:= += -= *= /= %=
    例如:i+=1 --> i=i+1

  4. 逻辑运算符:|| && !
    或=>|| 与=>&&
    非=>! 颠倒布尔值

  5. 自增自减运算符: ++ --
    i++ ==> i=i+1 ==> i+=1
    前++ 和 后++的区别:
       单独使用没有任何区别
       如果是等式运算,则返回的结果不同:
          前++返回的是加了之后的值
          后++返回的是未加的值

  6. 位运算
    左移:m<<n,读作m左移了n位,m*2的n次方
    左移:m>>n,读作m右移了n位,m/2的n次方
    缺点:底数只能是2

2.7 用户输入框:var 变量=prompt('提示文字');

三、分支结构

通过条件判断,选择部分代码执行

3.1 if...else...

三种写法:
1.一个条件,一件事,满足就做,不满足就不做
    if(条件){
        操作;
      }
2.一个条件,两件事,满足就做第一件,不满足就做第二件
    if(条件){
        操作;
      }else{
        操作;
      }
3.一个条件,多件事,满足哪个条件就做对应的操作
    if(条件){
        操作;
      }else if{
        操作;
      }else if{
        操作;
      }else{
        操作;
      }
  1. else if想写多少就写多少,由程序员决定
  2. else默认操作可以不写,但不建议,如果条件都不满足则什么都没有发生
  3. 分枝走了一条路就不会走其他的路

3.2 switch...case...

switch(变量/表达式){
        case 值:
        case 值:
        case 值:
        操作;
        break;
        case 值:
        操作;
        break;
        default:
        操作;
    }
  1. 默认只要一个case满足后,会将后续所有的操作全部做完。
    解决:在每一个操作后面添加break; default后面不用跟。

  2. default可以不写,但是不推荐不写,如果条件都不满足的时候,则什么都不会发生,什么都不发生就感觉这段代码还不如不写

  3. case在做比较的时候是不带隐式转换的

3.3 if和switch的区别(面试题)

  1. switch...case:
    优点:执行效率高,速度快(case做比较时,case做的不是范围查找,而是等值比较
    缺点:必须要知道最后的结果是什么才可以使用

  2. if...else...:
    优点:可以做范围判断,也可以做等值比较
    缺点:执行效率较慢,速度慢(因为是范围查找)

  • 建议:代码开发结束后,做代码优化,尽量少用if...else...,多用switch...case和三目运算

3.4 三目运算(简化分支)

语法: 条件?操作:默认操作; ==>if...else

  • 条件和操作可以有多个,默认操作只有一个,且默认操作必须写
    比如:条件?操作1:条件?操作2:条件?操作3:默认操作; ==> if...else if...else if...else

  • 如果条件和操作太多,不建议使用三目运算,还是推荐 if 或 switch

  • 注意:默认操作不能省略,省略了会报错的

四、强制转换数据的类型

解决隐式转换解决不了的,例如:"100px"*2=NaN
页面上一切数据,js获取到都是字符串类型

4.1 转数字

  1. parseInt(str/num); 主要用于字符串转整数
      识别规则:从左往右依次识别数字字符,碰到第一个不是数字的字符串就停止,如果第一个不是数字字符串,则为NaN,不认识小数点

  2. parseFloat(str); float浮点数,主要用于字符串转小数
      识别规则:只认识第一个小数点,其余与整数相同

  3. Number(x); 万能公式,任何东西都可转换为数字类型,绝对不要手动转换,等同于隐式转换 -0 *1 /1,减0,乘1,除1

4.2 转字符串

  1. var str=x.toString(); x不能是undefined和null,会报错,他们不是对象,不能使用.做任何操作

  2. var str=String(x); 万能的,绝对不要手动转换,等同于隐式转换 +,加

4.3 转布尔:

  • Boolean(x); 万能的,任何东西都可以转布尔,绝对不要手动转换,完全等同于隐式转换,不如 !!x

  • 只有6个为false:false 0 undefined NaN null 空引号""

  • 除了这6个,剩下的都是true

五、循环结构

循环结构:反复执行相同或相似的操作

循环三要素:
  1、循环条件:开始-结束,循环的次数
  2、循环体:做的操作是什么
  3、循环变量:记录着当前循环到哪一次了,会不断地变化

循环都是一次一次执行的

5.1 for循环

语法:
    for(i=0;i<5;i++){
        console.log(i);
    }

执行原理:与while循环的原理相同
for循环的语法比while循环的语法看上去更简洁,更舒服

5.2 while循环

语法:
    var i=0;
    while(i<5){
        console.log(i);
        i++;
    }
  • 执行原理:首先创建出循环变量,判断循环条件,如果条件满足,则做一次循环体操作,并不会退出循环,而会回过头再次判断循环条件满不满足,如果满足,则做一次循环体操作,...直到条件不满足,才会退出循环
  • while 和 for的区别(面试题):
    while和for在原理上几乎没有区别
    一般来说不确定循环次数的时候,会使用while循环- 死循环
    一般来说确定循环次数的时候,会使用for循环 - 大部分情况

5.3 do...while循环(不常用)

语法:
    var i=0;
    do{
        console.log(i);
        i++;
    }while(i<5)
  • while和do...while的区别(面试题)
      区别只在第一次,如果第一次条件满足,则没有任何区别,但是如果第一次条件不满足,while循环一次都不会做,do...while至少执行一次。根据代码从上到下的运行规则,可以看出,do...while循环是先执行里面的循环体,再判断,而while循环是先判断,再执行。

5.4 死循环

  • while(true){}
  • for(;;){}
  1. 死循环:默认永远不会停下来的循环
     何时使用:不确定循环次数的时候:while(true){死循环}

  2. 让死循环停下来:
     break; - 退出整个循环,大多是用来搭配死循环的
     continue; - 退出本次循环,下一次依然会执行

六、Function函数

Function 称为函数,又称为方法,先预定义好,以后可以反复使用的代码段

6.1、Function基础

6.1.1 如何使用函数:(2步)

  1. 定义/创建/声明:
    function 函数名(){函数体/代码段;}
    注意:函数创建后,不会立即执行,需要调用函数才能执行
  2. 调用函数:(2种)
      (1)在js内部写:函数名(); ——程序员写几次就调用几次
      (2)在HTML上绑定事件:<elem onclick='函数名'></elem>————什么元素都可以绑定事件

6.1.2 何时使用:

  1. 不希望打开页面立刻执行
  2. 希望用户来触发提升用户的体验感
  3. 以后每一个独立的功能都要封装为一个函数
  • 函数在js里的地位极高,函数是js的第一等公民

6.1.3 创建出带有形参的函数:

形参:其实就是一个变量,只不过不需要加var、不需要赋值,所以称之为形式参数 - 简称形参

  1. 语法: function 函数名(形参,...){函数体/代码段;}
  2. 使用带有形参的函数时,必须传入实参 - 实际参数
     调用函数:函数名(实参,...)
    注意:传参时顺序不能乱,必须和形参的顺序一一对应,数量不多不少
  3. 总结:
    a、不带参数的函数:用于执行一些固定操作
    b、带参数的函数:可以根据我们传入的实参的不同,做的略微不同

6.1.4 函数和循环的区别:

  1. 循环:几乎是一瞬间就完毕了
  2. 函数:需要调用后才会执行

6.2 自定义函数Function

函数:需要先定义好,以后可以反复使用的一个代码段

何时使用:不希望打开页面立刻执行,以后可以反复使用,希望用户来触发...

6.2.1 函数的创建:(2种)

  1. 声明方式 创建函数:
    function 函数名(形参列表){代码段;return 返回值/结果;}
  2. 直接量方式 创建函数:--不推荐
    var 函数名=function(形参列表){代码段;return 返回值/结果;}

6.2.2 函数的调用:

var 变量名=函数名(实参列表)

  1. return的本意是退出函数,但是如果return后面跟着一个数据,顺便将数据返回到函数作用域的外部,但是return只负责返回,不负责保存,所以调用函数时要自己拿个变量来接住他

  2. 就算省略return,默认也有,会return一个undefined

  3. 具体需不需要得到函数的结果,看你自己:如果有一天你在全局希望拿着函数的结果去做别的操作,那么记得加return

  4. 前辈们提供的方法,大部分基本上都加了return

6.2.3 作用域:(2种)

  1. 全局作用域:
    全局变量 和 全局函数,在页面的任何一个位置都可以使用

  2. 函数作用域:
    局部变量 和 局部函数,在当前函数调用时,内部可用

  3. 带来了变量的使用规则:
    优先使用局部的,局部没有找全局要,全局也没有那就会报错
    特殊点/缺点:
      1、千万不要在函数中对着未声明的变量直接赋值 - 全局污染:全局本身没有这个东西,但是被函数作用域给添加上了
      2、局部可以使用全局的,但是全局不能使用局部的 - 解决:return

6.2.4 声明提前:(鄙视题)

规则:在程序正式执行之前,将var声明的变量(轻)和function声明的函数(重),都会悄悄的集中定义在当前作用域的顶部,但是赋值留在原地

  • 强调:声明方式创建的函数会完整的提前直接量方式创建的函数不会完整提前,只有变量名部分会提前,赋值留在原地

  • 何时使用:永远不会自己使用,会干扰我们判断 - 只会在笔试中遇到

  • 为什么平时开发不会遇到? 只要你遵守以下规则: 1、变量名和函数名尽量不要重复 2、先创建后使用

6.2.5 重载:

  • 概念:相同的函数名,根据传入的实参的不同,自动选择对应的函数去执行,但是JS不支持,函数名如果重复了,后面的肯定会覆盖掉前面的

  • 目的:减轻我们程序员的压力,记住一个方法就可以执行很多的操作

  • 解决:在【函数内部】自带一个arguments的对象(类似数组对象):不需要我们去创建 - 哪怕没有写任何形参,他也可以接受住所有实参,所以他默认length长度为0
      固定套路: 1、通过下标去获取传入的某一个实参:arguments[i] —— i从0开始。 2、通过length去获取到底传入了几个实参:arguments.length

  • 通过判断传入的实参的不同,在内部去写判断,从而变相的实现重载

七、DOM

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

7.1 DOM树概念:

  • DOM将HTML看作是一个倒挂的树状结构,但是树根不是html标签,而是document的对象

  • document对象:不需要我们程序员创建,由浏览器的js解释器自动创建,一个页面只有一个document树根
     作用:可以通过树根找到我们想要的任何一个DOM元素/节点/对象(属性和方法)

  • DOM会将页面上的每个元素、属性、文本、注释等等都会被视为一个DOM元素/节点/对象

7.2 查找元素:(两大方面)

7.2.1 直接通过HTML的特点去查找元素

  1. 通过 ID 查找元素:
    var elem=document.getElementById("id值");
    特殊:
      1、返回值,找到了返回当前找到DOM元素,没找到返回的一个null
      2、如果出现多个相同id,只会找到第一个
      3、记住控制台输出的样子,这个样子才叫做一个DOM元素/节点/对象,才可以下午去做操作
      4、忘记此方法,不允许使用,id不好用,一次只能找一个元素。id留给后端用
      5、其实根本不用查找,id直接可用

  2. 通过 标签名 查找元素:
    var elems=document/已经找到的父元素.getElementsByTagName("标签名");
    特殊:
      1、返回值,找到了返回一个类数组DOM集合,没找到得到空集合
      2、js只能直接操作DOM元素,不能直接操作DOM集合,解决:要么下标拿到某一个,要么遍历拿到每一个。
      3、不一定非要从document开始查找,如果从document去找,会找到所有的元素,可以换成我们已经找到的某个父元素

  3. 通过 class 查找元素:
    var elems=document/已经找到的父元素.getElementsByClassName("class名");
    特殊:与标签名相同

7.2.2 通过关系去获取元素:

  • 前提条件:必须先找到一个元素才可以使用关系`
  1. 父元素:elem.parentNode; - 单个元素
  2. 子元素:elem.children; - 集合
  3. 第一个子元素:elem.firstElementChild; - 单个元素
  4. 最后一个子元素:elem.lastElementChild; - 单个元素
  5. 前一个兄弟:elem.previousElementSibling; - 单个元素
  6. 后一个兄弟:elem.nextElementSibling; - 单个元素
  • 为什么要通过关系去找元素呢?
    不希望影响到别人,只希望影响到自己的关系网

7.3 操作元素:(3方面)

前提:先找到元素,才能操作元素

7.3.1 操作元素的内容:

  1. elem.innerHTML - 获取和设置开始标签到结束标签之间的内容,支持识别标签的
    获取:elem.innerHTML;
    设置:elem.innerHTML="新内容";

  2. elem.innerText - 获取和设置开始标签到结束标签之间的纯文本,不识别标签的
    获取:elem.innerText;
    设置:elem.innerText="新内容";

  • 以上两个属性都是专门为双标签准备,而有一个单标签也是可以写内容,叫做,我们如何获取?
  1. input.value - 专门获取/设置input里的内容
    获取:input.value;
    设置:input.value="新内容";

7.3.2 操作元素的属性:

获取属性值:elem.getAttribute("属性名");
设置属性值:elem.setAttribute("属性名","属性值");

简化版
  获取属性值:elem.属性名;
  设置属性值:elem.属性名="属性值";
简化版的缺点:
  1、class必须写为className - ES6(2015年)class变成了一个关键字
  2、不能操作自定义属性

7.3.3 操作元素的样式:

使用样式的方式:(3种)
  1、内联样式
  2、内部样式表
  3、外部样式表 - 一阶段做开发用的都是外部样式表

二阶段用js来操作【内联样式
  1、不会牵一发动全身
  2、优先级最高

获取样式:elem.style.css属性名;
设置样式:elem.style.css属性名="css属性值";
特殊点
  1、css属性名,有横线的地方,去掉横线,变为小驼峰命名法:
     border-radius -----> borderRadius
  2、小缺陷:获取时,我们只能获取到内联样式,因为我们目前学的就是内联样式的操作

7.3.4 绑定事件:

elem.on事件名=function(){
        操作;
        关键字this - 这个
                如果单个元素绑定事件,this->这个元素
                如果多个元素绑定事件,this->当前触发事件元素
}
  • 一切的获取,往往都是为了判断
  • 一切的设置,可以说是添加也可以说是修改

八、数组

问题:保存1000个同学的名字?
var name1="钟清翰1";
...
var name1000="钟清翰1000";
不推荐,变量其实就是我们所谓的内存,变量创建的越多,那么我们的内存空间消耗越大,那么网站性能就会越差

解决:数组:创建一个变量可以保存【多个数据】
数组都是线性排列,除了第一个元素,每个元素都有唯一的前驱元素除了最后一个元素,每个元素都有唯一的后继元素

每个元素都有一个自己的位置,称之为叫做下标,下标都是从0开始的,到最大长度-1结束

8.1 创建数组:(2种)

1、直接量方式:
var arr=[];//空数组
var arr=[数据1,...];

2、构造函数方式:
var arr=new Array();//空数组
var arr=new Array(数据1,...);

8.2 获取数组之中的元素:

数组名[i];

8.3 后续添加/替换元素:

数组名[i]=新数据;
如果下标处没人则为添加,如果下标处有人则为替换

8.4 数组具有三大不限制

  1. 不限制元素的类型
  2. 不限制元素的长度
  3. 不限制下标越界 - 不是一个好东西
    如果获取元素时,下标越界,返回的是一个undefined
    如果添加元素时,下标越界,会得到一个稀疏数组,如果我们搭配上我们学过循环去遍历获取每个元素,那么你会得到很多很多undefined

问题:自己数下标,难免会数错,导致我们下标越界

8.5 下标越界的解决:数组中有一个唯一的属性--length

语法:数组名.length
作用:获取到数组的长度,长度是从1开始的

三个固定套路:

  1. 向末尾添加元素:arr[arr.length]=新数据;
  2. 获取数组的倒数第n个元素:arr[arr.length-n];
  3. 缩容:删除倒数n个元素:arr.length-=n;

8.6 遍历数组:

往往很多情况,我们不会拿出某个元素来使用,而是拿出每个元素来进行 相同 或 相似的操作 - 搭配上循环
固定公式:

for(var i=0;i<arr.length;i++){
	arr[i];//当前次元素
}

数组

一、数组的基础

1.1、创建数组:

1、直接量方式:var arr=[值1,......];

2、构造函数:var arr=new Array(值1,......);

构造函数创建数组有一个:new Array(num);这句话的意思是:创建了一个长度为num的空数组,里面没有任何东西,只有无数的undefined

1.2、按值传递:(面试题)

题目:var a=x; var b=a; 修改a,b变不变,或者修改b,a变不变

分两种情况讨论:

1、传递的如果是原始类型:

其实是复制了一个副本给对方,两者互不影响

2、传递的如果是引用类型(js中除了原始类型就是引用类型(函数、数组))--浅拷贝

因为引用类型很大,比原始类型大很多,不可能保存变量在本地,而是保存了一个地址值而已,其实是赋值了自己的地址值给对方,两者用的是同一个地址,一个发生变化,另一个也会改变。

1.3、如何释放一个引用类型:

一定要看清有几个变量引用着这个类型,每个变量都释放后,才能释放干净

在js底层有一个垃圾回收器,只有垃圾回收器的计数器(记录着这个数据有几个人使用)为0的时候,才会删除不要的数据

建议:我们的代码都要封装为一个函数,函数的一切变量都会自动释放

索引数组:下标都是数字组成的数组

二、hash数组:

又称关联数组,下标是可以自定义的

由来:索引数组的下标无具体意义,不便于查找

2.1、创建:(2步)

1、创建空数组:var arr=[];

2、为数组添加自定义下标并且赋值: arr["自定义下标"]=新值;

2.2、访问元素:

arr["自定义下标"];

2.3、遍历hash数组

hash数组的length失效了,length永远为0

遍历hash数组不能再使用for循环,必须使用for in循环(专门遍历hash数组的)

  for(var i in 数组名){
      i;//下标
      数组名[i];//当前次元素
  }

不止能遍历hash数组还能遍历索引数组

建议:索引数组依然使用for循环,hash数组使用for in循环

2.4、hash数组的原理

hash算法: 将字符串,计算出一个尽量不重复的数字(地址值),字符串的内容相同,则计算出的数字相同

添加元素:将自定义下标交给hash算法,得到一个数字(地址值),直接将你要保存的数据放到此地址保存起来

获取元素:将指定的自定义下标交给hash算法,得到一个和当初保存的的一样的数字(地址值),通过此地址找到你当初保存的数据,取出来使用

2.5、一切对象的底层都是hash数组

js里面一切东西都是对象,万物皆对象,除了undefined和null

三、数组的API

3.1、数组转字符串

var str=arr.join("自定义连接符");

固定套路:

1、鄙视题:将数组里面的内容拼接为一句话/单词--无缝拼接,拼接了一个空字符串

var arr=["h","e","l","l","o"," ","w","o","r","l","d"];
var str=arr.join("");
console.log(str);//结果:hello World

2、将数组拼接为DOM页面元素--数据渲染页面

//HTML中的内容
<select></select>
//js中的内容
var sel=document.getElementsByTagName("select")[0];
var arr=["-请选择-","北京","南京","重庆","湖南","天津","上海"];//读取数据,一般不会是自己创建
var str="<option>"+arr.join(</option><option>)+"</option>";//option可以换成任何其他标签
sel.innerHTML=str;//innerHTML能识别标签

3.2、数组的拼接

添加元素的新方式, 将你传入的实参全部拼接到arr的末尾:

var newArr=arr.concat(新值1,arr1,......);//传入的数组元素会被打散为单个元素

特殊:1、不修改原数组,只会返回一个新数组。 2、concat支持传入数组参数,悄悄的将你传入的数组打散为单个元素再拼接

3.3、截取子数组

根据你传入的开始下标截取到结束下标:var subArr=arr.slice(starti,endi+1);

特殊:1、不修改原数组,只会返回一个新数组 2、左闭右开 3、endi可以省略不写,会从开始位置一直截取到末尾 4、starti也可以省略,如果两个实参都省略,会从头截到尾的完全复制一份---深拷贝-复制了一个副本给对方5、支持负数参数,-1代表倒数第一个

下面的API会修改原数组

3.4、删除、插入、替换

删除:var dels=arr.splice(starti,n);//n代表删除的个数 虽然修改原数据,但也有返回值,返回的是被删除的数据

插入:arr.splice(starti,0,新值,...);//在某位置前面插入新值特殊:1、原starti位置的元素以及后续元素都会向后移动。2、尽量不要插入一个数组,会导致我们的数组一些是一维,一些是二维,遍历的时候不方便

替换:var dels=arr.splice(starti,n,新值,.....);//删除的个数不必与插入的个数相同

3.5、翻转数组

arr.reverse();//不常用

3.6、数组的排序

1、鄙视题:冒泡排序:将数据按照从小到大进行排序

var arr=[32,4,23,5546,2253,67,47,0,43,1];
for(var i=1;i<arr.length;i++){
    for(var j=0;j<arr.length-i;j++){
        if(arr[j]>arr[j+1]){
            var m=arr[j];
            arr[j]=arr[j+1];
            arr[j+1]=m;
        }
    }
}
console.log(arr);

2、正式开发:arr.sort();

默认:将数组中的元素转为字符串后,再按位PK每个字符的Unicode(ASCII码)

  1. 升序排列(从小到大)
arr.sort(function(a,b){
   console.log(a);//后一个数字
   console.log(b);//前一个数字
   return a-b;
})
  1. 降序排列(从大到小)
arr.sort(function(a,b){
   return b-a;
})

切记:1、网页上有功能带有排序,那么底层一定是数组,因为js中只有数组可以排序。2、网页上有随机的功能,底层一定是用了随机数公式

3.7、栈和队列

栈:数组只能从某一端(开头/结尾)进出;

  • 开头进:arr.unshift(新值,...);//向前添加,后面的元素的下标会发生变化

  • 开头出:var first=arr.shift();//一次只能 删除一个,其他元素的下标发生改变

  • 结尾进:arr.push(新值,...);//向后添加元素

  • 结尾出:var last=arr.pop();//从末尾删除

队列:一段进,另一端出:

  • 开头进:arr.unshiift(新值,...);

  • 结尾出:var last=arr.pop();

  • 结尾进:arr.push(新值,...);

  • 开头出:var fisrt=arr.unshift();

上面都是ES3提供的API,下面是ES5提供的6个API

3.8、判断(2个)

every:每一个,要求所有元素都满足条件才会为true,类似于&&

var bool=arr.every(function(val,i,arr){
    //val-当前的值(不能省略)
    //i-当前值的下标
    //arr-当前数组本身
    return 判断条件;
})

some:有一些,要求只要有一个元素满足条件就会为true,类似于||

var bool=arr.some(function(val,i,arr){
    return 判断条件;
})

3.9、遍历

遍历目的:拿到数组中的每个元素做相同或者相似的操作

forEach--直接修改原数组

arr.forEach(function(val,i,arr){
    你的操作;
})

map--不修改原数组,返回一个新数组

var newArr=arr.map(function(val,i,arr){
    return 操作;
})

3.10、过滤和汇总

过滤:筛选出你需要的部分,不修改原数据

var subArr=arr.filter(function(val,i,arr){
    return 判断条件;
})

汇总:不修改原数据

var result=arr.reduce(function(prev,val,i,arr){
    return prev+val;//数组求和,加号可以换成任何其他算术符
},基础值)

ES6箭头函数:简化一切匿名函数: 把function去掉,()与{}之间添加=>,如果()里面只有一个参数,那么()可以去掉,如果{}里面只有一句话,那么可以省略{}

例如:arr.forEach(val=>val+1); var result=arr.reduce((prev,val)=>prev+val);

四、二维数组

4.1、创建:

var arr=[    ['张三',18,900],
    ['李四',29,800],
    ['老王',30,600]
];

4.2、访问:

arr[行下标][列下标];

特殊:1、列下标越界,返回undefined。2、行下标越界,会报错,因为行下标越界已经得到undefined,undefined不能做任何操作,所以报错。

4.3、遍历:

必须是两层循环,外层循环控制行,内层循环控制列

for(var r=0;r<arr.length;r++){
    for(var c=0;c<arr[r].length;c++){
        console.log(arr[r][c]);
    }
}

字符串

多个字符组成的只读字符数组,不能直接原字符串,返回一个新字符串

与数组的相同点:

  1. 字符串中长度:str.length;
  2. 获取字符串中的某个字符:str[i];
  3. 遍历字符串
  4. 所有数组不修改原数组的API,字符串也可以使用,比如(concat、slice)

与数组的不同点:

所有数组可以直接修改原数组的API,字符串都不可以使用,比如排序等

引用/对象类型:11个

  • String Number Boolean -->包装类型
  • Array Function Date Math RegExp(正则:验证)
  • Error(错误) Object(面向对象开发方式)
  • Global(全局对象)--只有在浏览器中被window对象代替了,保存着全局变量和全局函数,只不过window可以省略不写,后面在学习node.js这个后端语言的时候,全局对象叫Global

包装类型:专门用于将原始类型的值封装为一个引用类型的对象

  • 原因:原始类型的值原本就是没有任何属性和方法,意味着原始类型是本来是不支持做任何操作的
  • 使用:只要你使用原始类型的变量调用属性或者方法,就会自动包装
  • 释放:方法的调用完毕后,自动释放包装类型,变为原始类型
  • 为什么null和undefined不能使用,因为前辈们没有给他们提供包装类型

字符串的API:

1、转义字符:\

作用:

  • 1、将字符串中和程序冲突的字符转为原文
  • 2、包含特殊功能的符号:换行:\n 制表符:\t -->tab键效果
  • 3、输出Unicode 编码的字符:最小的字\u4e00 一 --asci码:19968 最大的字\u9fa5 --ascii码:40869

2、大小写转换:

将字符串中每个字符统一的转为大写或者小写

  • 使用:只用程序不区分大小写,就要先统一的转为大写或小写,再作比较(比如做验证码)

  • 语法:大写/小写:var upper=str.toUpper/LowerCase;

3、获取字符串中指定位置的字符:

str.charAt(i); 等同于str[i];

4、获取字符串中指定位置的字符的ASCII码:

var ascii=str.charCodeAt(i);

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

5、检索字符串:

检查索引,检查下标:获取关键字的下标

var i=str/arr.indexOf("关键字",starti);从starti位置开始,从左到右依次查找,starti可以省略,默认从0开始查找

返回值:找到了,返回找到的第一个关键字的下标,没找到,返回-1

作用:判断有无

数组也可以使用此方法,数组这个方法是后面加上的,老IE的数组就没有此方法

鄙视题:这个方法默认只能获取到第一个关键字的下标,如何获取所有关键字的下标:

var str='no zuo ni die no zuo no bibi';
var indedx=-1;
while((index=str.indexOf('no',index+1))!=-1){
    console.log('找到了,下标为:'+index);
}

6、拼接字符串

var newStr=str.concat("新字符串",..); 不如 +运算

7、截取字符串

var subStr=str/arr.slice(starti,endi+1);//左闭右开

var subStr=str.substring(starti,endi+1);//左闭右开,不支持负数

var subStr=str.substr(starti,n);//n代表截取的个数

8、替换字符串

var newStr=str.replace("固定关键字"/正则表达式,"新内容");//这个方法加上正则表达式后十分强大

9、切割/分割/分离字符串

作用:将字符串转为数组

var arr=str.split("自定义切割符");

注意:1、切割后,切割符就不存在了;2、如果切割符是""空字符串,则会切散每一个字符

11、扩展:JS如何创建元素:3步

1、创建空标签:var elem=document.createElement("elem");

2、为其设置必要的属性和事件: elem.属性名="属性值"; elem.on事件名=function(){操作};

3、挂载上树/渲染页面:父元素.appendChild(elem);

Math对象

专门提供了数学计算的API

强调:不需要创建,直接使用

属性:Math有一些属性涉及到科学计数法,但很少用到,常用的就Math.PI,π不需要自己创建,浏览器自己有

Math的API

1、取整:3种

1、上取整:超过一点点就取下一个整数
var num=Math.ceil(num);//小数位数不能超过15位,否则会下取整

2、下取整:无论超过多少,都会省略掉小数点后面的部分
var num=Math.floor(num);

3、四舍五入取整:var num=Math.round(num);//只看小数点第一位

  • 以上三个方法都很一般,都是取整,但只能取整

  • 取整的方式:以上三个+parseInt(str去掉单位)+num.toFixed(d);

  • num.toFixed(d); 优点:1、可以四舍五入,并保留指定位小数d 2、解决浏览器带来的舍入误差,例如:2-1.6=3.9999999999...这种情况可以使用toFixed来解决
    缺点:结果数一个字符串,可以与parseFloat()搭配使用

鄙视题:不允许使用toFixed()的情况下,自己封装一个函数,由用户传入数字,实现四舍五入:

function toFixed(num,d){
    num*=10**d;
    num=Math.round(num);
    num/=10**d;
    return num;
}
var result=toFixed(Math.PI,2);
console.log(result);

2、乘方和开方

乘方:Math.pow(底数,幂); --> 简化:底数**幂

开方:Math.sqrt(num);仅仅只能开平方

3、最大值和最小值

var max/min=Math.max/min(23,435,2331,56,3254,3);

问题:本身不支持数组参数

解决:Math.max/min.apply(Math,arr);apply具有打散数组的功能

4、绝对值

把负数转为正数

Math.abs(-1);

5、随机数

Math.random();在0-1之间取一个随机小数

搭配上parseInt,只能取到0,不能取到1

语法:parseInt(Math.random()*(max-min+1)+min);

只要网页有随机功能,就一定用到了随机数

Date对象

日期对象,提供了操作日期和时间的API

1、创建:4种

1、创建一个当前日期: var now=new Date();

2、创建一个自定义时间: 法一: var birth=new Date("year/month/date hours:minutes:seconds")

法二: var birth=new Date(year/month/date hours:minutes:seconds);月份0-11,0代表1月

3、复制一个日期:
为什么:日期的所有API都是直接修改原日期的,无法获得修改之前的日期
所以,在执行API之前先进行复制,然后再操作复制后的日期:
var end=new Date(start);

2、Date的API

分量:时间的单位

年月日星期:FullYear Month Date Day

时分秒毫秒:Hours Minutes Seconds Milliseconds

每一个分量都有 getXXX/setXXX 的方法

特殊:

  1. 取值范围:
    FullYear - 当前年份的数字
    Month - 0-11
    Date - 1-31
    Day - 0-6: 0为星期天,6星期六 Hours - 0-23
    Minutes、Seconds - 0-59

  2. Day,没有set方法

  3. 某个分量的加减操作
    date.setXXX(date.getXXX()+/-n)

  4. 格式化日期为本地字符串
    date.toLocaleString(); - 垃圾:具有兼容性问题,我们一般会选择自己创建一个格式化方法来格式日期
    用了此方法会失去一些东西:日期的自动进制、日期的API 但是你也会获得一些东西:字符串的API

定时器

1、周期性定时器

每过一段时间就会执行一次,先等后做

开启:timer=setInterval(callback,间隔毫秒数);

停止:clearInterval(timer);

2、一次性定时器

等待一段时间,只做一次就结束了

开启:timer=setTimeout(callback,间隔毫秒数);

停止:clearTimeout(timer);

同步技术:代码必须一行一行执行,前面没做完,后面就等着

异步技术:无论这块代码多么耗时,都不会卡住后面代码的执行

定时器是异步技术

window对象

扮演着两个角色

1、全局对象:

保存着全局变量和全局函数

2、指代当前窗口本身:

属性:

1、获取浏览器的完整大小:outerWidth/outerHeight

2、*获取浏览器的文档显示区域的大小:innerWidth/innerHeight - 获取每台电脑的浏览器的文档显示区的大小

3、获取屏幕的完整大小:跟window没关系:screen.width/height; - 我们目前学习的都是浏览器应用(网页),并不会去做桌面应用

方法:

1、打开链接的新方式:

1、当前窗口打开,可以后退:
HTML:<a href="url">内容</a>
JS:open("url","_self");

2、当前窗口打开,禁止后退:
使用场景:比如电商网站,结账后不允许后退
HTML做不到,只有JS可以,也不是window能做到的:

  • history:当前【窗口的历史记录】,他其实可以做的事儿就是前进后退
  • location:当前【窗口正在打开的url】,有一个API:
    location.replace("新url");//叫做替换,不叫做跳转,不会产生历史记录,自然也不能后退了,但是网址替换了,网页必然会发生变化

3、新窗口打开,可以打开多个
HTML:<a href="url" target="_blank">内容</a>
JS:open("url","_blank");

4、新窗口打开,只能打开一个:
使用场景:比如电商网站,只允许用户打开一个结账页面
HTML:<a href="url" target="自定义一个name">内容</a>
JS:open("url","自定义一个name");
其实窗口的底层都是有一个名字的,如果打开了一个已经开着的名字的窗口的,他会把他关闭掉,再次打开

  • 学完这块的成果:
    1、以后的跳转,任何标签都可以 2、提升用户的体验感 3、a标签的其他用途:
    1、跳转 2、锚点 3、下载按钮:<a href="xx.exe/rar/zip/7z">下载</a> 4、打开图片和txt文档:<a href="xx.png/jpg/jpeg/gif/txt">打开图片和txt</a> 5、直接书写js-不需要绑定点击事件:<a href="javascript:js代码;">打开图片和txt</a>

2、打开新窗口/新链接:

newW=open("url","target","width=?,height=?,left=?,top=?");
特殊:
1、如果没有加第三个参数,那么窗口会和浏览器融为一体
2、如果你加了第三个参数,那么窗口会脱离浏览器独立存在

3、关闭窗口:window/newW.close();

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

5、改变新窗口的位置:newW.moveTo(新X,新Y);

6、*window提供了三个框:

警告框:alert("警告文字"); 输入框:var user=prompt("提示文字"); 确认框:var bool=confirm("提示文字");

7、*****定时器也是window的

8、事件:

1、window.onload事件 - load - 加载:等待其他所有的资源加载完毕后才会执行的代码,放在里面的代码其实要最后才会执行

2、window.onresize事件 - 窗口如果大小发生了变化,就会触发,搭配上判断innerWidth可以理解为是js版本的css媒体查询

3、window.onscroll事件 - 滚动事件,一旦滚动就会触发 1、获取滚动条当前的位置:window.scrollY 2、获取元素距离页面顶部有多远:elem.offsetTop/offsetLeft

9、*****本地/客户端存储技术:

1、cookie:淘汰了,存储的大小只有2kb,而且操作极其麻烦,尤其要到处切割,只能最多保存30天

2、webStorage:H5带来了一个新特性,存储的大小有8mb,永久保存,而且非常简单
分类2种:

  • 1、sessionStorage - 会话级,只要浏览器一旦关闭,数据就会死亡了
  • 2、localStorage - 本地级,只要你不清空,就会永久存在 两者的用法是一模一样的,不用创建,直接可用

操作:
1、添加:xxxStorage.属性名="属性值";
2、读取:xxxStorage.属性名;
3、删除:xxxStorage.removeItem("属性名");
4、清空:xxxStorage.clear();

BOM

Browser Object Model - 浏览器对象模型,专门用于操作浏览器,但使用不大多,远不如ES和DOM,浏览器很多操作都是自带的,而BOM没有标准

各个浏览器都有自己的定义,但大部分浏览器都是一致规范的,除了老IE(8-)

BOM的常用对象

1、history对象:

前进:history.go(1);

后退:history.go(-1);

刷新:history.go(0);

2、location对象:

程序员常识:一个url由几部分组成?分别每个部分有什么用? - 以后再学习服务器端和数据库的时候会有很大的帮助
5部分:

  • 1、协议:*https(加密)/*http(未加密)/ftp(传输文件)/ws(直播)... 前两个都属于叫做请求-响应模型

  • 2、主机号|IP地址|域名:域名是需要花钱购买的,主机号|IP地址是免费,127.0.0.1才真的是叫做主机号,只能自己访问自己

  • 3、端口号:https默认端口为443,http默认端口为80,只有默认端口可以省略不写

  • 4、文件的相对路径|路由:百度加密了

  • 5、查询字符串|请求消息:前端传输到后端的东西,前端对后端说的话,就是form表单提交带来的东西

属性:

获取url的5个部分的内容,但是不需要记忆,你直接输入location对象即可查看

    协议:location.protocal;
    域名:location.hostname;
    端口:location.port;
    路由:location.pathname;
    请求消息:location.search;	

跳转:location="新url" - 替换当前窗口,可以后退
跳转后,禁止后退:location.replace("新url") - 替换当前窗口,禁止后退
刷新:location.reload();

DOM

原本DOM是可以操作一切结构化文档的,但是再某一个升级后,为了方便各类程序员将DOM分为了3方面:

1、核心DOM:无敌的,即可以操作HTML又可以操作XML,但是语法相对比较繁琐

2、HTML DOM:只可以操作HTML,不能访问一切自定义的东西,但是语法简单

3、XML DOM:只可以操作XML,被淘汰了,被JSON数据格式代替了

1、查找元素

1、通过关系找元素

父:parentNode

子:children

第一个儿子:firstElementChild

最后一个儿子:lastElementChild

前一个兄弟:previousElementSibling;

后一个兄弟:nextElementSibling;

2、直接找元素:

1、document.getElementsByXXXX(); - 返回的是一个动态集合HTMLCollection

2、

  • 1、var elem=document.querySelector("任意css选择器的"); //query-查询 selector-选择器:查询css选择器
    缺陷:只能找到单个元素,如果匹配到了多个,也只会返回第一个,没找到null 一次只能操作一个元素

  • 2、var elems=document.querySelectorAll("任意css选择器的");
    优点: 1、找到了是一个集合,没找到是一个空集合 2、复杂查找时,非常简单 3、返回的是一个静态集合NodeList

面试题:document.getXXX 和 document.queryXXX的区别?

  • 1、后者更适合复杂查找
  • 2、动态集合和静态集合的区别:
    1.动态集合:每一次DOM发生变化,他都会悄悄的再次查找,让页面和数据保持一致,但是效率也就低下了 - 不支持forEach
    2.静态集合:每一次DOM发生变化,他不会悄悄的再次查找,让页面和数据没有保持一致,但是效率也就高了 - 支持使用forEach

2、操作样式:

1、内联样式

2、样式表样式:

    //获取你想要操作的样式表
    var sheet=document.styleSheets[i];
    //获取此样式表种所有的样式规则
    var rules=sheet.cssRules;
    //数出你想要操作的那个规则的
    var rule=rules[i];
    //操作
    console.log(rule.style.css属性名)
    rule.style.css属性名="css属性值";

3、操作属性:

1、获取属性值: 核心DOM:elem.getAttribute("属性名"); HTML DOM:elem.属性名;

2、设置属性值: 核心DOM:elem.setAttribute("属性名","属性值"); HTML DOM:elem.属性名="属性值";

3、删除属性:设置属性值为空字符串,确实某些属性可以算是删除,但是只是删除了属性值,属性名还在,而有的属性哪怕只有一个属性名,也会具有作用(比如:href、disabled、readonly)
核心DOM:elem.removeAttribute("属性名");-能删干净 HTML DOM:elem.属性名=""; - 属性节点删不干净

4、判断有没有:只能判断有没有,不能判断是什么,推荐用elem.getAttribute("属性名");去获取到值,自己再写比较运算 核心DOM:elem.hasAttribute("属性名"); HTML DOM:elem.属性名!="";

建议:优先HTML DOM,HTML DOM实现不了的再用核心DOM补充!

缺陷: 1、class必须写为className 2、自定义的东西都操作不了

4、操作内容

innerHTML/innerText/Value

5、如何创建元素以及上树:3步

1、创建空标签: var elem=document.createElement("标签名");

2、为其设置必要的属性和事件 elem.属性名="属性值"; elem.on事件名=function(){}

3、上树:3种

  • 父元素.appendChild(elem);//在父元素末尾处追加一个子元素elem
  • 父元素.insertBefore(elem,已有子元素);//在父元素追加一个子元素elem,但是放在已有子元素的前面
  • 父元素.replaceChild(elem,已有子元素);//在父元素追加一个子元素elem,但是会替换掉已有子元素

特殊: select&option只有他们可以简化创建元素和上树:
select.add(new Option("innerHTML","value"));

6、删除元素:elem.remove();

扩展:let关键字

1、创建变量:新增的一个let关键字: let 变量名=值;
作用:

  • 1、解决了声明提前
  • 2、带来了块级作用域,一个{}就是一个块,此变量只能在那个{}里面使用
  • 3、如果用let去当作下标绑定事件,那么他会记录着你当前元素的下标,不再需要自定义下标了 - 其实forEach的那个形参i就是let创建的

2、类数组转为普通数组:接住=Array.from(类数组对象);

递归

递归:简单来说就是再函数之中再一次调用了函数自己,迟早有一天会停下来;
何时:专门用于【遍历层级不明确的情况】 - DOM树和数据(children只能找到儿子层,找不到孙子层)
如何使用:2步

function 函数名(root){
	1、第一层要做什么直接做

	2、判断有没有下一层,如果有下一层则再次调用此方法,只不过传入的实参是自己的下一层		
}
函数名(实际的根)

算法:深度优先!优先遍历当前节点的子节点,子节点遍历完毕才会跳到兄弟节点

缺陷:不要过多使用,性能相对较差,同时开启大量的函数调用,浪费内存,我们只在一个情况:【层级不明确】

递归 vs 纯循环

递归:优点:简单易用 缺点:性能低
纯循环:优点:几乎不占用性能 缺点:难的一批

绑定事件:

3种方式:
1、在HTML上书写事件属性 <elem on事件名="函数名(实参)"></elem>
缺点: 1、不符合内容与样式与行为的分离原则 2、无法动态绑定,一次只能绑定一个元素 3、不支持绑定多个函数对象

2、在js中使用事件处理函数属性 elem.on事件名=function(){操作}
优点: 1、符合内容与样式与行为的分离原则 2、动态绑定,一次能绑定多个元素
缺点: 1、不支持绑定多个函数对象

3、在js中使用事件API:如果不用考虑老IE,他也不错
主流:elem.addEventListener("事件名",callback);
老IE:elem.attachEvent("on事件名",callback);
兼容:

    if(elem.addEventListener){
            elem.addEventListener("事件名",callback);
    }else{
            elem.attachEvent("on事件名",callback);
    }

优点: 1、符合内容与样式与行为的分离原则 2、动态绑定 3、支持绑定多个函数对象
缺点:有兼容性问题

属于BOM:

重点只有两个:定时器(翻之前笔记) + event(事件对象);

1、事件周期:

从事件发生,到所有事件处理函数执行完毕的全过程 3个阶段: 1、捕获阶段:由外向内,记录要发生的事件有哪些 2、目标优先触发:目标元素->当前点击的实际发生事件的元素 3、冒泡触发:由内向外,依次执行我们之前记录着的要发生的事件

2、事件对象event:

老IE:不支持HTML5和CSS3和ES5+

主流:会自动作为事件处理函数的第一个形参传入
老IE:event; - 老IE全局有这个变量
兼容:event;//第一次见到小三上位,不光老IE可用,主流也可用

得到了事件对象event可以做什么呢?

1、获取鼠标的坐标:

获取鼠标相对于屏幕的坐标:e.screenX/Y

获取鼠标相对于窗口/客户端/文档显示区域的坐标:e.clientX/Y

获取鼠标相对于网页的坐标:e.pageX/Y

2、阻止事件冒泡:- 鄙视面试中

主流:e.stopPropagation();

老IE:e.cancelBubble=true;

兼容:e.cancelBubble=true;//第2次见到小三上位,不光老IE可用,主流也可用

3、利用冒泡/事件委托

开发中常用,提升网页性能,有了它我们的事件函数也可以换为箭头函数了

优化:如果多个子元素定义了相同 或 相似的事件操作,最好只给父元素定义一次
为什么:每一次绑定一个事件函数,其实都是创建了一个事件对象,创建的事件对象越多,网站性能就越差

淘汰了this,他水性杨花,当前元素

认识一个目标元素target:你点击的是哪一个,他永远就是那一个,不会变化的
主流:e.target;
老IE:e.srcElement;
兼容:e.srcElement;
//第3次见到小三上位,不光老IE可用,主流也可用

如果事件委托了:

1、如何判断目标元素是什么标签:xx.nodeName; - 得到标签名,只不过是全大写组成的

2、事件处理函数可以写为箭头函数->this就会失效,所以必须使用目标元素target

3、this的指向: 1、单个元素绑定事件,this->这个元素 2、多个元素绑定事件,this->当前元素 3、箭头函数,this->外部对象

4、阻止浏览器的默认行为:

比如:a标签默认就可以跳转,提交按钮可以提交表单,右键自带一个弹出框,F12自带一个控制台,F11自带全屏功能,F5自带刷新功能

主流:e.preventDefault();

老IE:e.returnValue=false;

兼容:e.returnValue=false;//第4次见到小三上位,不光老IE可用,主流也可用

新事件:

  • 1、右键事件 - window.oncontextmenu
  • 2、键盘事件:一般来说用于游戏开发较多+都要搭配上键盘的键码 1.window.onkeydown - 按住和按下,任何键盘按键都可以触发
    2.window.onkeypress - 按住和按下,只有字母、数字、回车、空格可以触发,其他按键不行
    3.window.onkeyup - 松开,任何键盘按键都可以触发(比手速的游戏)

5、获取键盘的键码:

e.keyCode; - 可以获取到你按了那个键,每个键都有自己对应的键码,但是不需要记忆,要么输出看,要么百度搜个表

事件的取消绑定

1、如果你使用elem.onclick=()=>{},那么elem.onclick=null

2、如果你使用elem.addEventListener("事件名",回调函数); 那么:
elem.removeEventListener("事件名",回调函数); - 事件名和回调函数,必须和添加时的一模一样

this的指向:

单个元素绑定事件this->这个元素

多个元素绑定事件this->当前元素

箭头函数中的this->外部对象

函数中的this->当前正在调用函数的这个人

定时器的this->window

ES5强制改变this的指向

1、call/apply:临时的替换了函数的this - 借用

语法:函数名.call(借用的对象,实参,...); - 单独传入每个实参
函数名.apply(借用的对象,arr); - 只能传入一个实参要求是一个数组,apply其实会悄悄的打散数组

强调:call/apply:相当于立刻调用函数,立即执行的

2、bind:永久替换了函数中的this - 买

3件事:
1、创建了一个和原函数功能完全相同的新函数
2、将新函数的this永久绑定为了指定对象,别人都借不走
3、将新函数的部分参数永久固定

语法:var 新函数=函数名.bind(永久对象,永久实参,...); - 不是立刻执行,需要自己调用

强调:bind绑定的新函数没办法被call/apply再次借走

推荐:借

三个固定套路:

  • 1、Math.max/min.apply(Math,arr) - 也能支持数组参数:

  • 2、Object.prototype.toString.call/apply(x)==="[object Array]";//鄙视题:判断xx是不也是一个数组

  • 3、类数组转为普通数组:
    1、接住=Array.prototype.slice.call/apply(类数组对象)
    2、接住=Array.from(类数组对象)

ES6:

将数组转为单个元素 - ...arr

1、let、const关键字、箭头函数(之前笔记有)

2、模板字符串:

可以直接识别变量,不再需要+运算去拼接了,而且实现了一个简单的js环境,甚至支持再里面书写API

    console.log(`我的名字叫${name}`);

3、解构赋值:

顾名思义:解析结构再进行赋值 - 赋值的新方式,并且得到了增强

如果赋值符号,左右两边的结构一样的,就会悄悄的解开/脱掉结构再一一的进行赋值

语法:

1、类似数组的解构赋值

    let [a,b,c]=[1,2,3];
    console.log(a);
    console.log(b);
    console.log(c);

2、类似对象的解构赋值

    let {a,b=默认值,c}={c:3,a:1,b:2};
    console.log(a);
    console.log(b);
    console.log(c);
    //形参可以设置默认值,如果自己传入了,就用自己的

3、调用函数时,传递实参的顺序可以不在意

    function zwjs({name,age,hobby="playGame"}){
            return `我的名字叫${name},今年${age}岁,喜欢${hobby}`;
    }
    console.log(zwjs({hobby:"学习",age:18,name:"皮卡"}));

4、函数的返回的结果,可以有多个

    function f1(){
            var a=1;
            var b=2;
            return [a,b];
    }
    var [a,b]=f1();
    console.log(a,b);

只要以后见到:方法名({里面放着键值对就是使用了ES6的解构赋值})

4、Set和Map新的数据类型:

1、Set:类似于数组的一种数据格式 - 【去重数组,然后再转回数组】

var s=new Set(arr);

...s - 三个点扩展运算符,可以脱掉数组的外套

一句话完成:[...new Set(arr)] - 不用记忆任何API

2、Map:类似于对象的一种数据格式

var m=new Map();
添加:m.set("键","值");
获取:m.get("键");
清空:m.clear();
删除:m.delete("键");

5、新的循环:for...of --不常用于

    for(var v of arr){
            v;//得到数组里面的值
    }

缺陷:

1、没有提供过下标,意味着不能修改原数组
2、只能遍历索引数组,不能遍历hash数组,意味着也不能遍历对象

正则表达式:很重要

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

何时使用:切割 替换 【验证】!

1、如何使用:语法:/正则表达式/

1、最简单的正则就是关键字原文 "no" -> /no/后缀 后缀:g:找全部 i:忽略大小写

2、备选字符集:/^[备选字符集]$/

强调:

  • 1、一个中括号,只管一位字符

  • 2、问题:正则表达式默认只要满足就不管后续了,而我们做验证的人,希望的是用户从头到尾按照我们的要求来,希望从头到尾完全匹配:
    解决:前加^,后加$,两者同时使用,代表要求从头到尾完全匹配 - 只要做验证必加!

特殊:如果备选字符集中的ascii码是连续的,那么可用-省略掉中间部分

比如:
一位数字:[0-9];
一位字母:[A-Za-z];
一位数字、字母、下划线:[0-9A-Za-z_]
一位汉字:[\u4e00-\u9fa5]

除了xxx之外的:[^0-9] - 很少使用,范围太广了

3、预定义字符集:

前辈们提前定义了一些字符集,方便我们程序员 - 简化了备选字符集
一位数字:\d ===>[0-9]
一位数字、字母、下划线:\w ===> [0-9A-Za-z_]
一位空白字符:\s
一位除了换行外的任意字符:. - 很少使用,范围太广了

建议:优先使用预定义字符集,预定义满足不了我们再用备选字符集补充

问题:不管是备选字符集,还是预定义字符集,一个都只管一位

4、量词:规定一个字符集出现的次数:

1、有明确数量:

字符集{n,m}:前边相邻的字符集,至少n个,最多m个
字符集{n,}:前边相邻的字符集,至少n个,多了不限
字符集{n}:前边相邻的字符集,必须n个

2、无明确数量:

字符集?:前边相邻的字符集,可有可无,最多1个
字符集*:前边相邻的字符集,可有可无,多了不限
字符集+:前边相邻的字符集,至少一个,多了不限

5、选择和分组:

选择:在多个规则中选一个: 规则1|规则2

分组:将多个字符集临时组成一组子规则: (规则1|规则2)

6、指定匹配位置

^:开头
$:结尾
特殊:两者同时使用,前加^,后加$,表示从头到尾要求完全匹配 - 只要你做【验证】

7、密码强度验证:
2-4位,可以输入数字、字母,但是必须出现一位大写和一位数字的组合

    /^[0-9A-Za-z]{2,4}$/
    预判公式:
            (?![0-9]+$) -> 不能全由数字组成,可能有大写、小写、汉字、日文、特殊符号...
            (?![a-z]+$) -> 不能全由小写组成,可能有数字、大写、汉字、日文、特殊符号...
            (?![0-9a-z]+$) -> 不能全由数字组成、也不能全由小写组成、也不能全由数字和小写的组合组成

    比如:
            /(?![0-9a-z]+$)(?![A-Za-z]+$)[0-9A-Za-z]{2,4};//2-4位,可以输入数字、字母,但是必须出现一位大写和一位数字的组合
            /(?![0-9a-z]+$)(?![A-Za-z]+$)(?![A-Z0-9]+$)[0-9A-Za-z]{2,4}/;//必须三者都有
            /(?![0-9A-Za-z]+$)[0-9A-Za-z_]{2,4}/;//至少要有下划线

2、支持正则表达式的字符串API

1、切割: var arr=str.split("固定切割符"/RegExp);

2、替换:很有可能出现在笔试之中

1、基本替换法:

str=str.replace(/正则表达式/后缀,"新内容");
//replace支持支持正则,并且搭配上后缀g就可以找到全部
//缺陷:替换的新内容是一个固定的

2、高级替换法:

    str=str.replace(/正则表达式/后缀,function(a,b,c){
			console.log(a);//正则匹配到的关键字
			console.log(b);//正则匹配到的关键字的下标
			console.log(c);//原字符串
            return 判断a关键字的长度,而返回不同的星星数量
    });

3、格式化:身份证

    var id="500103198602215933";
    var reg=/(\d{6})(\d{4})(\d{2})(\d{2})(\d{4})/;
    id.replace(reg,function(a,b,c,d,e,f,g,h){
            //再replace的时候,正则出现了分组,我们会得到更多的形参
            //再形参a的后面就会出现n个形参,就看你有多少个分组
            //第1个分组获得的内容会保存在第2个形参之中
            //第2个分组获得的内容会保存在第3个形参之中
            //...
            //倒数第二个一定是下标
            //倒数第一个一定是原文本身
    })

格式化:手机号

    var phone="13452390312";
		var reg=/(\d{4})\d{4}(\d{3})/;
		
		phone=phone.replace(reg,(a,b,c)=>{
			return b+"****"+c;
		})
		
		console.log(phone);

总结:何时前加^后加$,何时又该添加后缀g

    1、前加^后加$ - 验证
    2、替换,你希望替换所有 - 必须加g

3、正则对象:

创建:
	1、直接量:var reg=/正则表达式/后缀;
	2、构造函数:var reg=new RegExp("正则表达式","后缀");

API:
	1、验证:var bool=reg.test(用户输入的);

animate.css文件

是一个动画库:放着很多很多的动画。

为了我们程序员方便,怕我们自己画的丑。 如何使用:

  • 1、打开百度:搜索animate.css 得到网址
    www.animate.net.cn/
    animate.style/
  • 2、下载 - 你们不用做这个操作
  • 3、引入此文件
  • 4、挑选你喜欢的动画,把class放到那个元素上
  • 5、并且要记得设置上animation-duration:3s; 执行时长
  • 6、还需要根据不同的动画,设置不同的初始效果,才会更好看

滚动轮播:

如果你会淡入淡出轮播,其实差不多,布局不同,动画方式不同

 var divImg=document.querySelector('.div_img'),
        imgs=document.querySelectorAll('.div_img>img'),
        btns=document.querySelectorAll('button'),
        lis=document.querySelectorAll('li');
        var j=0;
        judge(j);
        btns.forEach(btn=>{
            btn.onclick=function(){
                if(this.innerText=='>'){
                    j++;
                    if(j==lis.length){j=0};
                    setImg(j)
                }else{
                    j--;
                    if(j==-1){j=lis.length-1};
                    setImg(j)
                }
                judge(j);
            }
        })
        lis.forEach((li,i)=>{
            li.onclick=function(){
                setImg(i)
                j=i;
                judge(j);
            }
        })

        function setImg(a){
            divImg.style.marginLeft = a * (-100) + '%';
            lis.forEach(li => li.className = '');
            lis[a].className = 'active';
        }

        function judge(k) {
            if (k == 0) {
                btns[0].setAttribute('disabled', 'disabled')
            } else {
                btns[0].removeAttribute('disabled')
            }
            if (k == 2) {
                btns[1].setAttribute('disabled', 'disabled')
            } else {
                btns[1].removeAttribute('disabled');
            }
        }

无缝轮播:

var divImg = document.querySelector('.divImg'),
    lis = document.querySelectorAll('li'),
    btns = document.querySelectorAll('button'),
    srcs = ['../img/1.jpg', '../img/2.jpg', '../img/3.jpg'],
    state=0,
    j = 0;
btns.forEach(btn => {
    btn.onclick = () => {
        if (state == 0) {
            state = 1;
            if (btn.innerText == '>') {
                j++;
                j == lis.length && (j = 0);
                createImg(j, -1);
            } else {
                j--;
                j == -1 && (j = lis.length - 1);
                createImg(j, 1);
            }
        }
    }
})
lis.forEach((li,i) => {
    li.onclick = () => {
        console.log(i);
        if (state == 0) {
            state = 1;
            j = i;
            var oldLi = document.querySelector('li.active'),
                oldj = oldLi.getAttribute('tc');
            if (j > oldj) {
                createImg(j, -1);
            } else if (j < oldj) {
                createImg(j, 1);
            }
        }
    }
})

setInterval(() => {
    if (state == 0) {
        j++;
        j == lis.length && (j = 0);
        createImg(j, -1);
    }

},2000)


function createImg(k,a) {
    var img = new Image();
    if (a==-1) {
        img.className = 'rimg';
    } else {
        img.className = 'limg';
    }
    
    img.src = srcs[k];
    divImg.appendChild(img);

    var imgs = document.querySelectorAll('img');
    img.onload = () => {
        imgs[0].style.left = a * 100 + '%';
        imgs[1].style.left = '0%';

        setTimeout(() => {
            imgs[0].remove();
            state = 0;
        }, 1000);
    }
    lis.forEach(li => li.className = '');
    lis[k].className = 'active';
}

swiper插件:

专门的一个轮播插件,提供了你HTML/CSS/JS,我们只需要复制。

1、打开百度:搜索swiper

2、选你自己喜欢的

封装一个运动(动画)函数

btn.onclick = () => {
    //div.style.transition="1s";
    //div.style.width="600px";
    //div.style.height="600px";
    //div.style.background="green";
    //div.style.borderRadius="50%";
    //div.style.border="20px solid blue";
    //div.style.transform="rotateY(360deg)";

    move(div, {
        "transition": "1s",
        "width": "600px",
        "height": "600px",
        "background": "green",
        "borderRadius": "50%",
        "border": "20px solid blue",
        "transform": "rotateY(360deg)",
    })
}

function move(elem, obj) {
    for (var i in obj) {
        elem.style[i] = obj[i];
    }
}

Object:对象

Array/String/RegExp/Date... 对象具有属性和方法,都是预定义好的,现在我们可以学习自定义对象 - js是基于原型的面向对象语言

面向对象 - 三大特点:封装、继承、多态

1、开发方式:

面向过程:过程 - 开始->结束,其实我们一致的开发方式都是面向过程:先干什么再干什么最后干什么

面向对象:对象(属性和方法),js有一句话万物皆对象

何时使用面向对象:以后任何操作都要封装在一个对象之中 - 但是新手并不太推荐,难度较大

为什么要面向对象:现实生活中所有的数据都必须包含在一个事物之中才有意义

2、封装/创建/定义:

1、封装自定义对象:3种

1、直接量方式:

    var obj={
            "属性名":属性值,
            ...,
            "方法名":function(){操作},//可以简化为箭头函数
            ...
    }

强调:

  • 1、其实属性名和方法名的""可以不加 - 暂时建议你加上,以后我们要学习一个数据格式JSON,他必须在键上加上""

  • 2、访问对象的属性和方法:

      obj.属性名;	===	obj["属性名"];
      obj.方法名();	===	obj["方法名"]();
      建议使用.去访问对象的属性和方法,更简单
      ***js中一切都是对象,除了undefined和null,一切对象的底层都是hash数组
    
  • 3、访问到不存在的属性,返回undefined

  • 4、可以随时随地的添加新属性和新方法 obj.属性名=新值; obj.方法名=function(){};

  • 5、如果我希望遍历出对象所有的东西,必须使用for in,obj[i]才能拿到,不要使用.会出问题

  • 6、如果你希望在对象的方法里,使用对象自己的属性,写为this.属性名
    难点:this的指向: 1、单个元素绑定事件,this->这个元素 2、多个元素绑定事件,this->当前元素 3、定时器中的this->window 4、箭头函数this->外部对象 5、函数中this->谁在调用此方法,this就是谁 6、构造函数之中this->当前正在创建的对象

2、预定义构造函数方式:

    var obj=new Object();//空对象
    //需要自己后续慢慢添加属性和方法
    obj.属性名=新值;
    obj.方法名=function(){};

以上两个都有一个缺陷:一次只能创建一个对象,适合创建单个元素的时候(第一种方法),第二种方法完全是垃圾,如果你要批量创建多个对象,那么我推荐第三种方法

3、自定义构造函数方式:2步

    1、创建自定义构造函数
            function 类名(name,age,hobby){
                    this.name=name;
                    this.age=age;
                    this.hobby=hobby;
            }
            //千万不要在里面创建方法,每个对象都会创建出一个相同的方法,浪费内存 - 学习继承后可以解决

2、调用构造函数创建对象
	var obj=new 类名(实参,...);

面向对象:优点: 1、逼格高,所有的属性和方法都保存在一个对象之中 - 更符合现实更有意义 2、每个功能特地分开写 - 便于以后维护 3、铁锁链舟 - 一个方法触发多个方法联动
缺点:对新手不友好,尤其是this的指向问题

2、继承:

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

为什么要继承:代码重用!提高代码的复用性,节约了内存空间!提升了网站的性能!

何时继承:只要多个子对象共用的属性和【方法】,都要集中定义在父对象之中

1、如何找到原型对象(父对象):

保存了一类子对象共有属性和共有方法
1、对象名.proto; //必须先有一个对象
2、构造函数名.prototype;//构造函数名几乎人人都有,除了Math和Window,new 构造函数名();//Array、String、Date、RegExp...

2、面试题:两链一包:作用域链和【原型链】和闭包

每个对象都有一个属性:proto,可以一层一层的找到每个人的父亲,形成的链式结构,就称之为叫做原型链
可以找到父对象的成员(属性和方法),作用:找共有属性和共有方法
最顶层的是Object的原型,上面放着一个我们眼熟的方法toString,怪不得人人都可以使用toString JS万物皆对象

3、有了原型对象,可以设置共有属性和共有方法

1、原型对象.属性名=属性值;
2、原型对象.方法名=function(){}

继承具有很多的面试笔试题:

1、判断是自有还是共有:

1、判断自有:obj.hasOwnProperty("属性名");

如果结果为true,说明是自有属性,如果结果为false,有两种可能,说明可能是共有,也可能是没有

2、判断共有:

    if(obj.hasOwnProperty("属性名")==false&&"属性名" in obj){//in关键字,会自动查找整条原型链上的属性,找到了结果为true,找不到结果为false
            共有				
    }else{
            没有
            }

    完整公式:
            if(obj.hasOwnProperty("属性名")){
                    自有
            }else{
                    if("属性名" in obj){
                            共有
                    }else{
                            没有
                    }
            }

2、修改和删除:

自有:

修改:obj.属性名=新属性值;
删除:delete obj.属性名;

共有:

修改:原型对象.属性名=新属性值;//千万不要觉得,自己能拿到,就能直接修改,这样很危险,并没有修改原型的东西,而是在本地添加了一个同名属性
删除:delete 原型对象.属性名;//如果你对着本地直接删除,那么此操作直接无效

3、如何为老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;
      		}
      	}
      	var arr1=[1,2,3,4,5];
      	var arr2=[2,4,6,8,10];
    

4、如何判断x是不是一个数组:4种方法:

千万别用typeof(),只能检查原始类型,不能检查引用类型,如果检查引用类型得到的结果都是一个object。

1、判断x是否继承自Array.prototype:
Array.prototype.isPrototypeOf(x); 结果为true,说明是数组,结果为false,说明不是数组

2、判断x是不是由Array这个构造函数创建的
x instanceof Array; 结果为true,说明是数组,结果为false,说明不是数组

3、Array.isArray(x); - ES5新增的方法,只有数组可以这么使用
结果为true,说明是数组,结果为false,说明不是数组

4、输出【对象的字符串】形式
在Object的原型上保存着最原始的toString方法
原始的toString输出形式:[object 构造函数名]
多态:子对象觉得父对象的成员不好用,就在本地定义了同名函数,覆盖了父对象的成员,不严格定义:同一个方法,不同的人使用,效果不同,有多种形态

固定套路: Object.prototype.toString.call(x)==="[object Array]"

5、实现自定义继承:

1、两个对象之间设置继承

子对象.proto=父对象

2、多个对象之间设置继承

构造函数名.prototype=父对象;
时机:应该在开始创建对象之前就设置好继承关系

class关键字:

简化面向对象(封装、继承、多态)

class 类名 extends 老类{
	constructor(name,age,hobby,...){//放在constructor里面的都是自有属性
		super(name,age);
		this.hobby=hobby;
	}//放在constructor外面的都是共有方法
	//还会继承到老类所有的API,也可以添加新的
}

Function:闭包

作用域:2种

  • 1、全局:随处可用,可以反复使用,缺点:容易被污染

  • 2、函数:只能在函数调用时内部可用,不会被污染,缺点:一次性的,是会自动释放的

函数的执行原理:

  • 1、程序加载时
    创建执行环境栈(ECS):保存函数调用顺序的数组
    首先压入全局执行环境(全局EC)
    全局EC引用着全局对象window
    window中保存着我们全局变量

  • 2、定义函数时
    创建函数对象:封装代码段
    在函数对象之中有一个scope(作用域)属性:记录着函数来自己的作用域是哪里
    全局函数的scope都是window

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

  • 4、调用时:
    正是因为有前面三步,才来带变量的使用规则:优先使用自己的,自己没有找全局,全局没有就报错

  • 5、调用完:
    函数的EC会出栈,没人引用AO,AO自动释放,局部变量也就释放了

闭包:

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

何时使用:希望保护一个可以【反复使用的局部变量】的时候

如何使用: 1、两个函数进行嵌套 2、外层函数创建出受保护的变量 3、外层函数return出内层函数 4、内层函数再操作受保护的变量

强调:
1、判断是不是闭包:有没有两个函数嵌套,返回了内层函数,内层函数再操作受保护的变量
2、外层函数调用了几次,就创建了几个闭包,受保护的变量就有了几个副本
3、同一次外层函数调用,返回的内层函数,都是在操作同一个受保护的变量

缺点:受保护的变量,永远都不会被释放,使用过多,会导致内存泄漏 - 不可多用

问题:应该在哪里去使用呢? 1、三个事件需要防抖节流 - 共同点:触发的速度飞快
1、elem.onmousemove - 鼠标移动事件
2、input.oninput - 每次输入/改变都会触发
3、onresize - 每次窗口改变大小都会触发

防抖节流的公式:

		function fdjl(){
			var timer=null;
			return function(){
				if(timer!==null){clearTimeout(timer);timer=null;}
				timer=setTimeout(()=>{
					操作
				},500)
			}
		}
		
		var inner=fdjl();

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

总结:

两链一包:

  • 1、作用域链:
    以函数的EC的scope chain属性为起点,经过AO,逐级引用,形成的一条链式结构,我们就称之为叫做作用域链
    作用:查找变量,带来了变量的使用规则:优先使用自己的,自己没有找全局,全局没有就报错

  • 2、原型链:
    每个对象都有一个属性叫做.proto,可以一层一层的找到每个对象的原型对象,最顶层的就是Object的原型,形成的一条链式结构,我们就称之为叫做原型链
    作用:查找属性和方法,哪怕自己没有也会顺着原型链向上找,怪不得人人都能用toString(),因为他在最顶层

  • 3、闭包: 希望保护一个可以【反复使用的局部变量】的一种词法结构,其实还是一个函数,只是写法比较特殊
    作用:专门用于防抖节流