深入JavaScript高级语法

128 阅读16分钟

JavaScript

前端学习100天思维导图.png

  • 什么是

    • 前端三大语言

      • HTML

        • 专门编写网页内容的语言
      • CSS

        • 专门编写网页样式的语言
      • JS

        • 为什么

          • 仅用HTML和CSS编写的网页,只能看不能用!
          • 有些不需要服务器端数据,就可执行的功能,就不应该反复与服务器交互,频繁让用户等待。
        • 发展史

        • 专门编写客户端交互行为的语言

          • 交互(IPO)

              1. 用户输入
              1. 程序接受输入,处理数据
              1. 输出处理结果
        • 何时

          • 今后,凡是HTML和CSS编写的网页,必须用js添加交互行为后,才能让用户使用
          • 今后,不需要服务器数据,就可执行的操作,只要在客户端用JavaScript实现即可,不用频繁与服务器交互
    • 如何使用JavaScript

      • 在何处编写JavaScript

        • 2处

            1. HTML网页中的script标签内

            • 问题:

              • 无法重用

                • 程序的准则:

                  • DRY
                • 尽量:

                  • 一次定义,处处使用,一次修改,处处生效
            • 解决

            1. 在独立的js文件中编写JavaScript

            • 再用script标签引入网页中

      • 如何运行

        • 脚本解释引擎

          • 解析并执行js程序的小软件
        • 如何

            1. 浏览器已经自带脚本解释引擎

            • 浏览器包含两个小软件

              • 内容排版引擎

                • 解析HTML和CSS的程序

                  • 效率高
              • 脚本解释引擎

                • 解析js并运行js程序的小程序

                  • 效率比排版引擎略低
            1. 独立安装

            • Node.js
      • JavaScript语言基本语法

          1. 区分大小写
          1. 字符串必须用引号包裹, 但单双引号都行

          • 特殊

            • 如果字符串内又包含引号冲突, 只要内外使用不同的引号区分即可
          1. 每句话结尾必须用分号结束
          1. 注释

          • //单行注释
          • /* 多行注释 */
      • 调试JavaScript

        • 只要想要的效果没出来,就是出错了
        • 只要出错,先开F12控制台(Console)
        • Console中显示错误的原因,和出错位置
    • JavaScript能做什么

      • 客户端表单验证
      • 客户端数据计算
      • 客户端动画效果和网页游戏
      • 总之: js可对网页中的一切内容执行一切想要的操作——只有想不到,没有做不到
    • 常用输出

      • 向网页中输出内容

        • document.write("HTML片段或文字")
        • 问题: 影响网页中现有结构和内容
      • 弹出警告框

        • alert("提示内容...")

        • 问题:

            1. 样式写死,不可定制修改
            1. 模态,阻碍用户操作
      • 向控制台输出:

        • console.log("输出内容...")
      • 今后,凡是调试程序,查看输出结果,都要在控制台输出

    • 控制台的使用

        1. 单行代码按回车直接执行
        1. 切换出之前执行过的代码记录,微调

        • 按键盘上下按键切换
        1. 清屏

        • 点控制台左上角圆形叉号
        1. 仅换行输入,不执行

        • shift+enter
    • 原生js

      • 不依赖于第三方文件,仅依靠浏览器就可直接执行的代码

      • 包含3大部分

        • ECMAScript

          • 规定了js语言核心语法的标准

            • 3 5 6(2015)

            • JavaScript

              • 网景(Mozilla火狐)参照ES标准实现的JavaScript语言版本
            • JScript

              • 微软参照ES标准实现的JavaScript语言版本
        • DOM

          • 专门操作网页内容的API

          • 比如:

            • document.write(...)

              • 向网页内容中输出一行话
        • BOM

          • 专门操作浏览器窗口的API

          • 比如:

            • alert,prompt

              • 弹出浏览器的提示框
  • 变量

    • 什么是

      • 内存中存储一个数据的存储空间,再起一个名字
    • 为什么

      • 重用一个数据
    • 何时

      • 只要一个数据可能被反复使用时,都要保存在变量中
    • 如何

      • 声明

        • 什么是

          • 在内存中创建一块存储空间,再起一个名字
        • 何时

          • 任何变量在使用前,必须先声明
        • 如何

          • var 变量名;

            • 变量名

                1. 字母,数字,下划线和$组成,但不能以数字开头
                1. 不能使用保留字
                1. 见名知义
                1. 驼峰命名
        • 默认值

          • 仅声明,未赋值的变量,默认值都是undefined
        • 简写

          • var 变量1, 变量2, ...;
      • 赋值

        • 变量名=值

          • 将等号右边的值保存到等号左边的变量中
        • 只有=才能赋值

        • =不是追加,而是完全替换!

          • 覆盖原值
        • 特殊

          • 如果给未声明的变量强行赋值

            • 普通模式

              • 不会报错!

              • 会自动在全局创建该变量

                • 全局污染
            • ES5

              • 严格模式

                • 什么是

                  • 比普通js运行模式要求更严格的运行机制
                • 要求1:

                  • 禁止给未声明的变量赋值

                    • 报错!

                      • 变量未定义
              • 如何启用:

                • 在当前代码段的顶部先插入"use strict";
            • 总结

              • 禁止使用!
      • 简写

        • 问题:

          • 变量的默认值undefined是造成最多错误的根源
        • 强烈建议

          • 声明变量同时初始化变量的值

            • var 变量名=值
        • 特殊

          • 同时声明并初始化多个变量

            • var 变量1=值1, 变量2=值2, ...;
          • 鄙视

            • var a , b=2;

              • a: undefined
              • b: 2
      • 取值

        • 任何情况下使用变量名等效于复制出变量中的值使用

        • 尝试从未声明的变量中取值都会报错!

          • ReferenceError
    • 常量

      • 什么是

        • 一旦创建,值不能改变的量
      • 何时

        • 只要程序中的一个值,任何情况下都不能改变
      • 如何

        • const 常量名=值;
      • 强调:

        • 常量名必须全大写
        • 创建常量时必须立刻赋值
      • 强行修改常量的值:

        • 在最新的Chrome浏览器中,已经内置了对常量赋值的检查

        • 在ES3标准中,普通运行模式下,强行给常量赋值

          • 不报错!

          • 也不让修改

          • 静默失败

            • 解决

              • 启用严格模式

              • 要求:

                • 将所有静默失败升级为错误!
  • 数据类型

    • 什么是

      • 数据在内存中的存储格式
    • 为什么

      • 不同类型的数据,可执行的操作不一样
      • 不同的操作,要求专门的存储结构支持
    • 包括

      • 原始类型

        • 值直接保存在变量本地的数据类型
        • number string boolean null undefined
      • 引用类型

        • 值无法保存在变量本地的复杂数据类型

          • 如果一个复杂的数据结构,同时保存多个数据,就不能直接保存在变量中
          • 存储在window之外的一块独立存储空间
          • 独立存储空间中可同时存储多个数据,拥有唯一的地址
          • 变量名仅保存存储空间的地址
    • number

      • 什么是

        • 专门存储数字(即包含整数,也包含小数)的类型
      • 何时

        • 如果一个数值可能需要比较大小和进行数学计算时
      • 如何

        • 程序中只要不加引号的数字,自动就是number类型
      • 底层都是二进制存储

        • n.toString(2)
      • 存储

        • 整数占4字节
        • 浮点数占8字节
        • 存储空间和数值大小无关
    • string

      • 什么是

        • 专门存储一串用于显示的文字
      • 何时

        • 记录一串文字

          • 不用做比较和计算时
      • 如何

        • 凡是加引号的一串字符直接量称为字符串类型
      • 存储

        • unicode

          • 对全球主要语言的每个字编一个号

          • 为什么

            • 计算机不认识字符,只认识数字
          • 范围:

            • “0”~“9”

              • 48~57
            • "A"~"Z"

              • 65~90
            • "a"~"z"

              • 97~122
            • 汉字

              • “\u4e0”~"\u9fa5"
          • 每个字母/数字字符,占1字节

          • 每个汉字,占2字节

        • 其他

          • utf-8

            • 字母数字1字节
            • 汉字3字节
          • GBK

            • 字母数字1字节
            • 汉字2字节
    • boolean

      • 什么是

        • 专门表示判断结果的
      • 何时

        • 只要作为判断条件的结论时
      • 如何

        • 只有两个值

          • true/false

            • 不加引号
    • undefined

        • 专门由程序自动为一个变量赋初值

          • 程序员很少主动使用
    • null

        • 专门给程序员用于手动清空一个变量之用
  • 数据类型转换

    • 弱类型语言

        1. 声明变量时无需提前规定变量中存储数据的类型
        1. 一个变量先后可保存不同类型的数据
        1. js会根据自身的需要,隐式转化数据的类型
    • 什么是

      • 将数据从一种类型转化为另一种类型
    • 何时

      • 只要数据类型不是想要的
    • 2大类

      • 隐式转换

        • 无需程序员干预,程序自动完成的类型转换

        • 何时

          • 如果给定的数据类型和js运算要求的数据类型不相符,都会首先自动类型转换为规定的类型,再运行
      • 强制转换

        • 什么是

          • 程序员主动调用转化函数实现的类型转换
        • 何时

          • 只要隐式转换的结果不是想要的
        • 3种情况

          • 转为number

            • 2种

                1. 将纯数字组成的字符串和bool类型转为number

                • Number(x)

                  • Number(true)->1 Number(false)->0

                  • Number(null)->0 , Number("")->0

                  • Number(undefined)->NaN

                  • 其实也可以转字符串

                    • 只能转纯数字组成的字符串
                • 其实,所有隐式转化都是自动调用Number()

                1. 将字符串转为number

                • parseFloat(str)

                  • 将str转为number类型的浮点数(小数)

                  • 原理

                    • 从开头开始,依次读取每个字符
                    • 只要碰到数字和小数点就保留
                    • 直到碰到第一个不是数字和小数点的字符就不再继续读取
                  • 总结:

                    • 可保留小数

                      • 仅认识第一个小数点
                    • 去掉结尾的非数字字符

                      • 无法去掉开头的非数字字符
                  • 何时

                    • 只要希望去掉字符串结尾的单位时
                • parseInt(str)

                  • 将str转为number类型的整数

                  • 原理

                    • 和parseFloat完全一样
                    • 只是不认识小数点
                  • 总结

                    • 去掉小数
                    • 去掉结尾的非数字字符
                  • 何时

                    • 只要希望去掉结尾的单位,且同时舍弃小数部分时
                • 特殊:

                  • parseFloat(null/true/false)

                    • NaN
                    • 先将null/true/false隐式转化为字符串,再按照原理转为数字
            • 总结:

              • 如果将字符串转数字

                • 首选parseFloat

                  • 除非确定要舍弃小数部分采用parseInt
              • 如果将非字符串转数字

                • 首选Number

                  • 很少主动使用
            • NaN

              • Not a Number

              • js中number类型的一个特殊值

                • 代表一切不是数字的值
              • 只要将其它类型转为数字时,无法正常转换,都转为NaN

              • 特点:

                  1. 参与任何计算,结果依然是NaN
                  1. 不大于,不小于,不等于任何值
          • 转为string

            • x.toString()

              • 问题:

                • x不能是null和undefined
              • 解决

            • String(x)

              • 万能
              • 其实隐式转换都是自动调用String()
          • 转为boolean

            • Boolean(x)

              • 规则

                • 只有五个值被转为false

                  • 0, null, NaN, undefined, ""
                • 其余任何值都转为true

  • 运算符和表达式

    • 概念

      • 程序

        • 人的想法在计算机中的执行
      • 运算符

        • 程序中模拟人的想法的特殊符号
      • 表达式

        • 由数据,变量和运算符组成的一条程序的语句

        • 执行过程

          • 从左向右,依次执行

            • 每读到一个变量就将变量换成变量中的值
    • 包括

      • 算数计算

              • / %
          • %

            • 被除数/除数,不取商,取除不尽的余数部分

            • 何时:

                1. 取余数
                1. 判断能否整除

                • 比如

                  • 判断奇偶数
                  • 判断闰年
        • 隐式转换

          • 默认

            • 一切转为number,再算数计算
          • 特殊

            • +运算中只要有一个字符串

              • 则全部转为字符串

              • +计算改为字符串拼接

              • 问题:

                • 凡是从页面上获得的数据都是字符串
              • 解决:

                • 做算数计算前,必须用parseFloat()强转为数字类型
      • 关系运算

        • 做比较,做判断

        • < >= <= != ==

        • 返回值

          • 只能是true/false
        • 隐式转换

          • 默认

            • 一切都转为number,再做比较
          • 特殊

            • 两个string类型做比较

              • 不再转为number

              • 而是按位PK每个字符的unicode号

                • 只要一位字符能比较出结果,就不再继续
            • null和undefined

              • 问题

                • 用==无法区分null和undefined

                  • 普通关系运算会将undefined先隐式转为null再做比较
              • 解决

                • ===

                  • 先要类型相同,然后再值相等

                  • 不带隐式转换的==比较

                    • 强烈建议使用===代替==
                • !==

                  • 不带隐式转换的!=比较

                    • 强烈建议使用!==代替!=
            • NaN

              • 问题

                • 无法判断是不是NaN

                  • NaN不等于,不大于,不小于一切

                  • 因为NaN表示所有不是数字的内容

                    • 是一个范围,不表示一个具体值

                      • 做比较无任何意义
              • 解决

                • isNaN(num)

                  • 专门判断num是不是NaN

                  • 经常反用

                    • 只要不是NaN,一定是数字

                    • !isNaN(num)

                      • 专门用来判断num是不是有效的数字
            • 两个对象做==比较

              • 不再做任何转换,而是比较两个对象的地址是否相同

                • 判断两个变量是否引用同一个对象

                • 比如

                  • []==[]

                    • false
      • 逻辑运算

        • 将多个关系运算,组合起来,综合得出最终的结论

        • 返回true/false

        • 3种

            1. 逻辑与&&

            • 而且

              • 条件1&&条件2

                • 必须同时满足条件1和条件2才返回true
                • 只要一个条件不满足,都返回false
            1. 逻辑或

            • 或/要么

              • 条件1||条件2

                • 只要满足条件1或条件2中任意一个条件就返回true
                • 除非所有条件都为false时,才为false
            1. 逻辑非

            • 不/没有

              • !条件

                • 颠倒条件的判断结果
        • 隐式转换

          • 默认

            • 每个条件都转为bool类型,再联合判断
        • 短路逻辑

          • 在逻辑运算中,如果前一个条件已经可以得出最终的结论,则后续条件不再执行

          • &&

            • 如果前一个条件为false,则后续条件不再执行,直接返回false
          • ||

            • 如果前一个条件为true,则后续条件不再执行,直接返回true
          • 利用短路

            • 利用&&的短路

                1. 实现简单分支

                • 一个条件一件事,满足条件才执行,不满足就不执行
                • 条件&&(操作)
            • 利用||的短路

                1. 定义默认值

                • 值1||默认值

                  • 优先使用值1
                  • 如果值1转为bool后为false,才使用默认值作为备用
      • 位运算

        • 左移和右移

          • 左移

            • m<<n

              • 将m的二进制数左移n位

                • 相当于m* 2的n次方
          • 右移

            • m>>n

              • 将m的二进制数右移n位

                • 相当于m/ 2的n次方
        • 下取整

          • m>>>0
          • m|0
          • m^0
        • 不声明第三个变量,交换两变量的值

          • 方法一:

            • a^=b; b^=a; a^=b;
          • 方法二:

            • a+=b; b=a-b; a-=b;
          • 问题: 只能交换数字类型的数据

          • 方法三:

      • 赋值运算

        • 赋值运算也有返回值

          • 返回保存到等号左边的变量中的新值
        • 扩展赋值运算

          • 一种简写

            • 包括

              • a+=b

                • 累加

                • a=a+b

                  • 如果只是+1

                    • 更简化

                      • 递增
                      • a++
              • a-=b

                • a=a-b

                  • 如果只是-1

                    • 更简化

                      • 递减
                      • a--
              • a*=b

              • a/=b

              • a%=b

          • 何时

            • 只要先取出变量值,计算后,想再保存回原变量时
          • 递增/递减1

            • ++a vs a++

              • 单独使用

                • 没有差别
              • 如果嵌入其它表达式中

                • 相同

                  • 变量a中的值,一定都会被+1
                • 不同

                  • 返回值

                    • ++a,返回+1后的新值
                    • a++,返回+1前的旧值
  • 函数

    • 什么是

      • 封装一项任务步骤清单的代码段,再起一个名字
      • 函数名其实只是保存函数对象地址的变量
      • 函数也是引用类型的对象
    • 为什么

      • 代码重用
    • 何时

      • 只要一项任务可能被反复使用,都要先定义在函数中,再反复调用函数
    • 如何

      • 声明函数

        • function 函数名(参数列表){ 函数体; return 返回值; }

          • 参数

            • 函数执行过程中接收必须数据的变量

            • 为什么

              • 有些功能必须某些外来数据才能正常执行
              • 参数可让函数变的更灵活
            • 何时

              • 只要函数本身需要某些数据才能正常执行时
            • 如何

              • 声明

                • 函数名后的圆括号中, 用逗号分隔每个参数名

                  • 不用加var
              • 访问

                • 在函数内,参数的用法和普通变量完全一样
          • 返回值

            • 函数执行的结果

            • 何时

              • 只要调用者需要获得函数的执行结果时
            • 如何

              • 函数结尾 return 值

                • 向外部抛出函数处理结果的主要手段
      • 调用

        • 让引擎找到函数,按照函数的步骤清单执行程序

        • 如何

          • var 返回值=函数名(参数值列表)

            • 如果函数定义时,定义了参数列表,调用时必须传入参数值

              • 传入的参数值的顺序和个数必须和函数定义中的参数列表一致
            • 如果函数定义时,定义了返回值,调用时可以用变量接收函数的返回值

    • 作用域(scope)

      • 什么是

        • 一个变量的可用范围
      • 为什么

        • 避免不同范围的变量间互相污染
      • 包含:

        • 2种

          • 全局作用域

            • window

            • 全局变量

              • 什么是

                • 在函数外声明的不属于任何函数的变量
              • 特点

                • 随处可用
                • 可反复使用
              • 何时:

                • 只要一个变量希望反复使用或跨多个函数随处可用时
          • 函数作用域

            • 函数内部

            • 局部变量

              • 包含2种

                • 在函数内用var声明的变量
                • 参数变量也是局部变量
              • 特点

                • 仅函数内可用
                • 不可重用
              • 何时:

                • 如果希望一个变量仅在函数内可用,不希望污染函数外部时
      • 使用顺序

        • 优先在函数作用域中找局部变量使用
        • 如果局部没有,才去全局找
        • 如果全局没有,才报错
    • 声明提前

      • 在程序正式执行前, 先将所有var声明的变量和function声明的函数 提前到当前作用域的顶部,集中创建

        • 赋值留在原地
      • 问题

        • 破坏了程序执行的顺序
      • 避免

          1. 尽量将变量或函数的声明集中在当前作用域顶部创建
          1. 用let代替var

          • 不允许在let前使用未声明的变量
          • 不允许重复声明变量
          1. 用let 函数名=function(...){...}代替function 函数名(...){...}
    • 按值传递byValue

      • 在两变量间赋值时,或将变量作为参数传入函数时,仅将原变量中的值复制一个副本给对方

      • 影响

        • 如果传递的是原始类型的值,在函数中修改新变量,不会影响原变量
        • 如果传递的是引用类型的对象,在函数中用新变量修改对象,等效于直接修改原对象
  • 全局函数

    • ES标准中规定的,不需要任何前缀.就可直接调用的函数

    • 比如:

      • Number() String() Boolean() isNaN() parseFloat/parseInt()
      • encodeURI/decodeURI()
      • encodeURIComponent()/decodeURIComponent()
      • eval()
    • 反例

      • document.write() console.log();
      • alert() prompt()
  • 分支结构

    • 让程序根据不同的条件,执行不同的任务

    • 包括

        1. 一个条件一件事,满足就做,不满足就不做

        • 如果操作简单

          • 条件&&(操作1,操作2,...)
        • 如果操作复杂

          • if(条件){ 操作 }
        1. 一个条件2件事,二选一执行

        • 如果操作复杂

          • if(条件){ 操作1 }else{ 操作2 }
        • 如果操作简单

          • 三目/三元/条件

            • 条件?操作1:操作2;
        • 如果根据不同条件选择不同的值

          • 三目

            • 条件?值1:值2
        1. 多个条件多件事,多选一执行

        • 如果操作复杂

          • if(条件1){ 操作1 }else if(条件2){ 操作2 }else if(...){ ... }[else{ 默认操作 }]

            • 最后一个else不是必须
        • 如果操作简单

          • 条件1?操作1: 条件2?操作2: ... ? ... : 默认操作

            • 最后必须有一个默认操作
        • 如果只是根据不同条件返回不同的值时

          • 条件1?值1: 条件2?值2: ... ? ... : 默认值

            • 默认值不能省略!
        • 特殊

          • 如果所有条件都是等于比较时

            • switch(表达式){ case 值1: 操作1; break; case 值2: 操作2; break; case ... : ... ; break; [default: 默认操作] }

              • 原理:

                • 先计算表达式的值
                • 再用表达式的值和每个case做===比较
                • 只要表达式的值和某个case的值全等,就进入执行该case下的操作
              • 强调:

                • 表达式的值和case的值做全等比较,意味着首先类型必须相同

                • 每个case其实仅是一个入口而已

                  • 问题

                    • 默认

                      • 一但进入一个case开始执行,则会连续触发之后所有case和default的操作
                  • 解决

                    • 在每个case之间加break

                      • break退出当前结构
  • 循环结构

    • 让程序反复执行一段相同代码

    • 循环三要素

      • 循环条件

        • 控制循环可以继续反复执行的条件
        • 每执行完一次重复的操作,都要重新判断一次循环条件是否满足
        • 一旦循环条件不再满足,则退出循环,不再反复执行
      • 循环变量

        • 循环条件中用作比较和判断的变量

        • 都要考虑

          • 从几开始,到几结束,每次增/减几
        • 通常都会向着不满足循环条件的趋势不断变化

      • 循环体

        • 循环反复执行的代码段
    • 三种

      • while

        • 声明并初始化循环变量 while(循环条件){ 循环体; 修改循环变量; }

        • 何时

          • 循环变量的变化没有规律时
          • 只要不满足条件就一次都不能执行时
      • do while

        • 声明循环变量 do{ 循环体; 修改循环变量; }while(循环条件);

          • vs while

            • 如果第一次循环条件都满足

              • 二者效果完全相同
            • 如果第一次循环条件不满足

              • while是一次都不执行
              • do while至少可以执行一次
        • 何时

          • 即使第一次条件就不满足,也希望至少能执行一次时
      • for

        • for(声明循环变量; 循环条件; 修改循环变量){ 循环体; }

        • 何时

          • 循环变量的变化是有规律的
        • 简写

            1. 声明循环变量部分,可同时声明并初始化多个变量,用逗号分隔

            • 问题: 循环内声明的变量,循环外是否可用

            • 答: 可用

              • 因为js没有块级作用域

              • vs Java

                • 三级作用域

                  • 全局

                  • 函数

                  • 块级作用域

                    • if...else, while, do...while, for
            1. 修改循环变量部分,可同时执行多个短小的操作,用逗号分隔

            • 不能修改原程序的执行顺序

            • 结尾的分号不能省

              • 了解

                • 其实分支结构和循环结构中,如果if/else/else if/for/while之后只有一句话,可省略{}

                  • 禁止使用!
      • 死循环

        • 循环条件永远为true的不能自己退出的循环
        • while(true)
        • for(;;)
      • break和continue

        • break

          • 退出当前结构,不再循环
        • continue

          • 仅跳过本轮循环,依然继续执行下一轮
      • 退出循环

        • 2种办法

            1. 用循环条件控制退出

            • 优雅
            • 难度高
            1. 用死循环+break方式退出

            • 野蛮
            • 简单
  • 数组

    • 什么是

      • 内存中连续存储多个数据的存储空间,再起一个名字
    • 为什么

      • 连续存储的一组数据,可极大提高程序的执行效率

        • 便于维护和查找
    • 何时

      • 只要存储多个数据都要用数组
    • 如何

      • 创建

          1. 创建空数组

          • 何时

            • 如果创建数组时,暂时不知道数组的内容
          • 用new

            • var arr=new Array();
          • 数组直接量

            • var arr=[]
          1. 创建数组同时初始化数组元素

          • 何时

            • 如果创建数组时,已经知道数组内容
          • 用new

            • var arr=new Array(值1,值2,...);
          • 数组直接量

            • var arr=[值1, 值2,...]
          1. 创建n个空元素的数组

          • 何时

            • 如果创建数组时只知道数组的元素个数,暂时不知道内容时
          • 用new

            • var arr=new Array(n)
      • 访问

        • 元素

          • 保存在数组中的一个数据
        • 下标

          • 数组中唯一标识元素存储位置的序号
          • 默认从0开始,依次递增,连续不重复,到length-1结束
        • arr[i]

          • 单个元素的用法和单个变量完全一样

            • 数组其实是一组变量的集合,再起一个统一的变量名
          • 三个不限制

            • 不限制元素的数据类型

            • 不限制元素个数

              • 可随时在任意位置添加新元素

                • 添加新元素后,都会自动改变length属性为最大数字下标+1
            • 不限制下标越界

              • 取值

                • 不报错,返回undefined
              • 赋值

                • 不报错,自动在指定位置创建新元素

                  • 稀疏数组

                    • 下标不连续的数组
                  • 自动将length调整到最大下标+1

      • .length属性

        • 规定了数组理论上的元素个数

        • 始终等于最大下标+1

          • 自动维护
        • 固定套路

          • 获取最后一个元素

            • arr[arr.length-1]

            • 获取倒数第n个元素

              • arr[arr.length-n]
          • 末尾追加一个新元素

            • arr[arr.length]=值
          • 缩容

            • 删除末尾一个元素

              • arr.length--

              • 删除末尾n个元素

                • arr.length-=n
            • 清空数组

              • arr.length=0
      • 数组是引用类型

        • 不直接存储在变量本地

          • 一个变量只能存1个值
        • 实际存储在变量之外

          • 每个数组都有一个唯一的地址值

            • 变量中只保存数组的地址值

              • 也称变量引用着数组
        • 按值传递

          • 两变量间赋值或将变量传给函数参数时,其实只是将原变量中的值复制一个副本给对方

          • 原始类型

            • 修改新变量,不影响原变量
          • 引用类型

            • 通过新变量修改数组,同样会影响原变量

            • 为什么

              • 将原变量中的地址值复制给新变量

                • 结果

                  • 两个变量引用同一个数组
      • 垃圾回收

        • 垃圾

          • 不再被任何变量使用的对象
        • 垃圾回收

          • 程序会自动释放不再被任何变量使用的垃圾对象的内存空间
        • 为什么

          • 内存中的空间都是有限的,内存占用越多,程序运行越慢
        • 垃圾回收器

          • js引擎中,专门监控,并释放垃圾的小程序
        • 如何

          • 垃圾回收器伴随主程序在后台并行执行
          • 垃圾回收器会记录每个对象被几个变量使用着
          • 只要发现一个对象,不再被任何变量使用,就释放该对象所占内存
        • 建议:

          • 只要一个变量不再使用,都要主动赋值为null
      • 遍历

        • 依次取出数组中每个元素的值,执行相同的操作

        • 何时

          • 只要对数组中每个元素执行相同操作时
        • 如何

          • 索引数组

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

        • 什么是

          • 可自定义下标名称的数组

            • vs 索引数组

              • 下标都是数字的数组
        • 为什么

          • 索引数组的数字下标没有意义,只能通过遍历查找指定的元素

            • 查找速度受数组元素个数和元素位置的影响
        • 何时

          • 希望通过下标名称快速查找某个元素时

            • 无需遍历

              • 不受元素个数和元素存储位置的影响
        • 如何

          • 创建

            • 2步

              • 创建空数组

                • var hash=[]
              • 向数组中添加新元素

                • hash["下标名(key)"]=值(value)
          • 访问

            • hash["下标名(key)"]

              • 用法同访问索引数组中的元素
          • 特点

            • .length属性始终为0
            • 无法使用索引数组的API
          • 遍历

            • for(var key in hash){ key //仅获取当前下标名称 hash[key] //获取当前元素值 }

              • in 依次取出关联数组中每个下标名称保存在变量key
            • 固定套路

              • 仅获取hash中的所有key

                • var keys=[]; var i=0; for(keys[i++] in hash); //结束后: keys中保存了hash的所有key
      • 数组API

        • 转字符串

          • String(arr)

            • 将arr中每个元素转为字符串,用逗号链接

              • 拍照
          • arr.join("连接符")

            • 将arr中每个元素转为字符串,可自定义连接符

            • 固定套路

              • 无缝拼接

                • arr.join("")
              • 判断空数组

                • arr.join("")===""
              • 动态生成页面元素

                • var html=“”+arr.join("")+""
                • elem.innerHTML=html;
        • 拼接和选取

          • 强调: 都无权修改原数组,只能返回新数组,必须用变量接住返回值

          • 拼接

            • var newArr=arr1.concat(值1,值2,arr2,....)

              • 将值1,值2,arr2中的每个元素,拼接到arr1结尾
            • 强调:

              • 可打散数组类型参数
          • 选取

            • var subArr=arr.slice(starti,endi+1);

              • 选取arr中starti位置到endi位置的元素,组成新数组返回
            • 强调:

              • 凡是两个参数都是下标的API

                • 含头不含尾
            • 简写

              • 负数参数

                • 倒数第n个

                  • 本质:

                    • 自动执行length-n
              • 省略第二个参数

                • 从starti一直选取到结尾
              • 省略全部两个参数

                • 复制整个数组

                  • 用途

                    • 将类数组对象转化为数组对象

                      • Array.prototype.slice.call(arguments)

                        • 相当于

                          • arguments.slice()
              • 固定套路:

                • 获得i位置开始的n个元素

                  • arr.slice(i,i+n)
        • 修改数组

          • 删除元素

            • arr.splice(starti,n)

              • 删除arr中starti位置开始的n个元素
            • 强调:

              • 直接修改原数组
              • 不用考虑含头不含尾
            • 简写:

              • 支持负数参数,表示倒数第n个

              • 省略第二个参数

                • 删除starti位置后所有元素
            • 其实有返回值

              • 返回被删除的元素组成的临时数组
              • var deletes=arr.splice(starti,n)
          • 插入元素

            • arr.splice(starti,0,值1,值2,...)

              • 在arr中starti位置插入新值,原starti位置的值及其之后的值被向后顺移
            • 强调:

              • 不支持打散数组类型参数

                • 如果插入子数组,会变成二维数组
          • 替换

            • arr.splice(starti,n,值1,值2...)

              • 先删除arr中starti位置的n个元素,再在starti位置插入新元素
            • 强调:

              • 删除的元素个数不一定和插入的元素个数一致
          • 固定套路

            • 广告轮播

              • 移除开头的n个元素拼到结尾

                • imgs=imgs.concat(imgs.splice(0,n))
              • 移除结尾的n个元素拼到开头

                • imgs=imgs.splice(-n).concat(imgs)
        • 翻转

          • arr.reverse()
        • 排序

          • 手写排序:

            • 冒泡,快速,插入

            • 冒泡

              • 依次比较相邻两数,如果前数>后数,就交换两数位置
              • for(var r=1;r<arr.length;r++){ for(var i=0;i<arr.length-r;i++){ if(arr[i]>arr[i+1]){ arr[i]=arr[i+1],arr[i+1]=arr[i]; } } }
          • arr.sort()

            • 将arr中每个元素转为字符串,再按字符串升序排列

            • 问题:

              • 只能按字符串升序排列
            • 解决:

              • 自定义比较器函数

                • 2步:

                    1. 定义比较器函数

                    • 专门比较两个值大小的函数

                    • 2个要求

                        1. 两个参数a,b
                        1. 返回值

                        • a>b,返回正数
                        • a<b,返回负数
                        • a==b,返回0
                    • 比如

                      • 最简单的数字升序比较器

                        • function compare(a,b){return a-b;}
                    1. 将比较器函数作为对象传入sort方法

                    • arr.sort(compare)

                    • 强调:

                      • 回调函数

                        • 将一个函数,作为参数传入另一个函数内,被另一个函数使用

                        • 传入回调函数时,不要加()

                          • 因为不是立刻调用!也不是只调用一次!而是交给别人去调用
                • 其实都会简写为:

                  • arr.sort(function(a,b){return a-b;})

                    • ES6

                      • arr.sort((a,b)=>a-b)
            • 降序排列

              • 颠倒比较器函数返回值的正负号

              • 比如:

                • 数字降序比较器

                  • function compare(a,b){return b-a;}
        • 栈和队列

          • 何时

            • 只要按照顺序使用数组中的元素时
          • js中没有专门的栈和队列的类型,都是用普通数组模拟的

          • 栈stack

            • 一端封闭只能从另一端进出的数组

            • FILO

            • 何时

              • 希望始终使用最新进入数组的元素时
            • 如何

              • 结尾出入栈

                  • arr.push(值)

                    • arr[arr.length]=值
                    • 从数组尾部添加新元素
                  • arr.push(值)的返回值,是新数组的长度

                  • var last=arr.pop()
                  • 返回值是尾部清除的元素组成的临时数组
              • 开头出入栈

                  • arr.unshift(值)

                    • arr.splice(0,0,值)
                  • 从数组头部添加新数组,返回值是新数组的长度

                  • var first=arr.shift()
                  • 从数组的头部删除第一个元素,返回值是删除元素组成的临时数组
              • 强调:

                • 开头入栈和结尾入栈的顺序是相反的
              • 心法:别在意那些形式,记住核心的东西,从数组尾部、头部添加或者删除元素去理解;只要是添加东西,返回值都是新数组的长度;反之,就是删除元素临时组成的数组

          • 队列queue

            • 只能从结尾进入,从开头出的数组

            • FIFO

            • 何时

              • 只要按照先来后到的顺序使用数组元素时
            • 如何

              • 结尾入
              • 开头出
      • 二维数组

        • 什么是

          • 数组中的元素内容,又是一个子数组
        • 何时

            1. 保存横行竖列的二维数据
            1. 对大的数组中的元素,再进行细分类
        • 如何

          • 创建

              1. 先创建空数组,再赋值

              • var arr=[];
              • arr[i]=[值1,值2,...]
              1. 在创建数组同时,初始化子数组

              • var arr=[ [值1,值2,...], [值1,值2,...], ]
          • 访问

            • arrr

              • 用法和普通数组元素的用法完全一样

              • 强调:

                • 任何情况下行下标r不能越界

                  • 报错!
          • 遍历:

            • for(var r=0;r<arr.length;r++){ for(var c=0;c<arr[r].length;c++){ arrr //当前元素 } }
  • 内置类型

    • ES标准中规定的,浏览器厂商已经实现的对象

    • 11个

      • String Number Boolean

        • 包装类型

          • 什么是

            • 专门封装原始类型的值,并提供操作原始类型值的API
          • 为什么

            • 原始类型的值本身不具有任何功能
          • 何时

            • 只要试图对原始类型的值调用函数时,引擎会自动创建对应类型的包装类型对象

              • 封装原始类型的值
              • 调用包装类型中的方法操作原始类型的值
          • 比如:

            • n.toFixed(2)

              • typeof n

                • number

                  • new Number(n).toFixed(2)
            • str.charCodeAt()

              • typeof str

                • string

                  • new String(str).charCodeAt();
      • Array Date Math RegExp

      • Error

      • Function Object

      • Global

        • 全局对象

          • 在浏览器中被window代替
  • String

    • 什么是

      • 多个字符组成的只读字符数组

      • vs 数组

        • 相同

          • 下标
          • .length
          • for遍历
          • slice()
        • 不同

          • 两者类型不同

            • API不通用
    • API

      • 强调

        • 所有String API都无权修改原字符串,只能返回新字符串
      • 大小写转换

        • 何时

          • 只要不区分大小写时,都要先转换为一致的大小写,再比较
        • str.toUpperCase()

        • str.toLowerCase()

      • 获取指定位置字符

        • var char=str.charAt(i)

          • str[i]
        • var unicode=str.charCodeAt(i)

          • 简写

            • 省略i, 默认为0
          • var char=String.fromCharCode(unicode)

      • 选取子字符串

        • var subStr=str.slice(starti,endi+1)

          • 简写:

            • 同数组的slice
        • str.substring(starti,endi+1)

          • 简写

          • 不支持负数参数

            • 变通:

              • str.length-n
        • str.substr(starti,n)

          • 不考虑含头不含尾
          • str.substring(starti,starti+n)
      • 查找关键词

          1. 查找一个固定的关键词出现的位置

          • var i=str.indexOf("关键词",fromi)

            • 在str中从fromi位置开始查找下一个关键词的位置

            • 返回值

              • 返回找到的关键词所在的位置
              • 如果找不到返回-1
            • 固定套路

              • 查找所有关键词出现的位置

                • var i=-1; while((i=str.indexOf("关键词",i+1))!=-1){ i //本次找到的关键词位置 }
            • 简写:

              • 省略fromi,默认为0
              • 两个参数都省略,表示完整复制一个数组
          • 优:

            • 可以指定开始位置,可以找所有
          • 缺:

            • 不支持正则,一次只能找一种关键词
          • 专门查找最后一个关键词的位置

            • var i=str.lastIndexOf("关键词")
          1. 判断是否包含符合规则的关键词

          • var i=str.search(/正则表达式/i)

            • 如果返回-1,说明不包含,如果返回不是-1,说明包含

            • 忽略大小写

              • /正则表达式/i
            • 永远只找第一个关键词的位置

            • 强调

              • 不支持g
          • 优:

            • 支持正则
          • 缺:

            • 不能设置开始查找位置,只能找第一个,不能找所有
            • 只能返回位置,不能返回关键词内容
          1. 获取所有关键词的内容

          • var kwards=str.match(/正则表达式/ig)

            • 强调:

              • 省略g,只找第1个
              • 加g,才找所有
          • 返回包含所有关键词的数组

            • 如果没找到,返回null

              • 如果一个函数可能返回null,都要先验证,再使用结果
          • 优:

            • 获得所有关键词的内容
          • 缺:

            • 无法返回每个关键词的位置
          1. 即获得每个关键词的内容,又获得每个关键词的位置

          • regexp.exec(str)
      • 替换

        • 简单替换:

          • 将所有关键词都替换为统一的新值

          • str=str.replace(/正则表达式/ig,“替换值”)

            • 如果替换一个固定的关键词,则第一个参数可以不用正则,只要写死关键词即可
          • 问题: 无法根据不同的关键词,选择不同的值替换

        • 高级替换:

          • 根据每个关键词的不同,动态返回不同的替换值
          • str=str.replace(/正则表达式/ig,function(kw,1,1,2,...){ //kw: 会自动获得本次找到的完整关键词 //$n: 会自动获得本次找到的关键词中第n个分组的子内容 return 根据不同kw,返回不同替换值 })
        • 衍生

          • 删除

            • 替换为空字符串
          • 格式化

            • 2步

                1. 用正则对原始字符串分组

                • var reg=/(\d{4})(\d{2})(\d{2})/
                • 正则中每个分组都会自动获得一个分组序号,从1开始
                1. 在replace的替换值中使用$n,重新拼接新的格式

                • birth.replace(reg,"11年2月$3日")
      • 切割

        • 简单切割

          • var subs=str.split("分隔符")
        • 复杂切割

          • var subs=str.split(/正则表达式/)
        • 返回值

          • 多段子字符串组成的数组

            • 切割后的结果中不包含分隔符
        • 固定套路

          • 将字符串打散为字符数组

            • var chars=str.split("")
  • 正则表达式

    • 什么是

      • 规定一个字符串中字符出现规律的规则
    • 何时

        1. 按规则模糊查找多种关键词时
        1. 用规则验证用户输入的格式时
      1. 关键词的原文就是最简单的正则表达式
      1. 字符集

      • 什么是

        • 规定一位字符备选字符列表的规则
      • 何时

        • 只要一位字符有多个备选字时
      • [备选字符列表]

      • 强调:

        • 一个字符集只能匹配一位字符
      • 简写

        • [0-9]

        • [a-z]

          • [A-Z]
        • [A-Za-z]

        • [A-Za-z0-9]

        • [\u4e00-\u9fa5]

      • 除了

        • [^47]
      1. 预定义字符集

      • \d

        • [0-9]
        • 0-9之间的数字
      • \w

        • [A-Za-z0-9_]
        • 大小写字母及数字
      • \s

        • 空字符

          • 空格,制表符...
      • .

        • 通配符
      1. 量词

      • 什么是

        • 规定一个字符集出现次数的规则
      • 何时

        • 只要规定一个字符集出现次数时
      • 如何

        • 字符集量词
      • 强调:

        • 默认仅修饰相邻的前一个字符集
      • 有明确数量边界

        • 字符集{n,m}

          • 至少n个,最多m个
        • 字符集{n,}

          • n个以上
        • 字符集{n}

          • 必须n个
      • 没有明确边界

        • 字符集?

          • {0,1}
          • 可有可无,最多一个
        • 字符集*

          • {0,}
          • 可有可无,多了不限
        • 字符集+

          • {1,}
          • 至少一个,多了不限
      1. 选择和分组

      • 分组

        • (多个规则)

        • 何时:

            1. 希望一个量词同时修饰多个字符集时

            • 身份证号

              • \d{15}(\d\d[0-9Xx])?
            1. 希望分段获取或处理字符串中部分子内容时

            • 格式化生日

              • (\d{4})(\d{2})(\d{2})
      • 选择

        • 规则1|规则2

          • | 优先级最低
        • 何时

          • 在两种规则间任选其一匹配

            • 微信

              • (微|w(ei)?)\s*(信|x(in)?)
      1. 指定匹配位置

      • ^ 字符串开头

        • 比如

          • 开头的空字符

            • ^\s+
            • 从开头匹配空字符
      • $ 字符串结尾

        • 比如

          • 结尾的空字符

            • \s+$
          • 开头或结尾的空字符

            • ^\s+|\s+$
      • \b 单词边界

        • ^ $ 空字符 标点
      1. 密码强度:

      • 6~8位字母,数字的组合,至少包含一个大写字母和一位数字
      • ^(?![a-z0-9]+)(?![AZaz]+)(?![A-Za-z]+)[A-Za-z0-9]{6,8}$
  • RegExp

    • 什么是

      • 封装一条正则表达式,并提供用正则表达式执行验证和查找的API
    • 何时

        1. 使用正则表达式验证字符串格式
        1. 即查找关键词内容,又查找关键词位置
    • 创建

        1. 直接量

        • var reg=/正则表达式/ig

        • 何时

          • 如果正则表达式是固定不变的
        • 字符冲突

          • / -> /
        1. 用new

        • var reg=new RegExp("正则表达式","ig");

        • 何时

          • 如果正则表达式需要动态生成
        • 字符冲突:

          • ->\ " '

            • new RegExp("\d{6}")
    • API

      • 查找关键词

        • var arr=reg.exec(str)

          • 即查找内容又查找位置

            • 在str中查找下一个满足reg要求的关键词
          • 返回值:

            • arr: [0: "完整关键词", 1: 1,2:1, 2: 2,..., index: 本次找到关键词的位置]

            • reg.lastIndex

              • 下次开始位置
            • 如果没找到,返回null

              • 都要先判断不是null,再使用
          • exec做三件事

              1. 将本次找到的关键词,放入数组第0个元素, 将每个分组的子内容放入后续元素
              1. 修改数组的index属性,记录本次找到关键词的位置
              1. 修改reg.lastIndex属性=index+关键词的长度
          • 固定套路:

            • 找全部

              • var arr=null; while((arr=reg.exec(str))!=null){ arr[0] //完整关键词 arr[n] //第n个分组的子内容 arr.index //本次找到关键词的位置 reg.lastIndex //下次开始查找的位置 }

              • 简写

                • 如果只获得某个分组的子内容
                • while(reg.exec(str)!=null){ RegExp.$n //第n个分组的子内容 }
      • 验证

        • var bool=reg.test(str)

          • 验证str是否符合reg的规则要求
        • 问题:

          • test默认,只要部分匹配就返回true
        • 解决:

          • 只要验证,正则都要前加^,后加$

            • 表示从头到尾完全匹配
  • Math

    • 不能new

      • 所有API都用Math.直接调用
    • API

        1. 取整

        • 上取整

          • Math.ceil(num)
        • 下取整

          • Math.floor(num)

            • 只能对纯数字内容下取整

              • 如果传入的不是数字,就自动调用Number(x)隐式转换为数字
          • vs parseInt(str)

            • 先去掉字符串后非数字字符,再省略小数部分

            • 如果传入的不是字符串,就自动调用String(x),先转为字符串

            • 问题:

              • 多数情况下,只去单位,还要保留小数
            • 解决:

              • 如果只是去单位,首选parseFloat
        • 四舍五入取整

          • Math.round(num)

            • 返回值是number

              • 可直接做算术计算
            • 只能取整

              • 不能指定小数位数
          • vs num.toFixed(d)

            • 返回值string

              • 必须先转换,再计算
            • 可以按任意小数位数四舍五入

          • 自定义round

            • function round(num,d){ num*=Math.pow(10,d); num=Math.round(num); return num/Math.pow(10,d); }
        1. 乘方和开平方

        • 乘方

          • Math.pow(底数,幂)
        • 开平方

          • Math.sqrt(num)

            • for(var i=2;i<=Math.sqrt(num);i++){ if(num%i==0){不是质数,return} } 是质数
        1. 最大值和最小值

        • Math.max/min(值1,值2,...)

        • 问题:

          • 不支持查找一个数组中的最大值/最小值
        • 解决:

          • Math.max/min.apply(null,arr)
        1. 随机数

        • 默认

          • 0<=Math.random()<1
        • 在任意min~max之间生成随机整数

          • parseInt(Math.random()*(max-min+1)+min)
        • 在0~max之间生成随机整数

          • parseInt(Math.random()*(max+1))
  • Date

    • 什么是

      • 封装一个时间,并提供操作时间的API
    • 何时

      • 只要在程序中存储日期和时间,都要用Date对象
    • 创建

      • 4种

          1. 自动获得客户端当前系统时间

          • var now=new Date()
          1. 创建日期对象保存自定义时间

          • var date=new Date("yyyy/MM/dd hh:mm:ss")
          • var date=new Date(yyyy,MM-1,dd,hh,mm,ss)
          1. 复制一个日期对象

          • 为什么

            • 日期的计算都是直接修改原日期对象,计算后,原日期无法保留
          • 何时

            • 只要需要同时保存开始和结束时间时,都要先将开始时间复制一个副本,再用副本计算截止时间
          • var date2=new Date(date1)

          1. 用毫秒数

          • var date=new Date(ms)

          • 何时:

            • 将获得ms数转化为当地的时间时
    • 本质

      • 日期对象中存储的是1970年1月1日0点至今的毫秒数

      • 为什么

        • 毫秒数不受时区影响

          • 同一个毫秒数,在不同时区可用new Date()轻松转化为当地时间显示
      • var ms=date.getTime();

    • API

      • 单位

        • FullYear, Month, Date, Day

          • 没有s结尾
        • Hours, Minutes, Seconds Milliseconds

          • 有s结尾
      • 每个单位都有一对儿getXXX/setXXX方法

        • date.getXXX()负责获取指定单位的数值

        • date.setXXX(num)负责设置指定单位的数值

            • 自动调整时间进制
        • 特例:

          • Day没有setXXX()方法
      • 取值范围:

        • 只有月中的日Date,从1开始到31结束

        • 其余都从0~进制-1结束

        • 只有月份Month需要修正

          • 0~11

            • 计算机中的月份比现实中的月份至小1
        • 其余都不需要修正

    • 计算

      • 两日期对象可相减

        • 得到ms差

          • 可计算倒计时
      • 对任意单位做加减

        • date.setXXX(date.getXXX()+n)

        • 强调:

            1. 直接修改原日期对象
            1. setXXX() 可自动调整时间进制
    • 格式化

      • toString()

        • 当地标准时间的完整格式
      • toLocaleString()

        • 当地时间的简化版格式
      • toLocaleDateString()

        • 当地时间格式

          • 仅保留日期部分

            • 有兼容性问题
      • toLocaleTimeString()

        • 当地时间格式

          • 仅保留时间部分
      • toGMTString()

        • 国际标准时间(0时区)
  • Error

    • 什么是错误

      • 代表程序执行过程中导致程序无法正常执行的原因
    • 错误处理

      • 什么是

        • 即使程序发生错误,也保证不异常退出的机制
      • 为什么

        • 任何程序只要发生错误,就会立刻中断退出
      • 何时

        • 只要希望程序即使出错,也不会中断退出
      • 如何

        • try{ 可能出错的代码 }catch(err){ 只有出错才执行的错误处理代码 比如: 错误提示,记录日志,保存进度/数据 }finally{ 无论是否出错都必须执行的代码 释放资源 }

        • Error错误对象

          • 什么是

            • 在发生错误时自动创建的封装错误信息的对象
          • name

            • 错误类型

              • 6种

                • SyntaxError, ReferenceError, TypeError, RangeError, EvalError, URIError
          • message

            • 错误提示信息
          • String(err)

            • 错误类型:错误提示信息
    • 优化

      • 如果可以提前预知错误的原因

        • 建议用if...else...代替try...catch
    • 抛出自定义错误

      • 在协作开发中,程序的作者用于提醒调用者错误的使用了你的程序
      • throw new Error("自定义错误信息")
  • Function

    • 什么是

      • 函数其实是一个封装一段代码段的对象
      • 函数名其实仅是引用函数对象的一个普通变量
    • 为什么

      • 代码重用
    • 何时

      • 只要一项任务,可能被反复使用,都要定义为函数,反复使用函数
    • 创建

      • 3种

          1. 声明

          • function 函数名(参数列表){函数体; return 返回值}

          • 会被声明提前

            • hoist

              • 什么是

                • 在开始执行程序前
                • 引擎会首先查找var声明的变量和function声明的函数
                • 将其提前到当前作用域的顶部集中创建
                • 赋值留在原地
              • 鄙视时

                • 凡是看到先使用,后声明,都是在声明提前
                • 先改为声明提前之后的程序,再判断输出
              • 解决问题

                  1. 所有变量和函数的声明都放在当前作用域的顶部
                  1. ES6

                  • 可用let代替var

                    • 要求

                      • 当前作用域中let a之前不允许出现未声明的a
                  1. 函数
          1. 直接量

          • var 函数名=function(参数列表){函数体; return 返回值}
          • 不会被声明提前
          1. 用new

          • var 函数名=new Function("参数名1","参数名2",...,"函数体; return 返回值")
      • 参数:

        • 接收函数执行时必须的数据的变量

        • 为什么

          • 可让函数更灵活
        • 何时

          • 只要函数必须某些数据才能正常执行
      • 返回值:

        • 函数的执行结果

        • 何时

          • 如果调用者需要获得函数的返回值
    • 调用

      • 让引擎按照函数的步骤清单,执行任务

      • 强调:

        • 函数不调用不执行
        • 只有调用才执行
    • 重载(overload)

      • 什么是

        • 相同函数名,不同参数列表的多个函数

        • 在调用时

          • 根据传入参数的不同,自动选择匹配的函数执行
      • 为什么

        • 减少API的数量,减轻调用者的负担
      • 何时

        • 只要一项任务,需要根据不同的参数,执行不同的操作时
      • 如何

        • 问题

          • js语法不支持重载

            • js不允许多个同名函数同时存在
        • 解决

          • 每一个函数内,都有一个arguments对象接住所有传入函数的参数值
          • 根据arguments的元素内容或元素个数,判断执行不同的操作
        • arguments

          • 函数调用时,自动创建的

          • 自动接收所有传入函数的参数值的

          • 类数组对象

            • 长的像数组的对象

            • vs 数组

              • 相同

                  1. 下标
                  1. length
                  1. for/for of遍历
              • 不同

                • 类型不同

                  • 数组是Array类型

                  • 类数组对象是Object类型

                    • 类数组对象无法使用数组的API
    • 匿名函数

      • 什么是

        • 函数创建时,没有指定函数名

          • 使用后自动释放!
      • 为什么

        • 节约内存
        • 划分临时作用域,避免全局变量
      • 何时

        • 只要一个函数只用一次

            1. 回调callback

            • 将一个函数作为参数传入另一个函数内,被其他函数调用

            • 比如:

              • arr.sort(function(a,b){return a-b;})

                • ES6

                  • arr.sort((a,b)=>a-b)
              • str.replace(/reg/g, function(kw,1,1,2,...){return 替换值})

                • ES6

                  • str.replace(/reg/g, (kw,1,1,2,...)=>{return 替换值})
            1. 自调

            • 定义函数后自己调用自己,调用后,立刻释放

            • 何时

              • 定义一个临时作用域,减少使用全局变量, 避免全局污染
            • 如何

              • (function(参数列表){函数体; return 返回值})(参数值列表)
              • !function(参数列表){函数体; return 返回值}(参数值列表)
              • +function(参数列表){函数体; return 返回值}(参数值列表)
    • 作用域和作用域链

      • 作用域(scope)

        • 变量的可用范围

          • 其实是一个对象
        • 包括

          • 全局作用域对象

            • window

              • 保存全局变量

                  • 可反复使用
                  • 随处可用

                    • 全局污染

                      • 建议:

                        • 尽量少用全局变量
          • 函数作用域对象

            • AO

              • 保存局部变量

                • 包括

                  • 2种

                    • 参数变量
                    • 函数内var出的变量
                  • 仅函数内可用

                    • 不会被污染
                  • 不可重用
      • 函数的生命周期

        • 开始执行程序前

          • 创建ECS

            • 专门保存正在调用的函数的执行环境的 数组
          • 首先在ECS中添加浏览器主程序的执行环境main

          • 创建全局作用域对象window

            • 所有全局变量都是保存在window对象中
          • main执行环境引用window

        • 定义函数时

          • 用函数名声明全局变量
          • 创建函数对象,封装函数定义
          • 函数对象的scope属性,指回函数创建时的作用域
          • 函数名变量引用函数对象
        • 调用函数时

          • 向ECS中压入本次函数调用的执行环境元素

          • 创建本次函数调用时使用的函数作用域对象(AO)

          • 在AO中创建所有局部变量

            • 包括

              • 形参变量
              • 函数内用var声明的变量
          • 设置AO的parent属性引用函数的scope属性指向的父级作用域对象

          • 函数的执行环境引用AO

          • 变量的使用顺序

            • 先在AO中找局部变量
            • AO中没有才去父级作用域中找
        • 调用后

          • 函数的执行环境出栈

            • 导致AO释放

              • 导致AO中的局部变量一同被释放
      • 作用域链(scope chain)

        • 由各级作用域逐级引用,形成的链式结构

        • 保存着所有的变量

        • 控制着

          • 变量的使用顺序
      • 函数中,没有用任何对象/this就直接访问的变量,在作用域链中找

    • 闭包(closure)

      • 什么是

        • 即重用变量,又保护变量不被污染的机制
      • 为什么

        • 全局变量

          • 优:

            • 可重用
          • 缺:

            • 易被污染
        • 局部变量

          • 缺:

            • 不可重用
          • 优:

            • 不会被污染
      • 何时:

        • 即重用变量,又保护变量不被污染
      • 如何

        • 三特点(3步)

            1. 外层函数

            • 包裹受保护的变量和操作变量的内层函数
            1. 外层函数要返回内层函数的对象

            • 3种

                1. return function(){...}
                1. 直接给全局变量赋值一个内部function
                1. 将内部函数保存在一个对象的属性或数组元素中

                • return [function,function,function]
                • return { fun:function(){...} }
            1. 调用外层函数,用外部变量接住返回的内层函数对象

            • 形成闭包
      • 闭包如何形成

        • 外层函数调用后,外层函数的作用域对象(AO),无法释放

          • 被内层函数对象的scope引用着
      • 缺:

        • 比普通函数占用更多内存

          • 多的是外层函数的作用域对象(AO)始终存在
        • 容易造成内存泄漏

        • 解决:

          • 如何释放闭包

            • 将引用内层函数对象的外部变量置为null

              • 导致内层函数对象被释放

                • 导致外层函数的AO被释放
      • 鄙视

        • 画简图

          • 2步

              1. 找受保护的的变量,确定外层函数调用后,受保护变量的最终值
              1. 找操作受保护的变量的内层函数对象
            • 结论:

              • 同一次外层函数调用,返回的内层函数对象,共用同一个受保护的变量
  • OOP

    • 什么是面向对象

      • 程序中都是用对象结构来描述现实中一个具体事物
    • 什么是对象

      • 程序中描述现实中一个事物的属性和功能的程序结构
    • 为什么

      • 现实中,任何数据,必须属于一个具体事物才有意义!
    • 面向对象三大特点:

      • 封装

        • 创建一个对象,集中存储一个事物的属性和功能
      • 继承

        • 父对象中的成员,子对象无需重复创建,就可直接使用
      • 多态

        • 同一事物,在不同情况下,表现出不同的状态
    • 封装——创建对象

      • 也称为封装

        • 将一个事物的属性和功能集中定义在一个对象中

        • 事物的属性会成为对象的属性

          • 其实就是保存在对象中的普通变量
        • 事物的功能会成为对象的方法

          • 其实就是保存在对象中的普通函数
        • 对象的成员

          • 属性和方法统称为成员
      • 为什么

        • 便于维护
      • 何时

        • 只要使用面向对象,都要先创建对象,再按需调用对象的方法执行操作
      • 如何

        • 3种

          • 创建一个单独的对象

              1. 用对象直接量

              • var obj={ 属性名:属性值, ... : ... , 方法名: function(){ ... this.属性名... } }

                • ES6

                  • var obj={ 属性名:属性值, ... : ... , 方法名(){ ... this.属性名... } }
              • 何时

                • 在创建对象时,已知对象的成员
              • 问题:

                • 对象自己的方法,访问自己的属性,如果不加this,仅会在作用域链中找,不会在对象中找
              • 解决:

                • this.属性名

                  • 今后,只要对象自己的方法要访问自己的属性,必须用this.属性名
                • 强调:

                  • 不带this.的变量,在作用域链中查找
                  • this.属性在当前对象和当前对象的原型链中找
              1. 用new

              • 先创建一个空对象

                • var obj=new Object();

                  • {}
                • 简写:

                  • new可省略,()可省略,但不能同时省略

                    • 可推广到其它内置对象
              • 为新对象添加新属性

                • obj.属性名=属性值
                • obj.方法名=function(){ ... this.属性名 ... }
              • 何时:

                • 在创建对象时,暂时不知道对象的成员
              • 揭示:

                • js中一切对象底层都是关联数组

                  • 相同

                    • obj.属性名

                      • 等效于

                        • obj["属性名"]

                          • 只要访问对象的属性时,属性名是变化的变量,就只能用[变量]
                    • for in 遍历每个成员

                      • for(var key in obj){ key //当前属性名 obj[key] //当前属性值 }
                    • 访问不存在的属性,不报错,返回undefined

                    • 强行给不存在的属性赋值

                      • 不报错

                        • 自动添加该属性
                  • 不同

                    • 类型不同

                      • API不通用
          • 问题:

            • 反复创建多个相同结构的对象时,会造成大量重复的代码
          • 解决: 用构造函数反复创建多个相同结构的对象

            1. 用构造函数

            • 什么是

              • 规定一类对象统一结构的函数
            • 何时

              • 反复创建多个相同结构的对象
            • 作用:

                1. 描述统一的结构
                1. 将空对象构建成要求的结构
            • 如何

              • 2步

                  1. 定义构造函数

                  • function 类型名(属性参数){ this.属性名=属性参数; /this.方法名=function(){ ... this.属性名 ... } ///js中强烈不推荐将方法定义在构造函数中 }
                  1. 用new调用构造函数

                  • 实例化一个对象

                  • var obj=new 类型名(属性值,...,...)

                  • new

                      1. 创建一个新的空对象
                      1. 设置新的子对象的proto继承构造函数的prototype对象
                      1. 调用构造函数,将构造函数中的this自动替换为当前新对象

                      • 构造函数将规定的属性添加到新对象中,并将传入的参数值保存在新对象的新属性中
                      1. 返回新对象的地址保存到变量中
            • 优:

              • 代码重用
            • 问题:

              • 无法节约内存

                • 放在构造函数中的方法定义,每new一次,都会创建函数对象副本
              • 解决:

                • 继承
    • this

      • 什么是

        • 自动引用正在调用当前方法的.前的对象
      • 为什么

        • 不用this的普通变量,只能在作用域链中查找,无法进入对象中
        • 一般对象的变量名可能变化,所以不能写死在对象的方法内部
      • 何时

        • 只要希望去当前对象中找属性值时,就必须用this.属性名
      • obj.fun()

        • fun中的this->obj
      • new Fun()

        • Fun中的this->正在创建的新对象
      • fun()和匿名函数自调

        • this默认->window
      • 类型.prototype.fun

        • fun中的this->将来调用fun的.前的子对象

          • 子对象一定是当前类型
      • 如果this不是想要的

        • fun.call(替换this的对象)

          • 相当于 对象.fun()
    • 访问对象成员

      • 访问属性

        • 对象.属性名

          • 用法和普通变量完全一样
        • 特殊:

          • 如果访问对象的属性时,属性名需要动态拼接

            • 只能用obj[属性名]
      • 调用方法

        • 对象.方法名()

          • 用法和普通函数完全一样
    • 继承

      • 什么是

        • 父对象中的成员,子对象无需重复创建即可直接使用
      • 何时:

        • 只要多个子对象拥有相同的属性值或方法时,仅需要集中定义在父对象中一份,所有子对象共用即可
      • 为什么

        • 代码重用,节约内存
      • 如何

        • js中的继承都是继承原型对象

          • 原型继承
      • 原型对象

        • 什么是

          • 集中保存同一类型的所有子对象共有成员的父对象
        • 何时

          • 只要多个子对象,拥有相同的成员时,都要将相同的成员集中保存在原型对象中一份即可
        • 如何

          • 不用创建

            • 买一赠一

              • 在定义构造函数同时,已经自动创建了该类型的原型对象
            • 构造函数.prototype指向原型对象

            • 原型对象.constructor指回构造函数

          • new的第二步

            • 每创建一个新子对象,都会自动设置子对象的proto继承构造函数的原型对象
          • 向原型对象中添加共有成员:

            • 构造函数.prototype.成员=值
        • 自有属性和共有属性

          • 自有属性:

            • 直接保存在对象本地的属性
          • 共有属性:

            • 保存在原型对象中,所有子对象共有的属性
          • 操作

            • 读取

              • 子对象.属性
            • 修改

              • 自有属性

                • 必须用 子对象.属性名=值
              • 共有属性

                • 必须用 构造函数.prototype.属性名=值
          • 判断

            • 自有

              • var bool=obj.hasOwnProperty("属性名")
            • 共有

              • 不是自有

                • &&

                  • obj.属性名!==undefined

                  • 属性名 in obj

                    • 判断属性名是否在obj的原型链中
            • 否则,就是没有

        • 内置对象的原型对象

          • 其实内置对象类型Array,Date...都是构造函数

          • 每种类型都有自己的原型对象

            • Array.prototype, Date.prototype, ..
          • 内置对象的原型对象中保存了所有该类型的子对象共用的API

          • 固定套路:

            • 解决旧浏览器无法使用新API的问题

              • 2步:

                • 判断

                  • if(typeof 内置类型.prototype.API !=="function")
                  • if(!("API" in 内置类型.prototype))
                • 添加

                  • 内置类型.prototype.方法=function(参数){ ... this //获得将来调用方法的.前的对象 }
      • 原型链(prototype chain)

        • 由多级父对象逐级继承形成的链式结构

          • 保存了

            • 所有对象的属性
          • 控制着

            • 对象成员的使用顺序

              • 先用自有属性
              • 自己没有,才延原型链向父级找
              • 原型链上没有,返回undefined
          • vs 作用域链

            • 保存了

              • 局部和全局变量
            • 控制着变量的使用顺序

              • 先用局部变量
              • 局部没有,才去作用域链上找
              • 找不到,报错
          • 总结

            • 所有不用.的变量都在作用域链上找

              • 作用域链中的变量不用.,可直接访问
            • 所有对象.访问的属性都保存在原型链上

              • 原型链上的属性必须用“对象.”才能访问
      • 判断对象的类型

          1. typeof

          • 只能区分基础类型和function
          • 不能进一步区分对象的类型
          1. var bool=类型.prototype.isPrototypeOf(child)

          • 不仅检查直接父对象,而且检查整个原型链
          1. var bool= child instanceof 构造函数

          • 不仅检查直接父对象,而且检查整个原型链
        • 问题:

          • 检查整个原型链
        • 解决: 3. 检查内置class属性

          • Object.prototype.toString.call(obj)==“[object 类型名]”

            • obj.toString()
          • 更严格

            • class属性直接保存在对象本地
            • 只在创建对象时确定类型
            • 对象创建后,不随继承关系的改变而改变
        • 如果检查数组类型: 4. var bool=Array.isArray(obj)

          • ES5

            • IE9+
          • 原理和Object.prototype.toString.call一样

          • 鄙视:

            • 方法定义在原型对象中,还是定义在构造函数对象上

              • 如果方法仅限当前类型的子对象可用,其他类型的对象不可用,就定义在原型对象中

                • 必须当前类型的子对象才能调用
              • 如果方法不确定将来调用它的对象类型,就定义在构造函数对象上

                • 不需要任何对象实例,即可用构造函数名直接调用
    • 多态

      • 同一个方法,在不同情况下表现出不同的状态

      • 重写(override)

        • 如果子对象觉得父对象的成员不好用,可在本地定义同名自有成员,来覆盖父对象中的成员
    • 自定义继承

        1. 仅修改两个对象间的继承关系:

        • 获得子对象的父对象

          • var father=Object.getPrototypeOf(child)
        • 设置子对象继承指定父对象

          • Object.setPrototypeOf(child,father)
        1. 修改构造函数原型对象,来修改所有子对象的父对象

        • 构造函数.prototype=father

        • 时机:

          • 必须紧跟在构造函数定义之后
          • 开始创建子对象之前
        1. 仅基于现有父对象,创建子对象,并扩展自有属性: Object.create()

        • 创建新子对象,继承父对象,扩展子对象自有属性

        • var child=Object.create(father,{ 属性名:{四大特性}, ... : ... })

        • 鄙视: 模拟实现Object.create()

          • Object.create=function(father,props){ //var child=new Object(); //Object.setPrototypeOf(child,father); var Fun=function(){}; Fun.prototype=father; var child=new Fun();

      //Object.defineProperties(child,props); if(props!==undefined){ for(var key in props){ child[key]=props[key].value; } } return child; }

        1. 两种类型间的继承

        • 何时:

          • 如果发现多个类型拥有部分相同的属性结构和方法定义

            • 都要抽象父类型
        • 如何

          • 2步:

              1. 定义抽象父类型

              • 相同的属性结构定义在父类型的构造函数中
              • 相同的方法定义在父类型的原型对象中
              1. 让子类型继承父类型

                1. 在子类型构造函数中借用父类型构造

                • extends

                  • 让父类型构造函数帮助添加相同部分的属性定义
                  • 子类型构造函数仅负责添加独有的属性定义即可
                • 错误:

                  • 直接调用父类型构造函数

                    • this->window

                      • 父类型中的属性都泄露到全局
                • 正确

                  • 父类型构造.call(this, 参数1,参数2,...)

                  • 简写:

                    • 父类型构造.apply(this, arguments)
                • 鄙视:

                  • call vs apply

                    • 相同:

                      • 都是强行借用一个本来无法调用的函数,并临时替换函数中this为指定对象
                    • 不同:

                      • call

                        • 传入借用函数的参数,必须单独传入,逗号分隔
                      • apply

                        • 传入借用函数的参数,放在一个数组中整体传入
                        • 可自动打散数组类型参数
                1. 让子类型原型对象继承父类型原型对象

                • inherits
                • Object.setPrototypeOf( 子类型构造.prototype, 父类型构造.prototype )
  • ES5

    • 对对象的保护

      • 对象的属性

        • 命名属性

          • 可直接用.访问到的属性

          • 分为

            • 数据属性

              • 实际存储属性值的属性

              • 四大特性

                • { value: 属性值, writable: true/false, //控制是否可修改 enumerable: true/false, //控制是否可被for in遍历 configurable: true/false, //1. 控制是否可删除 //2. 控制是否可修改前两个特性 }

                • 强调

                  • configurable一旦改为false,不可逆
            • 访问器属性

              • 不实际存储数据,专门提供对其它数据/变量的保护

              • 何时

                  1. 用自定义规则保护属性时

                  • 比如

                    • age属性必须介于18~65之间
                  1. 为对象添加虚拟属性

                  • fullName

                    • 获取时

                      • firstName+lastName
                    • 赋值时

                      • 将fullName按空格分隔,一半给firstName,另一半给lastName
              • 2步:

                  1. 定义一个隐藏的数据属性实际存储属性值
                  1. 定义访问器属性专门读写隐藏的数据属性
              • 四大特性

                • { get:function(){return this.隐藏属性;}, set:function(val){ //如果val符合条件 this.隐藏属性=val //否则 报错 }, enumerable, configurable }

                  • 试图用访问器属性读取受保护的值时,自动调用get方法
                  • 试图用访问器属性修改受保护的值时,自动调用set方法,参数val可自动获得要赋的新值
                • 如果省略set方法,则该访问器属性相当于只读

          • 获取一个属性的四大特性

            • var attrs=Object.getOwnPropertyDescriptor(obj,"属性名")
            • attrs: {四大特性}
          • 修改四大特性

            • 只改一个属性的四大特性

              • Object.defineProperty(obj,"属性名",{ 特性名:值, ... : ... })
            • 同时修改多个属性的四大特性

              • Object.defineProperties(obj,{ 属性名1: { 特性名:值, ... : ... }, 属性名2: {四大特性} })
            • 强调:

              • 双保险

                • 修改writable或enumerable时,最好同时定义configurable为false,禁止反向修改
              • 要修改的属性不存在,会自动添加该属性

          • 特性的默认值

            • 用.添加的新属性

              • 特性的默认值为true
            • defineProperty/defineProperties添加的新属性

              • 特性的默认值为false
        • 内部属性

          • 不能用.访问到的属性

          • proto

            • Object.getPrototypeOf(obj)
            • Object.setPrototypeOf(child,father)
          • class

            • Object.prototype.toString.call(obj)
          • extensible:true

            • var bool=Object.isExtensible(obj)
            • Object.preventExtensions(obj)
      • 防篡改

          1. 防扩展

          • 禁止添加新属性

            • 相当于

              • 将对象的extensible:false
          • 判断是否已禁止扩展

            • Object.isExtensible(obj)
          • 设置防扩展

            • Object.preventExtensions(obj)
          1. 密封

          • 在防扩展同时,禁止删除现有属性

            • 相当于

              • 将每个属性的configurable:false

                • 其他属性在修改特性时,不必反复修改configurable:false
          • 判断是否已密封

            • Object.isSealed(obj)
          • 密封对象

            • Object.seal(obj)
          • 最像Java的构造函数

            • function Emp(id,ename,salary,age){ this.id=id; this.ename=ename; this.salary=salary;

    Object.defineProperties(this,{ id:{writable:false}, salary:{enumerable:false}, age:{ writable:true, enumerable:false }, age:{ get:function(){return this. age;}, set:function(val){ if(val<18||val>65) throw new Error(...) this._age=val; }, enumerable:true } }); this.age=age;

    //防篡改: Object.seal(this);//密封 }

                    - 鄙视: 
    ​
                        - 实现一个js类型,包含public属性和private属性
    ​
        - 3. 冻结
    ​
            - 在密封同时,禁止修改所有属性的值
    ​
                - 相当于
    ​
                    - 将每个属性的writable:false
    ​
            - 判断是否被冻结
    ​
                - Object.isFrozen(obj);
    ​
            - 冻结对象
    ​
                - Object.freeze(obj)
    ​
            - 何时:
    ​
                - 如果一个对象中所有属性值都不允许擅自修改
    
    • call/apply/bind()

      • 何时:

        • 只要函数中的this不是想要的,就可用call/apply/bind替换
      • call和apply

        • 立刻调用函数执行

        • 同时临时替换函数中的this

        • 何时

          • 如果立刻执行,且临时替换this
        • 如何

          • fun.call(obj, 参数值列表)

            • 调用fun
            • 替换fun中的this为obj
            • 将参数值列表传递给fun
          • fun.apply(obj, 参数值数组)

            • vs call

              • apply要求传入fun的参数必须放在数组中整体传入
              • apply可自动将数组打散为单个参数值分别传入
      • bind

        • 基于一个现有函数,创建一个新函数,并永久绑定this和部分参数

        • 何时:

          • 只要替换回调函数中的this时
        • 如何:

          • var newFun=fun.bind(obj, 参数值列表 )

            • 创建一个和fun功能完全一样的新函数
            • 永久绑定新函数中的this为obj
            • 永久绑定新函数中的部分参数为参数值列表中的值
          • 强调:

            • 被bind创建的函数中的this和绑定的变量,任何情况下不能再被call替换
    • 数组API

      • 判断

        • 判断arr中每个元素是否都符合条件

          • arr.every(function(val,i,arr){ //val: 当前元素值 //i : 当前位置 //arr : 当前数组 return 判断条件; })
        • 只判断arr中是否包含符合条件的元素

          • arr.some(function(val,i,arr){ return 判断条件; })
      • 遍历

        • 对数组中每个元素执行相同的操作

          • 直接修改原数组

            • arr.forEach(function(val,i,arr){ arr[i]=新值; })

              • IE8

                • Array.prototype.forEach=function(callback){ for(var i=0;i<this.length;i++){ if(this[i]!==undefined)//防稀疏数组 callback(this[i],i,this); } }
          • 不修改原数组,返回新数组

            • var newArr=arr.map(function(val,i,arr){ return 修改后的新值 })

              • IE8

                • Array.prototype.map=function(callback){ for(var i=0,result=[];i<this.length;i++){ if(this[i]!==undefined)//防稀疏数组 result[i]=callback(this[i],i,this); } return result; }
            • map() 方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。

              • function map(arr, mapCallback) { // 首先,检查传递的参数是否正确。 if (!Array.isArray(arr) || !arr.length || typeof mapCallback !== 'function') { return []; } else { let result = []; // 每次调用此函数时,我们都会创建一个 result 数组 // 因为我们不想改变原始数组。 for (let i = 0, len = arr.length; i < len; i++) { result.push(mapCallback(arr[i], i, arr)); // 将 mapCallback 返回的结果 push 到 result 数组中 } return result; } }
            • 其实map也可像forEach一样使用

          • 回调函数中的this,指window

      • 过滤和汇总

        • 过滤

          • 选取arr中符合条件的元素组成新的子数组
          • var subArr=arr.filter(function(val,i,arr){ return 判断条件 })
        • 汇总

          • 将数组中每个元素值汇总出一个结果
          • var r=arr.reduce(function(prev,val,i,arr){ return prev+val; }, 初始值)
    • 严格模式

      • 何时:

          1. 新项目,都要启用严格模式
          1. 旧项目,逐个模块向严格模式迁移
      • 如何

        • 2个范围

          • 整个代码块或js文件启用严格模式

            • 在代码块或js文件的开头插入: "use strict";
          • 仅在一个函数内启用严格模式

            • 在function内,函数体顶部插入: "use strict";
      • 要求:

          1. 禁止给未声明的变量赋值
          1. 静默失败升级为错误
          1. arguments, arguments.callee不推荐使用
          1. 匿名函数自调中的this不再自动指window,而是undefined
  • ES6

    • let

      • 声明一个仅在当前块中有效的变量

      • 解决:

          1. 为js添加了块级作用域

          • 避免了块内的变量污染外部

          • ES5

            • 匿名函数自调,来划分临时作用域
          1. 避免了声明提前

          • 在同一作用域内,let前不允许提前使用未声明的同名变量
      • 何时

        • 今后都用let代替var
    • 参数增强

      • 默认值

        • function fun(参数1,参数2,参数3=默认值)

        • ES5

          • 参数3=参数3||默认值
      • 剩余参数

        • 代替arguments

          • arguments问题

              1. 不是数组类型,用不了数组的API
              1. 默认只能获得所有参数值,不能选
          • ES5

            • var args=Array.prototype.slice.call(arguments) //arguments.slice()
        • function fun(参数1,参数2,...数组名){ //数组: 除参数1,参数2以外的剩余所有参数 }

      • 散播

        • 代替apply

          • apply问题

              1. 要求必须所有参数必须放在一个数组中
              1. 第一个参数,必须传入一个对象,用于替换this
        • 定义时

          • 每个参数单独定义

            • function fun(参数1,参数2,参数3,...)
        • 调用时

          • 部分参数值,可放在一个数组中集中传入

            • fun(参数1, ...数组)

              • 将数组打散为单个元素,分配给参数1后的每个参数
    • 解构

      • 简化批量赋值

        • 数组解构

          • 用下标匹配值和变量

            • var [a,b,c]=[1,2,3]
            • [a,b]=[b,a]
        • 对象解构

          • 用属性名匹配值和变量

            • var {x:a,y:b,z:c}={x:1,y:2,z:3}
        • 参数解构

          • 同数组解构和对象解构

            • function fun([a,b,c]){}

              • fun([1,2,3])
            • function fun({x:a,y:b,z:c})

              • fun({x:1,y:2,z:3})
    • 模板字符串

      • 简化

        • 字符串拼接
      • 反引号包裹的字符串xxx

      • 支持换行

      • 支持动态生成内容

        • 在``中使用${js表达式}
        • 模板字符串会自动执行js表达式的结果,并拼接到最终生成的普通字符串中
    • 箭头函数

      • 简化:

        • 回调函数
      • 如何

        • 去function,在()和{}之间加=>

        • 更简化:

            1. 如果只有一个参数,可省略()

            • 但是,如果没有参数,必须保留空()
            1. 如果函数体只有一句话,可省略{}

            • 更简化

              • 且一句话还是return

                • 则可省略return和{}
      • 何时

        • 几乎所有回调函数都可用箭头函数简化

        • 反例

          • 如果回调函数中的this和外部的this不相同时,不能简化

          • //this->window elem.addEventListener("click",function(){ //this->当前elem })

            • 如果用箭头函数简化,结果内外this都是window

            • 其实:

              • //this->window elem.addEventListener("click",e=>{ //this->window //e.target->当前elem })
    • for of

      • 简化:

        • 遍历索引数组和类数组对象
      • 问题:

          1. 不支持关联数组和对象
          1. 只能读取元素值,不能修改元素值

          • 按值传递
          1. 只能连续遍历所有
      • 如何

        • for(var val of arr){ val//当前元素值 }
    • class

      • 简化:

        • 面向对象

          • 封装,继承,多态
      • 如何

        • 封装

            1. 用“class 类型名{}”包裹原来的构造函数和原型对象方法
            1. 修改构造函数的“function 函数名”为“constructor”
            1. 原型对象方法:

            • 可省略开头的“类型.prototype”
            • 可省略方法名后的"=function"
            1. 添加访问器属性:

            • 在构造函数的平级
            • get 访问器属性名(){ return this.受保护的其他属性 }
            • set 访问器属性名(val){ if(条件) this.受保护的属性=val else 报错 }
        • 继承

            1. 不用再设置Object.setPrototypeOf
            1. 在"class 类型名"后添加" extends 父类型"
            1. 在构造函数中借用父类型构造函数

            • 不用call/apply,不用加this
            • super(属性值列表)
    • 集合

      • 为什么

        • 为了将查找功能从普通对象身上分离出来
      • 何时

        • 只要希望通过hash方式快速查找时
      • Set

        • 元素不能重复的集合

        • 何时

          • 如果希望通过元素值查找元素时
          • 去掉重复元素
        • 如何

          • 创建

            • var set=new Set(凡是可遍历的对象)
          • API

            • set.size

            • 判断是否包含

              • var bool=set.has(值)
            • 将set转为数组

              • var arr=[...set]
            • 添加/删除元素

              • set.add/delete(value)
            • 清空

              • set.clear();
      • Map

        • 用法和普通关联数组完全一样

        • 何时

          • 如果希望通过key查找元素时
          • 希望保存一组简直对儿的集合
        • 如何

          • 创建

            • var map=new Map();
            • map.set(key,value)
          • API

            • map.size
            • map.has(key)
            • map.get(key)
            • map.delete(key)
      • 遍历

        • for of

          • 为了简化普通for循环

          • 缺:

              1. 无法获得位置
              1. 只能从头到尾,逐个遍历,不能跳跃或选取
              1. 只能顺序遍历,无法反向遍历
    • Promises

      • 为什么

        • 避免callback hell
      • 何时

        • 只要要求,下一个函数必须在前一个函数执行后,才能开始执行时
      • 如何

        • 2步

          • 前一个函数结尾

            • 返回Promises对象

              • return new Promise((resolve, reject)=>{ 如果正确,就继续调用resolve() 否则,就调用reject("错误提示") })
          • 连续调用两个函数时

            • 前一个函数().then(下一个函数) .catch((err)=>{错误处理函数})

              • 不要加()
      • 多个函数需要连续调用,每个函数结尾,都要返回Promise对象,才能连续用then调用

一. JavaScript运行环境

1. 浏览器工作原理

  • 浏览器内核

  • 渲染引擎工作原理

  • WebKit内核

    • WebCore
    • JavaScriptCore

2. v8引擎工作原理

  • JavaScript引擎

    • SpiderMonkey
    • Chakra
    • JavaScriptCore
    • V8引擎
  • V8引擎工作原理

    • Parse:转换器
    • Ignition:解释器
    • TurboFan:编译器
    • Orinoco:垃圾回收

3. JavaScript内存管理

  • 理解内存管理

    • 什么是内存管理
    • 手动管理内存
  • 常见的GC内存算法

    • JavaScript的垃圾回收(Garbage Collection)
    • 引用计数算法
    • 标记清除算法
    • 标记整理算法
    • 分代回收算法
  • V8引擎内存管理

    • V8的分代算法
    • V8内存的分配
    • 新生代对象回收
    • 旧生代对象回收
  • Performance调试

  • JavaScript内存泄露

4. JavaScript事件循环

  • 浏览器的进程模式

    • 线程
    • 进程
    • JavaScript线程
  • 浏览器的事件循环

    • 宏任务macrotask
    • 微任务microtask
    • 常见的面试题
  • Node的事件循环

    • libuv
    • 阻塞IO
    • 非阻塞IO
    • 宏任务macrotask
    • 微任务microtask
    • 常见的面试题

二. JavaScript作用域和函数

1. 认识作用域

  • 认识作用域

    • JavaScript编译和执行
    • 深入理解作用域
    • 作用域的嵌套
  • 词法作用域

    • 认识词法分析
    • eval函数
    • with关键字
  • 作用域提升

    • JavaScript疑惑面试题分析
    • 编译器中的变量声明和提升
    • 函数和变量的提升
  • 块级作用域

    • with作用域
    • try...catch...作用域
    • let变量声明
    • const变量声明
  • 作用域相关的面试题

2. 执行上下文

  • 执行上下文

    • Global EC
    • Function EC
    • Eval EC
  • 变量对象VO

    • VO(G)全局变量对象
    • AO(Active Object)私有变量对象
    • GO(Global Object)全局对象

3. 深入函数执行

  • call/apply执行函数

  • 立即执行函数

  • Scopechain

    • 函数的生命周期

      • 定义
      • 调用时
      • 调用后
    • 函数执行过程的作用域链

    • 作用域链面试题

  • 深入闭包

    • 为什么需要闭包
    • 闭包的访问规则
    • 闭包的应用场景
    • 闭包的注意事项
    • 闭包相关的面试题

4. 函数的this绑定

  • this指向什么

    • 为什么使用this
    • this指向什么
  • this绑定规则

    • 默认绑定

    • 隐式绑定

    • 显示绑定

      • call/apply/bind
      • 内置函数
    • new绑定

  • 绑定的优先级

    • 默认绑定的优先级最低
    • 显示绑定优先级高于隐式绑定
    • new绑定优先级高于隐式绑定
    • new绑定优先级高于bind
  • 绑定之外this

    • 忽略显示绑定
    • 间接函数引用
    • ES6箭头函数
  • this相关的面试题

5. 函数的柯里化

  • 认识柯里化

    • 什么是柯里化(Currying)
    • 柯里化有什么作用?
  • 实现柯里化

    • 案例代码改造
    • 柯里化的实现
  • 柯里化应用

    • 柯里化应用场景

三. JavaScript面向对象

1. 深入理解对象

  • 对象的语法

    • 对象字面量
    • 对象的类型
    • 函数对象
  • 对象的内容

    • 属性和方法定义

    • 对象属性描述符

      • [[Configurable]]
      • [[Enumerable]]
      • [[Writable]]
      • [[Value]]
      • [[Get]]
      • [[Set]]
    • 访问器属性使用

      • Object.defineProperty
      • Object.defineProperties
      • Object.getOwnPropertyDescriptor
    • 对象的属性判断

      • hasOwnProperty
      • in关键字
      • for..in..
      • for..of..
  • 对象的拷贝

    • 对象的引用赋值
    • 对象的浅拷贝
    • 对象的深拷贝
  • ES6对象增强

    • Object.is()

    • 简写属性名

    • 可计算属性名称

    • 简写方法名

    • 对象的解构

      • 嵌套解构
      • 部分解构
      • 参数上下文匹配

2. 面向对象编程

  • 理解面向对象

    • 什么是面向对象编程?

    • 面向对象编程的特性

      • 封装
      • 继承
      • 多态
    • 类和对象的关系

  • 创建对象方式

    • 工厂模式创建

    • 构造函数模式

    • 原型创建模式

      • 认识原型
      • prototype 属性
      • constructor 的属性
      • __proto__属性
  • 面向对象继承

    • 认识原型链

      • 深入原型对象
      • 简洁的原型语法
      • 修改原型的属性
      • 深入理解原型链
      • 原型和实例的关系
      • 原型链存在的问题
    • 继承的实现

      • 经典继承

        • 实现方式
        • 存在弊端
      • 组合继承

        • 实现方式
        • 存在弊端
      • 原型式继承

        • 实现方式
        • 存在弊端
      • 寄生式继承

        • 实现方式
        • 存在弊端
      • 集成式组合继承

        • 释放方式
  • ES6类的使用

    • class类的定义

      • 构造方法
      • 属性定义
      • 方法定义
    • 类的实例化过程

      • 类的构建过程解析

      • 类的类型

        • function类型
    • 属性分类解析

      • 实例属性和方法
      • 原型属性和访问器
      • static类方法和属性
    • class类的继承

      • extends关键字

      • super函数的使用

        • 构造函数
        • 普通函数
    • Babel的处理

      • Babel工具对class的处理

        • 阅读Babel转换后的代码
      • Babel对继承的转换处理

        • Babel继承的源码阅读
        • _inherits
        • _possibleConstructorReturn
        • _classCallCheck
  • 面向对象面试题

四. ES6~12新特性讲解

1.新特性查询

2. ES6常见新特性

  • 新类型

    • Symbol
    • Promise
    • Reflect
    • Proxy
    • WeakSet
    • WeakMap
    • Set
    • Map
  • 新特性

    • generators
    • class
    • arrow functions
    • template string literals
    • for...of
    • enhanced object literals
    • spread syntax
    • rest parameter
    • default function parameters

3. ES7常见新特性

  • 新特性

    • Array.prototype.includes()

4. ES8常见新特性

  • 新特性

    • rest/spread properties
    • await
    • async

5. ES9常见新特性

  • 新特性

    • Promise.prototype.finally()
    • rest/spread properties
    • asynchronous generators and iteration

6. ES10常见新特性

  • 新特性

    • Array.sort()
    • catch without argument

7. ES11常见新特性

  • 新特性

    • import.meta
    • module namespace exports
    • globalThis
    • optional chaining syntax
    • nullish coalescing operator ??
    • dynamic import

8. ES12常见新特性

  • 新特性

    • Promise.any
    • logical assignment operators: &&=, ||=, ??=

五. JavaScript异步处理

1. 迭代器和生成器

  • 深入迭代器

    • 认识迭代器

      • 什么是迭代器
      • 什么是迭代器模式
    • 可迭代类型

      • 字符串String
      • 数组Array
      • 映射Map
      • 集合Set
      • arguments对象
      • 等等...
    • 迭代类型操作

      • for-of 循环
      • 数组解构
      • 扩展操作符
      • yield*操作符,在生成器中使用
      • 等等...
    • 迭代器协议

      • Symbol.iterator
      • iter.next
    • 自定义迭代器

      • 实现Symbol.iterator
      • 实现next
    • 终止迭代器

      • break、continue、return、throw
      • 结构操作值消耗
  • 深入生成器

    • 认识生成器

      • 什么是生成器
      • 生成器的作用
    • 生成器函数

      • 创建生成器函数
      • 生成器函数的调用
    • yield关键字

      • yield中断执行
      • yield输入和输出
      • 生成可迭代对象
    • 生成器和迭代器

    • 终止生成器

      • return()
      • throw()

2. Promise的使用

  • 异步代码处理

    • 代码执行的顺序
    • 异步代码执行
    • 事件循环
    • 并行和并发执行
    • 函数的回调
  • Promise的API

    • 认识Promise

      • Promises/A+规范

      • Promise的状态

        • 待定(pending)
        • 完成(fulfilled)
        • 拒绝(rejected)
    • 创建Promise

      • Promise构造器

        • resolve
        • reject
      • Promise.resolve()

      • Promise.reject()

      • Promise.all([ .. ])

      • Promise.race([ .. ])

    • Promise的处理

      • then
      • catch
      • finally
    • Promise链式

3. async和await

  • async、await基础

    • 和Promise的关系
    • await
    • async
  • 和generator的关系

  • async、await优缺点

六. 模块化和包管理工具

1. JS模块化开发

  • 认识模块化开发

    • JavaScript设计缺陷
    • 没有模块化带来的问题
  • CommonJS规范

    • CommonJS和Node的区别和关系
    • exports导出
    • module.exports导出
    • require导入和细节
    • 模块的加载顺序
  • AMD和CMD规范

    • CommonJS规范缺点

    • AMD规范

      • require.js
      • require.config
      • require
      • define
    • CMD规范

      • SeaJS
      • define
      • require
      • exports
      • module
  • ESModule模块化

    • export关键字
    • import关键字
    • export和import结合
    • default用法
    • import()异步导入
  • ES Module和CommonJS

    • ES Module和CommonJS的区别
    • ES Module和CommonJS的交互

2. npm包管理工具

  • 认识npm工具

  • package.json

    • 必须填写的属性:name、version
    • private属性
    • main属性
    • scripts属性
    • dependencies属性
    • devDependencies属性
    • engines属性
    • browserslist属性
  • 版本管理的问题

    • semver版本规范

      • X主版本号(major)
      • Y次版本号(minor
      • Z修订号(patch
    • ^x.y.z

    • ~x.y.z

  • npm install解析

    • npm install命令

      • 全局和局部安装
    • npm install原理

      • package-lock.json
      • 查找规则
      • 查找缓存
      • 等等...
    • npm其他常见命令

  • 其他工具对比

    • yarn
    • cnpm
    • npx

七. JavaScript特性补充

1. Proxy和Reflect

  • Proxy

    • 认识proxy

      • Proxy的作用
      • Proxy应用场景
    • 处理方法

      • get
      • set
      • has
      • deleteProperty
      • apply
      • construct
      • getPrototypeOf
      • setPrototypeOf
      • ownKeys
      • 等等...
    • 处理参数

      • target
      • property
      • value
      • receiver
  • Reflect

    • 认识Reflect

      • Reflect的作用
      • Reflect应用场景
    • 处理方法

      • get
      • set
      • has
      • deleteProperty
      • apply
      • construct
      • getPrototypeOf
      • setPrototypeOf
      • ownKeys
      • 等等...
    • 处理参数

      • target
      • property
      • value
      • receiver

2. JSON的序列化

  • JSON表示形式

    • 简单值
    • 对象
    • 数组
  • JSON的序列化

    • JSON.stringify

      • 使用

      • 参数

        • 过滤器
        • 字符串缩进
      • toJSON方法

    • JSON.parse

      • 使用

3. 浏览器API操作

  • 浏览器BOM操作

    • window对象
    • location对象
    • history对象
    1. 浏览器DOM操作

    • Node节点类型
    • DOM的元素操作
    • DOM的样式操作
    1. 浏览器事件处理

    • 事件冒泡
    • 事件捕获
    • DOM事件流
    • 事件对象
    • 常见事件类型
    1. 浏览器存储方案

    • cookie
    • localStorage
    • sessionStorage

八. 手写工具案例练习

1. 节流函数的实现

  • 认识防抖函数

  • 防抖的三方库

    • lodash
    • underscore
  • 手写防抖函数

    • 防抖基本功能
    • 优化的参数
    • 绑定this
    • 优化取消功能
    • 优化立即执行
    • 优化返回值

2. 防抖函数的实现

  • 认识节流函数

  • 节流的三方库

    • lodash
    • underscore
  • 手写节流函数

    • 节流基本功能
    • 优化参数和this
    • 优化最后执行功能
    • 优化取消功能
    • 优化返回值

3. 实现深拷贝案例

  • 认识深拷贝

  • 拷贝的三方库

    • lodash
    • underscore
  • 手写深拷贝函数

    • 深拷贝基本功能
    • 递归拷贝功能
    • 不同类型的处理
    • 函数的拷贝功能