JavaScript 学习笔记

基础

  • JavaScript 组成:
    • ECMAScript:JavaScript 语言基础
    • Web APIs
      • DOM:页面文档对象模型
      • BOM:浏览器对象模型

具体

书写位置

  • 书写位置:<script> 放在 <body> 尾部

    • 行内:

      <body>
          <button onclick=""></button>
      </body>
      
    • 内部:

      <body>
          <script>
          	<!-- code -->
          </script>
      </body>
      
    • 外部:只执行 src 导入的 js 文件内容,忽略 <></> 包裹的内容

      <body>
          <script src=""></script>
      </body>
      

注释

  • 单行注释://
  • 块注释:/**/

结束符

  • 可写可不写:;

输入与输出

  • 输出:

    // 文字
    document.write('content')
    // 元素
    document.write('<h1>content</h1>')
    // 警告对话框
    alert('content')
    // 控制台调试
    console.log('content')
    
  • 输入:返回用户输入,返回值为字符串类型

    prompt('content')
    

alert()prompt() 会跳过页面渲染先被执行

变量

  • 声明:let 关键字,不允许两个变量名重复

    let 变量名
    
  • 赋值:= 赋值

    let a
    a = 0
    

    声明同时赋值:

    let a = 0
    

    更新:

    let a = 0
    a = 1
    

    声明多个:

    let a = 0, b = 'hello'
    

    输出:

    let a = 0, b = 1
    console.log(a, b)
    
  • 变量名规则:

    • 不能与关键字重复
    • 只能用下划线、字母、数字、$ 组成,且不能以数字开头
    • 大小写敏感

数组

  • 声明:

    let 数组名 = [数据1, 数据2, ..., 数据n]
    let 数组名 = new Array(数据1, 数据2, ..., 数据n)
    

    索引:

    数组名[索引]
    

    索引超出数组长度时取出的值为 undefined

    长度:

    .length
    

    添加到末尾:返回数组新长度

    .push(data1, ..., dataN)
    

    添加到开头:返回数组新长度

    .unshift(data1, ..., dataN)
    

    删除末尾:返回该元素的值

    .pop()
    

    删除开头:返回该元素的值

    .shift()
    

    删除指定起点、指定数量的元素:deleteCount 不写时删除到末尾

    .splice(start, deleteCount)
    

    排序:

    // 默认升序
    .sort()
    // 升序
    .sort(function(a, b) {
        return a - b
    })
    // 降序
    .sort(function(a, b) {
        return b - a
    })
    

    map:遍历数组处理数据,返回新数组

    const newArr = arr.map(function (ele, index) {
        ...
        return ...
    })
    

    join:将数组中所有元素转换成一个字符串。参数为分隔符,不写为,

    arr.join('')
    

常量

  • 声明:必须赋值,后续不允许修改

    const 常量名 = 值
    

数据类型

  • 数据类型:
    • 基本数据类型:
      • number 数字
      • string 字符串
      • boolean 布尔
      • undefined 未定义
      • null 空
    • 引用数据类型:
      • object 对象

数字类型

弱数字类型

字符串类型

  • 声明:

    'string'
    "string"
    `string`
    

    拼接:+

    'a' + 'b'
    'age: ' + 18
    

    模板字符串:使用反引号 `` 包裹,拼接字符串与变量,使用 ${}

    `leftString${variant}rightString`
    

    去除两边空格:

    .trim()
    

布尔类型

  • true | false
    

未定义类型

  • 变量只声明不赋值时的值

    undefined
    

空类型

  • null

undefined 代表不确定,null 代表没有

检测数据类型

  • typeof x
    typeof(x)
    

类型转换

  • 隐式转换

    // + 双目运算符: 有字符串转换为字符串
    1 + '1' // 11
    // - 单目运算符: 转换为数字型
    +'1' // 1
    // - * / : 转换为数字型
    2 - '1' // 1
    2 * '1' // 2
    2 / '1' // 2
    
  • 显式转换

    Number()
    Boolean() // '' 0 undefined null false NaN 都为 false
    parseInt() // 整数,可以去除非数字后缀
    parseFloat() // 小数,可以去除非数字后缀
    

运算符

赋值运算符

  • =
    +=
    -=
    *=
    /=
    %=
    

一元运算符

  • // 分为前置与后置。区别与C语言一致
    ++
    --
    

比较运算符

  • >
    <
    >=
    <=
    == // 值是否相等
    != // 是否不等
    === // 类型与值都相等
    !== // 是否不全等
    

逻辑运算符

  • &&
    ||
    !
    
  • 逻辑中断:为未定义值赋值

    x = x || 0
    

语句

分支语句

  • if 语句

    if () {
        
    } else if {
        
    } else if {
    	...
    } else {
        
    }
    
  • 三元运算符

    cond ? stmt1 : stmt2
    
  • switch 语句:与每一个 value 作全等比较

    switch (key) {
        case value1:
            stmt1
            break
        ...
        case valueN:
            stmtN
            break
        default:
            stmt
            break
    }
    

循环结构

  • while 循环

    while (cond) {
        stmt1
    }
    
  • for 循环

    for (stmt1; cond; stmt2) {
        stmt3
    }
    
  • 循环退出

    continue
    break
    

函数

  • 声明:

    function 函数名(参数列表) {
        函数体
    }
    
  • 命名建议:

    动词 含义
    can 是否可执行
    has 是否含有
    is 是否是
    get 获取
    set 设置
    load 加载
  • 返回值:

    return 返回值
    
  • 后声明的函数会覆盖前面的同名函数:

    function fn() {
        console.log('1')
    }
    
    function fn() {
        console.log('2')
    }
    
    fn() // 输出:2
    
  • 匿名函数:

    • 函数表达式:将匿名函数赋值给变量,通过变量名调用

      let fn = function() {}
      fn()
      

      类似于具名函数,但是具名函数的调用可以在声明之前,函数表达式由于 let 关键字限制调用必须在变量赋值之后

    • 立即执行函数:避免全局变量之间的污染

      // 写法 1
      (function() {
          函数体
      })();
      // 写法 2
      (function() {
          函数体
      }());
      

      注意:立即执行函数必须写结束符 ;

对象

  • 声明:

    // 字面量
    let 对象名 = {
        属性名: 属性值,
        ...
        方法名: 函数,
        ...
    }
    // 默认构造函数
    let 对象名 = new Object({
        属性名: 属性值,
        ...
        方法名: 函数,
        ...
    })
    // 自定义构造函数
    function 构造函数名(参数1, 参数2, ...) {
        this.参数1 = 参数1
        this.参数2 = 参数2
        ...
    }
    const obj = new 构造函数名(实参1, 实参2, ...)
    
  • 组成:

    • 属性
    • 方法
  • 访问对象内部成员:

    // 方法 1
    对象名.成员名
    // 方法 2
    对象名['属性名']
    
  • 修改或添加新的对象内部成员值:

    对象名.成员名 = value
    
  • 删除:

    delete 对象名.成员名
    
  • 遍历:for in

    for (let k in obj) {
        console.log(obj[k])
    }
    
  • 内置对象:

    • Math

      random
      ceil
      floor
      max
      min
      pow
      abs
      

      随机数函数:Math.random(),返回随机小数

      // 0-10 整数
      Math.floor(Math.random() * (10 + 1))
      // 5-10 整数
      Math.floor(Math.random() * (5 + 1)) + 5
      // N-M 整数
      Math.floor(Math.random() * (M - N + 1))) + N
      
    • 静态成员与静态方法:

      构造函数名.属性名 = 属性值
      构造函数名.函数名 = 函数体
      

Web APIs

DOM (Document Object Model) 文档对象模型

  • 获取 DOM 对象:

    • 借助 CSS 选择器

      • 选择匹配的第一个元素

        document.querySelector('CSS 选择器')
        
      • 选择匹配的所有元素

        document.querySelectorAll('CSS 选择器')
        
    • 其他方法:现在并不常用

      document.getElementById()
      document.getElementsByTagName()
      document.getElementsByClassName()
      
  • 操作元素内容:

    • .innerText:显示纯文本,不解析标签
    • .innerHTML:解析标签
  • 操作元素属性:

    • 操作元素常用属性:

      .src
      .title
      ...
      
    • 操作元素样式属性:

      • 通过 style 属性操作:

        .style.width
        .style.marginTop
        ...
        
      • 通过 className 操作:执行 className

        .className = 'className'
        
      • 通过 classList 操作类

        // 追加
        .classList.add('类名')
        // 删除
        .classList.remove('类名')
        // 切换:有就删掉,没有就加上
        .classList.toggle('类名')
        // 查询:是否包含
        .classList.contains('类名')
        
    • 操作表单元素属性:

      .type
      .value
      .checked = true | false
      .disabled = true | false
      ...
      
    • 自定义属性:

      • 标签上以 data- 开头:

        <div data-id="10"></div>
        
      • 获取:

        .dataset.id
        
  • 定时器-间歇函数:

    • 开启定时器:间隔时间单位:ms。返回值代表当前定时器 id

      setInterval(函数,间隔时间)
      
    • 关闭定时器:

      clearInterval(id)
      
  • 事件监听:

    .addEventListener('事件类型', 要执行的函数)
    

    事件类型:

    • 鼠标事件:

      click
      mouseenter // 不冒泡
      mouseleave // 不冒泡
      mouseover // 冒泡
      mouseout // 冒泡
      
    • 焦点事件:

      focus
      blur
      
    • 键盘事件:

      keydown
      keyup
      
    • 文本事件:

      input
      

    事件对象:

    • 获取事件对象:回调函数的第一个参数 e

      .addEventListener('', function(e) {})
      
    • 事件对象常用属性:

      type 事件类型
      clientX/clientY 光标相对于浏览器可见窗口左上角的位置
      offsetX/offsetY 光标相对于当前 DOM 元素左上角的位置
      key 键盘值
      pageX/pageY 鼠标在页面中的坐标
      

    环境对象:函数被调用者

    回调函数:函数作为函数实参

  • 事件流:事件完整执行过程中的流动路径

    • 捕获:从父到子

      .addEventListener('事件类型', 要执行的函数, true)
      
    • 冒泡:从子到父

    • 阻止传播(捕获与冒泡):

      事件对象.stopPropagation()
      
    • 解绑事件:

      removeEventListener('事件类型', 要执行的函数)
      

      匿名函数无法解绑

  • 事件委托:利用事件冒泡,将事件注册到父元素身上

    事件对象.target // 获取触发的元素对象
    事件对象.tagName // 获取触发的元素对象标签名,注意大写
    
  • 阻止默认行为:链接的跳转,表单域跳转等

    事件对象.preventDefault()
    
  • 其他事件:

    • 页面加载事件:写在 <body> 前需要等待页面加载完毕

      // 页面全部加载完
      window.addEventListener('load', function() {})
      // 无需等待样式表、图像完全加载
      document.addEventListener('DOMContentLoaded', function() {})
      
    • 页面滚动事件:滚动条滚动时触发

      window.addEventListener('scroll', function() {})
      

      滚动属性:

      .scrollLeft
      .scrollTop
      

      HTML 元素:

      document.documentElement
      

      滚动到指定的坐标:

      window.scrollTo(x, y)
      
    • 页面尺寸事件:

      window.addEventListener('resize', function() {})
      

      获取宽高:不包含边框,margin,滚动条等

      .clientWidth
      .clientHeight
      
    • 页面平滑滑动:

      html {
          scroll-behavior: smooth;
      }
      
  • 元素尺寸与位置:要求可视,

    // 包含 padding, border 等,位置相对于定位的父级元素
    .offsetWidth
    .offsetHeight
    .offsetLeft
    .offsetTop
    // 相对于视口来说
    element.getBoundingClientRect()
    
  • 日期对象:

    • 实例化:

      // 当前时间
      const date = new Date()
      // 指定时间
      const date = new Date('xxxx-xx-xx')
      const date = new Date('xxxx-xx-xx xx:xx:xx')
      
    • 日期对象方法:

      getFullYear()
      getMonth()
      getDate()
      getDay()
      getHours()
      getMinutes()
      getSeconds()
      toLocaleString()
      toLocaleDateString()
      toLocaleTimeString()
      
    • 时间戳:

      // 三种获取方法
      getTime()
      +new Date()
      Date.now()
      
  • 节点操作:

    • 节点类型:

      • 元素节点:标签
      • 属性节点:属性
      • 文本节点:文本
      • 其他
    • 查找节点:

      • 父节点:找不到返回 null

        .parentNode
        
      • 子节点:

        .childNodes // 获得所有子节点,包括文本节点、注释节点等
        .children // 仅获得所有元素节点,返回伪数组
        
      • 兄弟节点:

        nextElementSibling
        previousElementSibling
        
    • 增加节点:

      • 创建节点:

        document.createElement('标签名')
        
      • 追加节点:

        // 插入到父元素最后一个子元素
        .appendChild(元素)
        // 插入到父元素某个子元素的前面
        .insertBefore(要插入的元素, 指定位置的元素)
        
      • 克隆节点:默认 false

        // true: 同时克隆后代节点 | false: 不包含后代节点
        元素.cloneNode(布尔值)
        
    • 删除节点:

      .removeChild(元素)
      
  • M 端事件:

    • touch 事件:

      touchstart // 触摸到
      touchmove // 滑动
      touchend // 移开时
      
  • JS 插件:swiper 插件

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

  • 定时器-延时函数:延时执行一次就结束

    setTimeout(回调函数, 等待的毫秒数)
    clearTimeout(id)
    
    
  • JS 执行机制:单线程。先执行同步任务再执行异步任务

  • location 对象:

    // 赋值以跳转
    location.href = 'url'
    // 地址 ? 后的参数
    location.search
    // 刷新当前页面, true 强制刷新
    location.reload(true)
    
  • navigator 对象:浏览器自身相关信息

    // 浏览器版本及平台
    navigator.userAgent
    

    例:检测平台

    (function() {
        const userAgent = navigator.userAgent
        const android = userAgent.match(/(Android);?[\s\/]+([\d.]+)?/)
        const iphone = userAgent.match(/(iPhone\sOS)\s([\d_]+)/)
        if (android || iphone) {
            location.href = 'm.url'
        }
    })();
    
  • history 对象:

    back() // 后退
    forward() // 前进
    go(参数) // -n | n 代表后退 | 前进 n 个页面
    
  • 本地存储:以字符串形式存储

    • localStorage

      • 存储数据:键值均为字符串类型

        localStorage.setItem(key, value)
        
      • 获取数据:

        localStorage.getItem(key)
        
      • 删除数据:

        localStorage.removeItem(key)
        
    • sessionStorage:生命周期是浏览器窗口

    • 存储复杂数据类型:转换成 JSON 字符串

      localStorage.setItem(key, JSON.stringify(value))
      

      JSON 字符串转对象:

      JSON.parse(localStorage.getItem(key))
      
  • 正则表达式:

    • 语法:

      // 定义正则表达式
      const 规则名 = /表达式/
      // 判断是否符合规则,匹配为 true,不匹配为 false
      规则名.test(被检测字符串)
      // 判断是否符合规则,匹配成功返回数组,不匹配为 null
      规则名.exec(被检测字符串)
      
    • 元字符:

      • 边界符:

        // 开始
        ^
        // 结束
        $
        
      • 量词:

        // >= 0
        *
        // >= 1
        +
        // 0 | 1
        ?
        // n
        {n}
        // >= n
        {n,}
        // >= n && <= m
        {n,m}
        
      • 字符类:

        // 包含任意一个字符
        []
        [a-z] // 连字符表示范围
        [A-Z]
        [0-9]
        [a-zA-Z0-9]
        [^a-z] // [] 里的 ^ 表示取反
        // 匹配除换行符外任意单个字符
        .
        // 或
        |
        // 预定义
        \d // [0-9]
        \D // [^0-9]
        \w // [A-Za-z0-9_]
        \W // [^A-Za-z0-9_]
        \s // [ \t\r\n\v\f]
        \S // [^ \t\r\n\v\f]
        
      • 修饰符:

        /表达式/修饰符
        i // 不区分大小写
        g // 匹配所有满足的结果
        
    • 替换:替换全部需要加修饰符 g

      str = str.replace(/表达式/, '替换文本')
      

进阶

  • 作用域:

    • 局部作用域:
      • 函数作用域
      • 块作用域
    • 全局作用域:
      • <script> 标签内
      • .js 文件中
    • 作用域链:变量查找机制
  • 垃圾回收机制:

    • 引用计数法
    • 标记清除法
  • 闭包:内存函数 + 外层函数的变量

    function f() {
        const a = 1
        function add() {
            a++
        }
        return add
    }
    const add = f()
    add() // a = 2
    add() // a = 3
    
  • 变量提升:使用 var 时会出现

    console.log(a) // undefined
    var a = 1
    

    var 型变量未声明时会自动在当前作用域最前面声明,但不赋值,相当于:

    var a
    console.log(a)
    a = 1
    
  • 函数进阶:

    • 函数提升:类似于变量提升自动在作用域最前面声明

      函数表达式不能提升

    • 函数参数:

      • 动态参数 arguments:函数内置的伪数组,包含所有实参

      • 剩余参数:形参 ... 后的自定义真数组,接收剩余参数

        function(int a, int b, ...arr) {}
        

        展开运算符:展开数组

        const arr = [1, 2, 3]
        console.log(...arr) // 1 2 3
        

        使用场景:参数传递,数组合并等

        Math.max(...arr)
        
        [...arr1, ...arr2]
        
    • 箭头函数:替代某些匿名函数

      (形参) => {}
      

      特殊的:形参只有一个参数时可以简化为

      单个形参 => {}
      

      特殊的:只有一行代码时可以省略大括号,并且默认值为该行代码运行结果

      (形参) => 单行代码
      

      如果返回对象时,避免大括号歧义:使用小括号包裹对象

      (a) => ({a: a})
      

      箭头函数没有动态参数 arguments,但是有剩余参数

      箭头函数中的 this 指向的是上一层普通函数的 this 或全局对象,而不一定是调用者

  • 解构赋值:

    • 数组解构:

      const [var1, var2, ...] = arr
      

      例:变量值交换

      ;[a, b] = [b, a]
      

      注意前面要加 ;,防止歧义

      可以使用剩余数组存储多余值:

      const [a, b, ...arr] = [1, 2, 3, 4]
      

      此时 arr 是真数组

      支持默认参数:

      const [a = 1, b = 0] = []
      

      选择性赋值:

      const [a, b, , d] = [1, 2, 3, 4] // d = 4
      
    • 对象解构:

      const {a, b} = {a: a, b: b}
      

      注意左侧变量名与右侧属性名必须相同

      重命名:旧: 新

      const {a: na, b: nb} = {a: a, b: b}
      

      多级解构:

      const {a, b: {c, d}} = {a: a, b: {c: c, d: d}}
      
  • forEach 遍历数组:

    arr.forEach(function(item, index) {})
    

    只遍历,不返回数组(与 map 区分)

  • filter:筛选数组元素,true 为接受,类似于 map 返回处理后的新数组

    arr.filter(function(item, index) {
        return true
    })
    
  • 内置构造函数:

    • Object

      Object.keys(obj)
      Object.values(obj)
      Object.assign(target, source) // 追加或修改属性
      
    • Array

      const arr = new Array(1, 2, 3, ...)
      // 实例方法:
      forEach
      filter
      map
      reduce                
      

      reduce:累计处理,例如求和函数

      arr.reduce(function(previous, current) {
          return previous + current
      }, 起始值)
      

      数组中每一项不是数字时必须给起始值,可以为 0

      其他方法:

      join
      find
      every
      some
      concat
      sort
      splice
      reverse
      findIndex
      

      伪数组转换为真数组:

      Array.from(arr)
      
    • String

      length
      split('分隔符')
      substring(首字符索引[, 尾字符索引]) // 不包含尾字符
      startsWidth(字符串[, 检测位置索引])
      includes(字符串[, 检测位置索引])
      toUpperCase
      toLowerCase
      indexof
      endsWidth
      replace
      match
      
    • Number

      toFixed(小数位位数) // 四舍五入
      num.toString() | String(num) // 转换为字符串
      
  • 对象:

    • 原型:

      构造函数.prototype
      
    • 添加公共函数:

      // 公共的方法应写在原型身上,追加
      构造函数.prototype.函数名 = 函数体
      // 一次赋值多个函数,需要带上构造函数
      构造函数.prototype = {
          constructor: 构造函数,
          函数名1: 函数体1,
          函数名2: 函数体2,
          ...
      }
      
    • 原型的构造函数:指向 构造函数

      构造函数.prototype.constructor
      
    • 对象原型:

       实例对象.__proto__ // 只读
      
    • 原型继承:

      function Father() {}
      function Son() {}
      Son.prototype = new Father()
      
    • 原型链:继承的实现方法,通过原型链查找属性方法

      // 非 Object 对象的prototype.__proto__ === Object.prototype
      // Object.prototype.__proto__ === null
      

      instanceof 实例是否属于原型链上的某类型

  • 深浅拷贝:

    • 浅拷贝:拷贝的地址

      Object.assign()
      {...obj}
      Array.prototype.concat()
      [...arr]
      
    • 深拷贝:

      • 递归:注意 Array 分支在 Object 分支之前

        function deepCopy(newObj, oldObj) {
            for (let k in oldObj) {
                if (oldObj[k] instanceof Array) {
                    newObj[k] = []
                    deepCopy(newObj[k], oldObj[k])
                } if (oldObj[k] instanceof Object) {
                    newObj[k] = {}
                    deepCopy(newObj[k], oldObj[k])
                } else {
                    newObj[k] = oldObj[k]
                }
            }
        }
        
      • JS 库 loadash

        const newObj = _.cloneDeep(oldObj)
        
      • 字符串化:

        const newObj = JSON.parse(JSON.stringify(oldObj))
        
  • 异常处理:

    • throw:直接中断

      throw 'message'
      throw new Error('message')
      
    • try/catch/finally:不中断

      try {} catch(e) {} finally {}
      

      finally 一定会执行

    • debugger:断点

      debugger
      
  • this 指向:

    • call()thisArg 为指定的 this 值,其余为实参传入函数

      fun.call(thisArg, arg1, arg2, ...)
      
    • apply()thisArg 为指定的 this 值,其余参数放入数组

      fun.apply(thisArg, [argsArray])
      

      例:方便数组展开

      Math.max.apply(null, arr)
      
    • bind():不调用函数,返回新函数

      fun.bind(thisArg, arg1, arg2, ...)
      

      例:不立即调用

      btn.addEventListener('click', function () {
          this.disabled = true
          setTimeout(function () {
              this.disabled = false
          }.bind(this), 2000)
      })
      
  • 防抖:只执行最后一次

    • Lodash 防抖函数:单位为 ms

      _.debounce(func, [wait])
      
    • 手写防抖函数:

      function debounce(func, wait) {
          let timer
          return function() {
              timer && clearTimeout(timer)
              timer = setTimeout(func, wait)
          }
      }
      
  • 节流:只执行一次

    • Lodash 节流函数:

      _.throttle(func, wait)
      
    • 手写节流函数:定时器开启内无法清除

      function throttle(func, wait) {
          let timer
          return function() {
              if (!timer) {
                  timer = setTimeout(function() {
                      func()
                      timer = null
                  }, wait)
              }
          }
      }
      
目录

ConnectC Is All You Need.