Skip to content

1 基础

约 2010 个字 102 行代码 预计阅读时间 8 分钟

Intro

JavaScript & ECMAScript

一般情况下我们认为两者等价

  • ECMAScript 是一个标准,需要各个浏览器厂商进行实现
  • JavaScript > ECMAScript,其完整实现囊括了 ECMAScript, DOM and BOM.
  • JavaScript 的特点
    • 解释型语言
    • 类似于 C & Java 的语法结构
    • 动态语言
    • 基于原型的面向对象
  • JavaScript 的编写位置
    • HTML 文件的 <script> 标签中
    • 外部文件中,并通过 <script> 标签引入
      • 可以被不同文件引用,利用浏览器的缓存机制加快加载过程
      • 用于引入外部文件的 <script> 标签内书写的内容会被忽略(禁止吃里扒外)
  • 基本规则
    • 严格区分大小写
    • 每一条语句以 ; 结尾,不手写浏览器会自动添加(也可能加错)
    • 忽略多个空格和换行

1 字面量 & 变量

  • 字面量

    一些不可改变的值(如 1 / 1000),可以直接使用(但一般不直接用)

  • 变量

    可以用于保存字面量,其值可以任意改变;

    简单的变量声明:var a,此时我们尚未赋值,a = undefined

标识符命名规则

在 JS 中,所有支持自主命名的都称为“标识符”(如 变量名 / 函数名 / 属性名),需要满足以下规则:

  • 可以包含:字母、数字、下划线、$
  • 不能以数字开头
  • 不能是 ES 中的 关键字 / 保留字
  • 建议使用小驼峰命名法

JS 底层使用 Unicode 编码保存标识符,理论上支持 utf-8 中的所有内容(比如中文)

使用 XXXX 表示 Unicode 字符的十进制编号:

  • JS 中使用 \uXXXX 输出

  • HTML 中使用 &#XXXX 输出

1.1 基本数据类型

JS 中共包含六种数据类型:

  • 基本数据类型:String, Number, Boolean, Null, Undefined
  • 引用数据类型:Object

我们可以使用 typeof varName 简单检查变量的类型。

String

  • 需要使用引号包裹(单双都行,但不要混用)
  • 引号不能嵌套(可以大肠包小肠,但是自产自销时需要用 \ 进行转义)
  • 特殊符号也需要使用 \ 进行转义

Number

整数计算精度有保障,但是浮点数就 emmm ...

  • JS 中的所有数值都属于 Number 型(整数 & 浮点数)
  • 最值与无穷

    • 最大值 Number.MAX_VALUE = \(1.7976931348623157*10^{308}\),超过这个值将返回 Infinity
    • 最小值是最大值的相反数,超过则返回 -Infinity
    • Number.MIN_VALUE = \(0.5*10^{323}\) 表示支持的最小正值

    Infinity 是一个类型为 Number 的字面量,我们可以通过 var a = Infinity 进行赋值

  • NaN 表示 Not a Number,是一个 Number 型的字面量

Boolean

true or false

Null

只有 null 值,表示 空对象,使用 typeof 返回 Object

Undefined

只有 undefined 值,表示 未定义(声明但没赋值)

1.2 强制类型转换

转 String

关于 toString 方法
  • toString 方法不会改变原变量的类型,而是返回一个 String 型的执行结果
  • Null & Undefined 类型没有 toString 方法,调用时会报错
  1. 调用被转换数据类型toString 方法
    var a = 123;
    var b = a.toString();
    console.log(typeof a, typeof b); // => number, string
    
    // 其实也可以
    a = a.toString();
    
  2. 调用 String 函数
    • 对于 Number / Boolean 类型,直接调用 toString 方法
    • 对于 Null / Undefined 类型,直接转为字符串
      var a = 123;
      a = String(a); // => a = "123"
      
      // 兼容 null & undefined
      var b = null;
      var c = undefined;
      console.log(String(b), String(c)); 
      // => "null" & "undefined"
      

转 Number

  1. 使用 Number 函数

    Type Result
    存在非数字内容 NaN
    空串 / 纯空格 0
    true 1
    false 0
    null 0
    undefined NaN
  2. 使用 parseInt / parseFloat 函数(String 类型专用)

    • 提取从头开始匹配的最长连续字串

      • parseInt("123px456") => 123
      • parseFloat("123.456.789") => 123.456
    • 对「非 String 类型」调用会先转换为 String,再进行对应转换。

      • parseInt(true) => parseInt("true") => NaN
      • 甚至可以悄悄取整 parseInt(198.23) => parseInt("198.23") => 198

进制转换

  • 非十进制的表示

    进制 前缀 示例
    16 0x 0x12f
    8 0 070
    2 0b 0b1010
    • console.log() 均以十进制输出结果
    • 不是所有浏览器都支持以 0bxxx 表示二进制数
  • 指定进制解析

    • 场景:parseInt("070") 视浏览器可能被解析为 \(56_o\)\(70_d\)
    • 解决:传入第二个参数指定进制 parseInt("070", 8) === 56

转 Boolean

使用 Boolean 函数,转换规则如下:

Input Result
一般数字 true
0 false
NaN false
一般字符串 / 纯空格 true
空串 false
null false
undefined false
Object true

1.3 包装类

用于将「基本数据类型」转换为「对象」

  • JS 中存在三个包装类 String(), Number(), Boolean()

    var num = new Number(3); // typeof num 返回 Object(而非 Number)
    
    // 注意:实际开发时不会使用(比较结果不符合预期)
    var n1 = 3; var n2 = 3;
    n1 == n2; // => true
    var n3 = Number(3); var n4 = Number(3);
    n3 == n4; // => false 对象地址不同
    // 以及 Boolean 对象作为条件
    var b = new Boolean(false);
    if(b) console.log("ok"); // 会输出 ok(Object 转 Boolean 为 true)
    

  • 我们可以为包装类对象添加属性

    num.hello = "dfghjk"; // 若是 var num = 3; 就会报错哩
    console.log(num.hello);
    

  • 当我们试图调用「基本数据类型」的 属性/方法 时,浏览器会临时使用包装类将其转换为对象

    var a = 123; // 显然此时 a 是一个 Number
    a = a.toStirng(); // a 被临时转换为 Number 对象,调用对象的 toString
    console.log(typeof a); // => string
    
    a.hello = "hello"; // 不报错 -> 临时转换为对象,马上销毁
    console.log(a.hello); // => undefined 又被临时转换为对象(但不是同一个)
    

1.4 String 对象方法

包装类的存在使得我们可以直接使用 String 对象的方法

  • String 在底层是以「字符数组」的形式保存的

    • 可以通过 str.length 获取字符串长度
    • 可以通过 str[idx] 获取指定位置上的 char
  • 下面是一些方法

    方法 描述
    charAt(idx) 返回 str[idx]
    charCodeAt(idx) 返回 str[idx] 的 Unicode 编码
    fromCharCode(charCode) 从字符编码创建一个字符串
    concat(str1, str2, ...) 拼接多个字符串
    indexOf("str", start) 返回从 start 开始首次出现该子串的起始下标,不存在时返回 -1
    lastIndexOf("str") 返回最后一次出现该子串的起始下标,不存在时返回 -1
    slice(start, end) 返回 str[start, end) 子串,不影响原串(允许传入负数)
    subString(start, end) 和 slice 类似(不允许传入负数,且 p2<p1 时自动交换)
    subStr(start, len) 不太建议使用
    split("指定分隔符") 将 String 拆分为 Array (不指定/指定空串会逐个拆开)
    toLowerCase()/toUpperCase() 大小写转换

2 基本运算

2.1 运算符

  • 类型运算符 typeof

    将值的类型以 String 形式返回

  • 算数运算符(二元)

    • + (左结合)

      • String + 任意类型,将“任意类型值”转换为 String,随后进行 concat

        我们可以使用 var + '' 将任意数据类型转为 String(隐式类型转换,由浏览器自动调用 toString 函数)

      • Number + 任意非String类型,会对非 Number 的值进行转换后运算

      • 任何值与 NaN 运算均返回 NaN
        • - * / 会把两侧的数据均转换为 Number

      可以使用 var -0 / *1 / /1 将任意数据类型专为 Number

    • % 朴素的取模

  • 一元运算符

    • + - 正负号,会将非 Number 转为 Number
    • ++ -- 自增自减
      • ++a 先递增再返回结果
      • a++ 先返回结果再递增
  • 逻辑运算符: 非 Boolean 类型会先进行转换

    • !

      可以使用 !!var 快速将变量转为 Boolean

    • &&

      • 第一个条件失败后,不会执行第二个条件
      • True && Ture,返回后面的 True 结果
      • 存在 False 时,返回首个 False 结果
    • ||
      • 第一个条件成功后,不会执行第二个条件
      • Ture || ?,返回首个 Ture 结果
      • False || ?,返回第二个结果
  • 赋值运算符 = += -= *= /= %=

  • 关系运算符 > >= < <=

    • 会将非 Number 转换为 Number,返回 Boolean
    • 符号两侧均为 String 时,直接逐位比较 Unicode 编码
    • 任何值与NaN比较均返回 false
  • 相等运算符

    • == 会自动将两侧转为相同类型进行比较(大部分情况下转 Number)

      • undefined == null -> true

      • NaN == Anything -> false (包括 NaN == NaN,需要用 isNaN 函数判断)

    • === 全等(不会做类型转换,类型不同直接 false

    • != 不等,!== 不全等
  • 条件运算符(三元)condition?case1:case2

    当 condition 的结果非 Boolean 类型时会进行转换

2.2 优先级

序号越大优先级越低,平级时左结合

  1. . [] new
  2. ()
  3. ++ --
  4. ! ~ +/- 符号 typeof void delete
  5. % * /
  6. +/- 加减法
  7. << >> >>>
  8. < <= > >=
  9. == != === !==
  10. &
  11. ^
  12. |
  13. &&
  14. ||
  15. ?:
  16. = += -= /= *= %= <<= >>= >>>= &= ^= |=

3 语句

获取用户输入

您可以使用 prompt("提示语句") 弹出弹窗以获得用户输入:

document.write("您的输入是:",prompt("say sth:"));// 将会在 body 中显示您的输入

  • IF 多分支模版

    // First Match
    if(condition1) {
        case1;
    } else if(condition2) {
        case2;
    } else {
        case3;
    }
    

  • Switch 模版

    switch(variable) {
        // 会对依次对 case 进行 === 比较,并将匹配位置作为 entry
        // 没有 break 就会按照书写顺序一直向下执行
        case case1: // case1 = 60 / score>=60
            break;
        case case2:
            break;
        default:
            break;
    }
    

  • While 模版

    // while 循环 -> 可能一次也不执行
    while(condition) {
        sth;
    };
    
    // do-while 至少执行一轮
    do {
        sth;
    } while (condition)
    

  • For 模版

    • 执行顺序:初始化 -> 判断条件 -> 循环体 -> 更新
    • 初始化/条件/更新都可以 省略 or 写在外面(要留 ;
    for(var i=initVal; i<endVal; i++) {
        sth;
    }
    
  • break & continue

    • break 可用于退出最近的一层 switch / 循环语句(不能用于 IF)
    • continue 可以用于逃过当次循环的余下部分(作用于最近的循环)
使用 Label 标识循环语句

我们可以通过 labelName:循环语句 的形式进行标识

hello:for(var i=0; i<10; i++) {
    for(var j=0; j<5; j++) {
        // 可以通过 break label 指定需要终止的循环
        break hello;
    }
}

4 作用域 Scope

变量作用的范围

  • JS 共有两种作用域

    • 全局作用域

      • 页面打开时创建,关闭时销毁
      • 不被函数包裹的代码均在全局作用域中
      • 具有代表浏览器窗口的全局对象 window ,可以直接使用

        在全局作用域中创建的变量都会作为「window 的属性」保存,可以在任意位置访问

        var a = 10;
        // 直接 a = 10 也行
        console.log(window.a);
        

        在全局作用域中创建的函数都会作为「window 的方法」保存

        function fun() {console.log("hello")};
        window.fun(); // => 控制台输出 hello
        

      • 变量的声明提前

        使用 var 声明的变量将在「所有代码执行前」被定义(但没有赋值!)

        console.log(a); // 输出 undefined 但不报错
        var a = 123;    // 先使用后声明,此处才进行赋值
        a = 123;        // 这样会报错
        
      • 函数的声明提前

        以「声明」形式创建的函数会在所有代码执行前被创造(可以在声明前调用)

        f1();
        function f1() {console.log("F1")}; // 声明形式创建
        
        f2(); // 报错 -> 此时 f2 = undefined
        var f2 = function() {console.log("f2")}; // 此处才为 f2 赋值
        
    • 函数作用域

      • 调用函数时创建,执行完毕后销毁(每调用一次就会创建一个独立的作用域)
      • 可以访问全局变量,但是全局作用域不能访问函数作用域中变量(var定义)
      • 局部变量与全局变量同名时,优先使用局部变量(可以用 window.name 强行指定)

        本地不存在时,逐层向上一级查找(直至全局作用域,还是没有报 ReferenceError

      • 也存在「声明提前」

        var a = 123;
        function f() {
            console.log(a); // 输出 undefined -> 局部声明提前
            var a = 234;
            a = 345; // 就是全局变量(没有提升),上面输出 123(全局值)
            b = 567; // 没有使用 var 关键字 => 定义了变量 b
        
            subF(); // 函数声明提前,正常调用
            function subF() {console.log("subF")};
        }
        
        console.log(a); // 若没有 var => c 就是全局变量,输出 345
        console.log(b); // 正常输出 567(在函数中定义了全局变量)
        
        // 参数列表相当于 var params;
        var e = 111;
        function f2(e) {    // 等同于 var e
            console.log(e); // => undefined
            e = 222;        // => 修改的是局部变量
        }
        

5 垃圾回收 GC

  • 「垃圾」
    • 当一个对象没有任何变量或属性对其进行引用时,我们将永远无法对其进行操作
    • 「垃圾」会占用大量内存空间,导致程序运行速度变慢
  • JS 的「垃圾回收机制」

    JS 会「自动」将垃圾从内存中销毁(不允许进行手动操作)

    但是我们不手动将不再使用的变量指向 null ,JS 将无法进行识别