夜听城嚣 夜听城嚣
首页
  • 学习笔记

    • 《JavaScript高级程序设计》
    • 前端基建与架构
  • 专题分享

    • Git入门与开发
    • 前端面试题汇总
    • HTML和CSS知识点
  • 项目实践
  • 抓包工具
  • 知识管理
  • 工程部署
  • 团队规范
bug知多少
  • 少年歌行
  • 青年随笔
  • 文海泛舟
  • 此事躬行

    • 项目各工种是如何协作的
    • TBA课程学习
收藏

dwfrost

前端界的小学生
首页
  • 学习笔记

    • 《JavaScript高级程序设计》
    • 前端基建与架构
  • 专题分享

    • Git入门与开发
    • 前端面试题汇总
    • HTML和CSS知识点
  • 项目实践
  • 抓包工具
  • 知识管理
  • 工程部署
  • 团队规范
bug知多少
  • 少年歌行
  • 青年随笔
  • 文海泛舟
  • 此事躬行

    • 项目各工种是如何协作的
    • TBA课程学习
收藏
  • 第1章 什么是JavaScript
  • 第2章 HTML中的JavaScript
  • 第3章 语言基础
    • 第4章 变量、作用域与内存
    • 第5章 基本引用类型
    • 第6章 集合引用类型
    • 第7章 迭代器与生成器
    • 第8章 对象、类与面向对象编程
    • 第9章 代理与反射
    • 第10章 函数
    • 第11章 异步编程
    • 第12章 BOM
    • 第14章 DOM
    • 第15章 DOM扩展
    • 第16章 DOM2和DOM3
    • 第17章 事件
    • 第18章 动画与Canvas图形
    • 第19章 表单脚本
    • 第20章 JavaScript API
    • 第21章 错误处理与调试
    • 第23章 JSON
    • 第24章 网络请求与远程资源
    • 第25章 客户端存储
    • 第26章 模块
    • 第27章 工作者进程
    • 第28章 最佳实践
    • 《JavaScript高级程序设计》
    frost
    2021-06-27

    第3章 语言基础

    # 语法

    # 区分大小写

    ECMAScript 中,一切都区分大小写。 typeof 和 Typeof 完全不同。

    # 标识符

    标识符,就是变量、函数、属性或函数参数的名称。

    • 第一个字符必须是字母、_或$
    • 剩下的其他字符可以 是字母、下划线、美元符号或数字

    标识符推荐使用驼峰大小写形式,如 myName。

    # 注释

    • 单行注释

      // 单行注释
      
      1
    • 多行注释

      /*
      多行注释
      */
      
      1
      2
      3

    # 严格模式

    一般在脚本开头加上

    'use strict'
    
    1

    不规范写法在严格模式下会被处理,不安全的活动会报错。

    # 语句

    建议以分号结尾。

    if 之类的控制语句在执行多行语句时要求有代码块,即左右 花括号 包裹。

    # 关键字和保留字

    规定或建议,不能用关键字作为标识符或属性名。

    # 变量

    # var

    var 声明的变量在当前作用域下生效。另外,变量的查找遵循变量搜索原则。

    var 声明的变量会自动提升到函数作用域的顶部。

    var 在全局作用域下声明的变量会成为 window 对象的属性。

    # let

    与 var 不同,let 声明的范围是块作用域,它是函数作用域的子集。

    let 声明的变量不会提升。

    let 在全局作用域中声明的变量不会成为 window 对象的属性。

    不允许在 let 声明之前使用变量,也不允许在同一作用域中重复声明。

    for 循环中的 let 声明,每个迭代循环会声明一个新的迭代变量,如

    for (let i = 0; i < 5; i++) {
      setTimeout(() => {
        console.log(i)
      }, 0)
    }
    // 0,1,2,3,4
    
    1
    2
    3
    4
    5
    6

    # const

    基本语法和 let 相似,不同点是它声明的变量必须初始化,同时不允许再次赋值。

    对于对象类型,它只是指向变量的引用,对象内部属性的修改是允许的。

    # 最佳实践

    不再使用 var。

    优先使用 const,对于可预见会被修改的变量,可以使用 let。

    # 数据类型

    • 6 种简单数据类型
      • Undefined
      • Null
      • Boolean
      • Number
      • String
      • Symbol
    • 复杂数据类型
      • Object,包括 object,Array,function,内置对象等

    判断类型的方式

    typeof:可用来判断简单数据类型。typeof a

    Instanceof:判断构造函数的原型是否在该对象的原型链上。 b instanceof Array

    Object.prototype.toString.call(xxx)

    # undefined

    对于声明了却未初始化的变量,如下

    let a
    console.log(a) // undefined
    console.log(typeof a) // 'undefined'
    
    1
    2
    3

    如果未定义一个变量,则不能直接访问,会报错;但可以使用 typeof 来执行

    console.log(b) // ReferenceError: b is not defined
    console.log(typeof b) // 'undefined'
    
    1
    2

    # null

    逻辑上讲,null 值表示一个空对象指针

    let a = null
    console.log(typeof a) // 'object'
    
    1
    2

    所以,当声明一个对象类型的变量,但不确定值时,可以使用 null 来初始化。

    # boolean

    只有 true 和 false2 个值,在 if 等流程控制语句中会将其他类型转为 boolean 类型

    // string
    !!'1' === true
    !!'0' === true
    !!'' === false
    
    // number
    !!0 === false
    !!NaN === false
    !!1 === true
    !!Infinity === true
    
    // object
    !!{} === true
    !![] === true
    !!null === false
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    # number

    包括二进制、八进制、十进制、十六进制等

    科学计数法,如 3.125e7===31250000

    精度丢失,因为十进制转二进制计算时,二进制表示的数会出现 0 舍 1 入,导致精度丢失。经典示例如 0.1+0.2 !==0.3

    值的范围:最大数 Number.MAX_VALUE,最小数 Number.MIN_VALUE,超出这个范围则为无穷大,可用 isFinite 判断一个数是否无穷大。

    console.log(isFinite(1)) // true
    console.log(isFinite(Number.MAX_VALUE)) // true
    console.log(isFinite(Number.MAX_VALUE + Number.MAX_VALUE)) // false
    
    1
    2
    3

    NaN:Not a Number。

    console.log(0 / 0) // NaN
    console.log(+0 / 0) // NaN
    console.log(+0 / -0) // NaN
    console.log(Number('a')) // NaN
    console.log(1 / 0) // Infinity
    console.log(-1 / 0) // -Infinity
    
    1
    2
    3
    4
    5
    6

    涉及 NaN 的操作始终返回 NaN;NaN 不等于任何值(包括 NaN)

    console.log(NaN + 1) // NaN
    console.log(NaN === NaN) // false
    
    1
    2

    可用 IsNaN()函数判断是否 NaN,它会先尝试将参数转为数值类型

    console.log(isNaN(NaN)) // true
    console.log(isNaN('a')) // true
    console.log(isNaN('')) // false
    console.log(isNaN(10)) // false
    console.log(isNaN(true)) // false
    
    1
    2
    3
    4
    5

    数值转换:有 3 个函数,Number(),parseInt(),parseFloat()

    注意:parseInt('') 得到 NaN,另外可以传入第二个参数,表示进制。

    # string

    表示方式:'' 、""、``

    除了 null 和 undefined 没有 toString 方法,其他类型都有。可以传入一个参数,表示进制。

    如果要强制转换成 string,可以使用 String()

    # Symbol

    译为”符号“,Symbol 实例是唯一,不可变的,用途是确保对象属性使用唯一标识符,不会发生属性冲突的危险。

    # 基本用法
    let foo1 = Symbol('foo')
    let foo2 = Symbol('foo')
    
    console.log(typeof foo1) // symbol
    console.log(foo1 === foo2) // false
    
    1
    2
    3
    4
    5

    此外,Symbol 不能用作构造函数,即不能与 new 一起使用

    # 全局符号注册表

    由于 Symbol 实例具有唯一性,那么怎么共享 Symbol 呢,使用 Symbol.for()方法

    // 局部!==全局
    const localSymbol = Symbol('foo')
    const globalSymbol = Symbol.for('foo')
    console.log(localSymbol === globalSymbol) // false
    
    // 全局可以共享
    const globalSymbol2 = Symbol.for('foo')
    console.log(globalSymbol === globalSymbol2) // true
    
    1
    2
    3
    4
    5
    6
    7
    8

    此外可以使用 Symbol.keyFor()来查询全局注册表,入参为 Symbol 实例,返回对象的字符串键。

    let s = Symbol.for('foo')
    console.log(Symbol.keyFor(s)) // foo
    
    1
    2
    # 常用内置符号

    1.Symbol.iterator

    表示一个方法,该方法返回对象默认的迭代器,由 for-of 语句使用,for-of 会利用这个函数执行迭代操作。

    class Emitter {
      constructor(max) {
        this.max = max
        this.idx = 0
      }
      *[Symbol.iterator]() {
        while (this.idx < this.max) {
          yield this.idx++
        }
      }
    }
    
    function count() {
      let emitter = new Emitter(3)
      for (let x of emitter) {
        console.log(x)
      }
    }
    
    count()
    // 0
    // 1
    // 2
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23

    上述代码中,count 方法等同于下面的 count

    function count() {
      let emitter = new Emitter(3)
    
      let iterator = emitter[Symbol.iterator]()
    
      let progress = iterator.next()
      console.log(progress.value)
      progress = iterator.next()
      console.log(progress.value)
      progress = iterator.next()
      console.log(progress.value)
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    2.Symbol.asyncIterator

    与 Symbol.iterator 类似,不过这个表示实现异步迭代的函数,该方法返回对象默认的 AsyncIterator,由 for-await-of 使用。

    class Emitter {
      constructor(max) {
        this.max = max
        this.asyncIdx = 0
      }
      async *[Symbol.asyncIterator]() {
        console.log('start')
        while (this.asyncIdx < this.max) {
          yield new Promise((resolve) => resolve(this.asyncIdx++))
        }
      }
    }
    
    async function asyncCount() {
      let emitter = new Emitter(5)
      console.log('emitter', emitter)
      for await (const x of emitter) {
        console.log(x)
      }
      // 等同于下面的调用
      // let progress = emitter[Symbol.asyncIterator]()
      // let a = await progress.next()
      // console.log(a.value)
      // a = await progress.next()
      // console.log(a.value)
      // a = await progress.next()
      // console.log(a.value)
      // a = await progress.next()
      // console.log(a.value)
      // a = await progress.next()
      // console.log(a.value)
    }
    
    asyncCount()
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34

    3.Symbol.hasInstance

    可以理解为将 instanceof 转为函数行为

    function Foo() {}
    
    let foo = new Foo()
    
    console.log(foo instanceof Foo) // true
    console.log(Foo[Symbol.hasInstance](foo)) // true
    
    1
    2
    3
    4
    5
    6

    此外还有 Symbol.match、Symbol.replace、Symbol.search、Symbol.split 等,这里不再列举。

    # Object

    对象是一组数据和功能的集合。Object 实例包括如下属性和方法。

    constructor:创建当前对象的函数。

    hasOwnProperty(propertyName):当前对象实例上(不是原型)是否存在给定的属性。

    isPrototypeof(Object):用于判断当前对象是否为另一个对象的原型。

    propertyIsEnumerable(propertyName):判断给定属性是否可以用 for-in 枚举。

    toLocaleString():返回对象的字符串表示,该字符串反映对象所在本地化的执行环境。

    toString():返回对象的字符串。

    valueOf():返回对象对应的字符串、数值或布尔值表示。

    # 一元操作符

    # 1.递增/递减操作符
    • 前缀版 ++age --age
    • 后缀版 age++ age--
    let age = 20
    let newAge = age++
    console.log(age) // 21
    console.log(newAge) // 20
    
    1
    2
    3
    4
    let age = 20
    let newAge = ++age
    console.log(age) // 21
    console.log(newAge) // 21
    
    1
    2
    3
    4

    递减类似,在之前的值减 1。

    tips:这 4 个操作符可作用于任何值,可能涉及到数据类型转换。

    # 2.一元加和减

    常用于将值转换为数值类型。例如

    let a = '1.2'
    let b = false
    console.log(+a) // 1.2
    console.log(-b) // -0
    
    1
    2
    3
    4

    # 位操作符

    正值在计算机中以二进制格式存储,负值以二补数的二进制存储,过程如下

    (1)确定绝对值的二进制表示,如-18 的绝对值为 18;

    (2)找到数值的补数,即 0 变为 1,1 变为 0;

    (3)给结果加 1。

    18
    二进制如右 0000 0000 0000 0000 0000 0000 0001 0010
    补数			1111 1111 1111 1111 1111 1111 1110 1101
    加一		  1111 1111 1111 1111 1111 1111 1110 1110
    得-18
    
    1
    2
    3
    4
    5

    按位非:~,返回数值的补数

    按位与:&,根据真值表,两个操作数为 1 时返回 1,有 0 时则为 0.

    按位或:|,两个数均为 0 时返回 0,有 1 时则为 1.

    按位异或:^,两个数相同时返回 0,相异时为 1.

    左移:<<,所有位数向左移动,注意符号位不动。

    有符号右移:>>,所有位数向右移动,不足补 0,符号位不动。

    无符号右移:>>>,所有位数向右移动,不足补 0,符号位也跟着移动。

    # 布尔操作符

    逻辑非:!,先转为布尔值,再取反。

    逻辑与:&&,真真得真,有假得假。

    逻辑或:||,有真得真,假假得假。

    注意,&&和||都是短路操作符。

    # 乘性操作符

    乘法操作符:数学运算中的乘法,*表示。

    除法操作符:数学运算中的乘法,/表示。

    取模操作符:%,取余数。指数操作符:**,代替 Math.pow()

    console.log(Math.pow(3, 2)) // 9
    console.log(3 ** 2) // 9
    
    1
    2

    # 加性操作符

    加法操作符:+,求 2 个数的和。注意如果有字符串,则不是加法运算,而是字符串拼接。

    let num1 = 5,
      num2 = 10
    console.log('result is ' + num1 + num2) // result is 510
    console.log('result is ' + (num1 + num2)) // result is 15
    
    1
    2
    3
    4

    减法操作符:-,数学运算减,也会进行类型转换。

    # 关系操作符

    比较两个值的大小,包括<、>、<=、>=,注意如下规则:

    • 如果操作数都是数值,则比较数值
    • 如果都是字符串,则逐个比较字符串中对应字符的编码
    • 如果有一个是数值,则将另一个数转换为数值,进行数值比较
    • 如果有对象,则调用其 valueOf 方法,取结果进行比较,否则调用 toString()方法比较
    • 如果是布尔值,将其转为数值,进行比较

    此外,任何关系操作符涉及比较 NaN 时都返回 false

    console.log(NaN < 3) // false
    console.log(NaN >= 3) // false
    
    1
    2

    # 相等操作符

    • ==,比较前会执行转换
    • ===,不会执行类型转换,推荐使用
    console.log(10 == '10') // true
    console.log(10 === '10') // false
    
    1
    2

    # 条件操作符

    即三元表达式,variable = boolean_expression ? true_value : false_value

    # 赋值操作符

    包括简单赋值(=)和复合赋值(如+=,-=,*=等)

    let num = 10
    num = num + 10
    // 可以简写为
    num += 10 // 只是写法简洁,并不会提升性能
    
    1
    2
    3
    4

    # 逗号操作符

    有 2 种常见场景:

    1.声明变量

    let num1 = 1,
      num2 = 2
    
    1
    2

    2.辅助赋值,取表达式最后的一个值

    let num = (3, 2, 1) // num=1
    
    for (let i = 0, j = 0; i < 4, j < 3; i++, j++) {
      console.log(i) // 0,1,2
    }
    for (let i = 0, j = 0; i < 3, j < 4; i++, j++) {
      console.log(i) // 0,1,2,3
    }
    
    1
    2
    3
    4
    5
    6
    7
    8

    # 语句

    也称为流控制语句。

    # if 语句

    if (true) {
      console.log(1)
    }
    if (false) {
      // ...
    } else if (true) {
      // ...
    } else {
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    # while 语句

    while (true) {
      // ...
    }
    
    1
    2
    3

    # do-while 语句

    do {
      // ...
    } while (true)
    
    1
    2
    3

    # for 语句

    for (let i = 0; i < 5; i++) {
      console.log(i)
    }
    
    1
    2
    3

    # for-in 语句

    用来枚举对象中的非符号属性,是无序遍历

    let obj = { a: 1, b: 2 }
    for (const key in obj) {
      console.log(obj[key])
    }
    
    1
    2
    3
    4

    # for-of 语句

    用于遍历可迭代对象的元素,比如数组

    let nums = [1, 2, 3]
    for (const item of nums) {
      console.log(item)
    }
    
    1
    2
    3
    4

    # 标签和 break/continue 语句

    标签用来给语句做标识,一般配合 break 和 continue 使用

    break 用于立即退出当前循环体,执行循环体外的下一条语句。

    continue 用于中断当前轮次循环,会再次从循环体顶部继续执行下一轮次。

    for (let i = 0; i < 3; i++) {
      if (i === 1) {
        continue
      }
      console.log(i) // 0 2
    }
    console.log('----')
    
    for (let i = 0; i < 3; i++) {
      if (i === 1) {
        break
      }
      console.log(i) // 0
    }
    console.log('----')
    
    for (let i = 0; i < 3; i++) {
      for (let j = 0; j < 3; j++) {
        if (j === 1) continue
        console.log(j) // 0 2 0 2 0 2
      }
    }
    console.log('----')
    
    // 仅跳出当前循环体
    for (let i = 0; i < 3; i++) {
      for (let j = 0; j < 3; j++) {
        if (j === 1) break
        console.log(j) // 0 0 0
      }
    }
    console.log('----')
    
    // 使用标签:中断外层循环体
    outer: for (let i = 0; i < 3; i++) {
      inner: for (let j = 0; j < 3; j++) {
        if (j === 1) continue outer
        console.log(j) // 0 0 0
      }
    }
    console.log('----')
    
    // 使用标签:跳出外层循环体
    outer: for (let i = 0; i < 3; i++) {
      inner: for (let j = 0; j < 3; j++) {
        if (j === 1) break outer
        console.log(j) // 0
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49

    # switch 语句

    let i = 5
    switch (i) {
      case 1:
        console.log('small')
        break
      case 5: // 没有break则表示满足一个即可
      case 6:
        console.log('mid')
        break
      default:
        console.log('big')
        break
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    # 函数

    1.函数声明会提前

    2.基本写法如下

    function a(arg1, arg2) {
      console.log(arg1, arg2)
      return 1
    }
    
    1
    2
    3
    4

    包括函数名(a),参数(arg1,arg2),函数体 console.log(...),返回值(如过没有,则默认返回 undefined)

    3.return 可以出现在函数任何地方,一旦出现,函数会立即停止执行并退出。

    上次更新: 2022/11/28, 18:07:15
    第2章 HTML中的JavaScript
    第4章 变量、作用域与内存

    ← 第2章 HTML中的JavaScript 第4章 变量、作用域与内存→

    最近更新
    01
    提交代码时修改commit消息
    04-09
    02
    如何快速定位bug
    02-20
    03
    云端web项目开发踩坑
    08-25
    更多文章>
    Theme by Vdoing | Copyright © 2021-2025 dwfrost | 粤ICP备2021118995号
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式
    ×