JS语言基础

本文最后更新于:2020年11月7日 凌晨

语言基础

区分大小写

ECMAScript中一切区分大小写,无论函数,变量,操作符(虽然 typeof 这种关键字不能作为变量名,但是Typeof可以);

标识符

变量,函数名,属性,函数参数的名称,要求如下:

  • 第一个字符必须是字母,$符号,或者下划线_开头;
  • 剩下的字符可以是字母,下划线,美元符号,或者是数字;
  • 字母可以是Unicode字母或者ASCII字母,但是不推荐使用;
  • 关键字,保留字,true, false, null 不能做标识符;
  • 一般使用驼峰命名,因为很多JS内置函数都是用驼峰,但是不是强制

注释

1
2
3
4
// 这是单行注释
/*
这是多行注释
*/

严格模式

严格模式会解决ES3或者之前不规范的地方,对于不安全的错误会抛出;

使用方法:

  • 在脚本文件的开头: "use strict";

  • 或者在函数内部单独使用:

    1
    2
    3
    4
    function dpSomeThing() {
    "use strict";
    console.log("Hello World!");
    }

语句

ECMAScript中语句都是用分号结尾的;由解析器确定语句在哪结尾;

即使分号不是必须的,但是加上分号是有规范意义的;

包括像条件语句中,可以加上{ }来明确代码块;

变量

ECMAScript中变量是松散类型,即变量可以存储任何类型的数据;

每一个变量只是用于保存任意值的占位符;

存在三个值声明变量:

  • var
  • const
  • let

注意: var 在任何ES版本中都可以使用,但是 const 和 let 只能在ES6以及更晚的版本中使用;

var关键字

var在全局作用域声明会成为window的属性中

1
2
3
var age = 10;

console.log(window.age); // 10
  • 语法:

    1
    2
    3
    4
    5
    6
    // var关键字 + 变量名;
    var message;

    // 声明并赋值
    // 这里不会说明该变量为字符串类型,只是简单的赋值,后续可以更改值甚至值类型;
    var name = "hi";

    当我们不对变量赋值的时候,变量的初始值为undefiened;

  • 🌟🌟作用域:

    当在函数内部使用 var,变量就是局部变量;

    函数内部的var定义的变量,在函数推出的时候就被销毁:

    1
    2
    3
    4
    5
    function test() {
    var a = 10;
    }

    console.log(a); // undefined

    当我们在函数内部取消 var 关键字,就会使的变量成为全局变量,在函数外部也会访问到;首先必须调用包含这个变量的函数!

    1
    2
    3
    4
    5
    6
    function test_2() {
    a = 10;
    }
    // 必须调用这个函数才可以使a成为全局变量;
    test_2();
    console.log(a); // 10

    👀:但是不推荐这么做,在局部定义域中定义全局变量不确定是声明还是赋值,很难维护;

  • 声明多个变量:

    1
    2
    // 声明多个变量使用逗号隔开
    var name = "xxx", age = 12, gen = "男"
  • 🌟🌟🌟 var的声明提升 (hoist )

    首先请看下面的例子:

    1
    2
    3
    4
    5
    function test() {
    console.log(age);
    var age = 10;
    }
    test(); // undefined

    使用 var 关键字,会将该变量自动提升到函数作用域顶部;

    这里并不会像上面一样报错,是因为等价于:

    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
    function test_2() {
    var age;
    console.log(age);
    age = 10;
    }
    test_2();


    /*
    等价于
    先:
    var age;
    age = 20;
    console.log(age);
    */
    function test_3() {
    age = 20;
    var age;
    console.log(age);
    }
    test_3(); // 20


    // 反复声明多个var 变量也不会报错,以最后那个最准:
    function cmpVarable() {
    var age = 10;
    var age = 12;
    var age = 26;
    console.log(age);
    }
    cmpVarable(); // 26

    说明,在JS引擎中一般分为两步:

    • 初始化阶段 ( Creation Stage)

      • 创建 var 变量, function 函数和函数的arguments 参数
      • 如果作用域是函数内部,把函数参数放进前面的context json 中;
      • 扫描当前作用域寻找函数(优先寻找函数):
        • 每发现一个函数,就把名字和函数指针放进前面的json中;
        • 如果函数名已经存在,覆盖之前的函数指针;
      • 扫描当前作用域寻找变量:
        • 每发现一个变量 var,就把名字放进前面的json中,并把值设>成 undefined
        • 如果变量名已经存在,不会覆盖,忽略然后继续扫描
    • 代码执行阶段 (Activation/Code Execution Stage)

      • 给变量和函数赋值,以及执行代码
      • 逐行执行代码,并且赋值之前为undefined的变量var
let关键字

let关键字和var关键字最大的区别是,let声明范围是块级作用域;var声明的则是函数作用域;

  • 语法:

    1
    let message;

    由于js引擎会记录变量声明的标识符以及所在的作用域,因此嵌套的使用相同let标识符不会报错;

  • 作用域:

    块级作用域;

    块级作用域是函数作用域的子集;

  • 暂时性死区(temporal dead zone)

    在解析代码,js引擎也会注意到let声明,但是在之前不能以任何形式引用未声明的变量;

  • 全局声明

    var 在全局声明中会被当成window的属性,但是let不会;

  • 条件声明

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // let的作用域是块级的,所以不能使用typeof或者try...catch
    if(typeof name === 'undefined') {
    let name;
    }
    name = "xxx"; // 相当于全局声明

    try {
    console.log(age)
    }catch(error) {
    let age;
    }
    age = 10; // 相当于全局声明
  • 为什么在for循环迭代变量使用let

    因为在for循环中使用var会溢出到循环外部;但是let不会;

const关键字
  • const关键字和let关键字基本使用一样,但是声明const必须初始化变量,并且后续不能修改;

  • const声明作用域也是块级;不允许重复声明;

  • const声明的限制只是它指向的变量的引用,const引用一个对象,修改对象的内部属性是不违反限制的;

  • 语法:

    1
    const message = "xxx";

数据类型

  • 检验数据类型: typeof 操作符

    对一个值使用 typeof 会返回一个类型字符串

    • “undefined”表示未定义
    • “boolean”表示布偶值
    • “string”表示字符串
    • “number”表述数值
    • “object”表示为对象 或者 null (这是因为null会被当成一个空对象的引用)
    • “function”表示为函数
    • “symbol”表示为符号
    1
    2
    3
    4
    let message = "some thing"
    console.log(typeof message); // "string"
    // 虽然typeof 不需要参数,但是可以使用参数比如:
    console.log(typeof(message));
Undefined 类型
  • 只有一个值就是 undefined;

  • undefined 是一个假值(在布尔判断时候为假);

  • 当使用 var 或者 let 声明了变量但是没有初始化,变量的值就是 undefined; undefined可以赋值给变量;

    1
    2
    let message = undefined;
    console.log(message == undefined); // true
  • 对于未定义的变量 唯一可以执行的操作就是进行 typeof 操作:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    let message;

    // 未定义变量age

    // 对于没有声明的变量返回是 undefined
    console.log(typeof message); // "undefined"
    // 对于没有定义的变量返回 undefined
    // 但是不可以直接判断 age == undefined
    console.log(typeof age); // "undefined"
Null 类型
  • 只有一个值是 null;

  • null 是一个假值(在布尔判断时候为假);

  • null表示一个空对象的指针,所有使用 typeof 返回的是 “object”;

  • undefined 是 null 派生来的,所有使用等于操作符返回 true, 两者只是表面相等(==);

    1
    console.log(undefined == null); // true
Boolean 类型
  • 存在两个字面值: true 和 false;

  • 布尔值转化: Boolean();

    数据类型 转换成true的值 转换成false的值
    Boolean true False
    Number 非0 0,NaN
    String 非空字符串 “”(空字符串)
    Object 任意对象(包括 { } ) null
    undefined / undefined
Number类型
  • 整数可以是八进制也可以是十六进制:

    1
    2
    3
    4
    // 八进制必须以 0o 开头
    let num_1 = 0o70;
    // 十六进制必须以 0x 开头
    let num_2 = 0x1f;
  • 浮点数

    • 浮点数的存储空间是整数的两倍(所以ES中都是设法转换成整数保存)

    • 整数部分为0,可以省略整数部分 0.1  => .1

    • 科学计数法用一个整数或者浮点数 + 大写或者小写的e,再加上10的n次幂;3.12E8

    • 浮点数的值最高高达17位小数,但是并不够,例如:

      1
      2
      // 这里并不是JS的问题,而且使用了IEEE754数值就会出现该问题
      console.log(.1 + .2) // 0.30000000000000004
  • ES可以表示的最小数保存在: Number.MIN_VALUUE;可能包含-Infinty

  • ES可以表示的最大数保存在: Number.MAX_VALUUE;可能包含Infinty

  • 超过保存的最大数或者最小数会被转换成Infinity 或者 -Infinity;并且不能进行任何操作;

  • 检测一个数是不是无限大或者无限小的数: isFinite();返回 true 表示该数有限;

  • NaN(Not a Number)

    • 涉及到NaN的操作始终返回NaN
    • NaN不等于任何值,包括NaN
    • 通过isNaN()判断一个数是不是NaN;将值传给 isNaN(),该函数会尝试转换成数值;
  • 数值转换: Number(), parseInt(), parseFloat()

    • Number():是转型函数,可以用于任何数据类型:

      数据类型 转换后
      布尔值 true转换成1, false转换成0
      数值 直接返回
      null 0
      undefined NaN
      字符串 1.字符串包含数值字符,直接转换,包括加减号,忽略掉数值字符前面无效的0;
      2.字符中包含有效浮点数,直接转换相应的浮点数值,同样忽略掉前面无效的0;
      3.字符串包含有效的十六进制的数比如: “0xf”, 转换成十进制:
      4.空字符串转换成0;
      5.其他字符串转换成NaN;
      对象 想调用对象的 valueOf()方法,如果返回值为NaN,调用 toString()方法,按照字符串转换方式;
    • parseInt():主要用于字符串的转换;

      parseInt(number, type);第一个参数是转换的数值,第二个可选参数是需要转换的进制

      • 字符串最前面的空格会被省略;

      • 转换浮点数字符串只会保留整数部分

      • 如果第一个字符不是数值字符,加号,减号,直接返回NaN;

        1
        2
        let str = "1.23blue"
        parseInt(str); // 1.23 后续的字符串会被省略
      • 遇见空字符串直接返回NaN,与Number()不一样, 包括undefined和null也会转换成 NaN;

      • 当不传入第二个参数的时候,需要在八进制和十六进制前面加上 0o 或者 0x;传入了可以省略;

        1
        2
        3
        let num = parseInt("AX") // NaN
        let num2 = parseInt("AX", 16) // 175
        let num3 = parseInt("0xAX") // 175
    • parseFloat(): 也是主要用于字符串的转换;

      • 主要转换方式同parseInt();
      • 始终忽略掉字符串开头的0,所以解析十六进制数据只会返回0;
      • 只解析十进制数据;
String类型
  • 字符串可以使用单引号(’’),双引号(””), 反引号(`);

  • 可以表示零个或者多个Unicode字符;

  • 以某种引号开头,必须以某种引号结尾;

  • 字符串使用.length获取字符串长度;

  • 特点:

    • 字符串不可变,一旦创建值不可变,要修改某个字符串变量,就是先销毁原来的字符串,再重新把新值赋值;
  • toString():

    • toString();基本所有值可以用,但是null,undefined不可用;
    • 使用toString()转换数值时候,可以传入参数,数值表示转换字符串的进制数;默认是十进制;
  • String():

    • String();当我们不确定转换的值是不是null,undefined时候,可以使用该转换;
    • String()遵循一些规定:
      • 值存在toString();则使用该方法,不可以穿入参数;
      • null转换成”null”;
      • undefined转换成”undefined”;
  • 模板字面量(``):

    • 严格意义上讲模板字面量不是吧字符串,只是运行后得到字符串的JS语法表达式;

    • 模板字面量会保留换行,即我们可以使用模板字符串定义的模板:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      let temp = `
      Hello
      World
      `
      /*
      Hello
      World
      */
      console.log(temp)
      // true
      console.log(temp[0] === '\n')
  • 字符串插值:

    • 使用${}来使用插值;

    • 被插入的值强制使用toString();

      1
      2
      3
      4
      5
      6
      7
      let value =5;
      let message = 'send';

      // 方式1
      let str_value = message + (value * value);
      // 方式2
      let str_value_2 = `${message} ${value * value}`
  • 模板字面量标签函数(tag function)

    • 对于红宝书上的定义我自己解释如下

    • 标签函数也是正常函数,定义函数时候正常书写,使用时候写法不一样:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
       	var a = 1;
      var b = 2;
      function simpleTag(string, value_1, value_2, value_3) {
      console.log(string) // ["get ", " + ", " = ", "", raw: Array(4)]
      console.log(value_1) // 1
      console.log(value_2) // 2
      console.log(value_3) // 3
      }

      // 简写成
      function simpleTag(string, ...value) {
      console.log(string) // ["get ", " + ", " = ", "", raw: Array(4)]
      console.log(value) // [1, 2, 3]
      }

      let res = simpleTag`get ${a} + ${b} = ${a + b}`
    • 如上所示,标签函数,第一个参数,输出的是除去插值的模板,后续第二个参数开始是插值的数据,可以简写成数组;

    • 输出模版的时候,是以遇见插值前的字符串为一组,包括空格;

  • 原始字符串

    • 即我们在使用字符串会遇见转译字符,我们可以使用 String.row,保留原本的转义字符;

      1
      2
      3
      4
      5
      6
      console.log(`\u00A9`); // ©
      console.log(String.raw`\uooA9`); // \u00A9

      // 换行符
      // 但是只能控制显式不能转换实际回车的换行符
      console.log(Stirng.raw`adc \n edf`) // adc \n edf
Symbol类型

新增一个符号为原始值,并且符号是不可变的,唯一的;

符号的用途是确保对象属性使用了唯一的标识符,确保属性冲突的危险;

  • 符号的基本用法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    let a = Symbol();
    let b = Symbol();
    console.log(a); // Symbol()
    console.log(typeof a); // symbol
    console.log(a === b); // false
    console.log(a == b); // false

    // 我们可以在Symbol中传入一个对符号的描述
    let c = Symbol("Hello");
    // c.description访问到描述
    console.log(c.description); // Hello
  • 使用全局符号注册表:

    • 由于之前每次新建Symbol;都是不一样的,当我们需要共享或者重用符号实例的时候,需要在全局符号注册表中实现:

      1
      2
      let a = Symbol.for("Hello");
      console.log(typeof a); // symbol

      Symbol.for(),当我们使用某一个字符调用的时候,它会检查全局运行的注册表,发现不存的会新建一个到全局注册表中;

      如果发现相同的,那么就会返回该符号实例;

      1
      2
      3
      4
      5
      6
      7
      let a = Symbol.for("Hello");
      let b = Symbol.for("Hello");
      console.log(a === b); // true

      let a = Symbol("Hello");
      let b = Symbol.for("Hello");
      console.log(a === b); // false
    • 查询全局注册表:

      1
      2
      let a = Symbol.for("Hello");
      console.log(Symbol.keyFor(a); // Hello

      Symbol.keyFor();接受一个符号参数,返回符号的描述内容;

      如果不是符号会抛出异常:TypeError;

      该查询描述内容只能使用于Symbol.for();创建的Symbol实例;如果是普通的Symbol("name")会报错为undefined;

  • 未完待续….

Object类型
  • 新建一个对象:

    1
    2
    3
    let a = new Object();

    let b = {};
  • 属性方法:

    • 注意:严格来说ECMA-262中的对象和JS的对象不一定一样,比如DOM和BOM,都是宿主环境定义和提供的对象,所以他们可能不会继承Object(可能不会继承以下属性);
    • constructor:用于创建当前对象的函数;
    • hasOwnProperty(propertyName):判断当前对象实例上是否存在给定的属性,属性必须是字符串;
    • isPrototyprof():判断当前对象是否是另一个对象的原型;
    • propertyIsEnumerable(propertyName):判断给定的属性是否可以使用for-in枚举;
    • toLocaleString();返回对象的字符串表示;
    • toString();返回对象的字符串表示;
    • valueOf();返回对象的对应的字符串,数值或者是字符串;

操作符

一元操作符
递增/递减操作符:
1
2
3
4
5
6
7
8
9
10
// 这两个操作符一般存在前缀版和后缀版的
// 前缀版的变量般会在求值之前改变
let age = 29;
++age;
console.log(age); // 30;
console.log(--age + 2); // 29 + 2 = 31;

// 后缀版主要是在后缀的变量会在语句求值以后才会发生变化
let b = 30;
age++;
  • 该操作符可以作用任何值,包括字符串,布尔值,数值,浮点数,对象;

    字符串:有效数值变成数值应用(加1或者减1),无效数值变成NaN;

    布尔值:true为1再应用(加1或者减1), false为0再应用(加1或者减1);

    浮点数:加1或者减1;

    对象:调用valueOf(),如果结果是NaN,则调用toString(),再次变化;

一元加和减:
  • 将 一元加 应用到非数值,则会执行与使用 Number()转型函数一样的类型转换:

  • 布尔值 false和 true 转换为 0 和 1;

  • 字符串根据特殊规则进行解析;

  • 对象会调用它们的 valueOf()或 toString()方法以得到可以转换的值。

  • 对数值使用 一元减 会将其变成相应的负值(如上面的例子所示)。在应用到非数值时,一元减会遵

    循与一元加同样的规则,先对它们进行转换,然后再取负值;

位操作符:
  • ECMAScript中的所有数值都以 IEEE 754 64 位格式存储;

  • 位操作并不直接应用到 64 位表示,而是先把值转换为32 位整数,再进行位操作,之后再把结果转换为 64 位;

    1. 有符号整数使用 32 位的前 31 位表示整数值。第 32 位表示数值的符号,如 0 表示正,1 表示负。这

    一位称为符号位(sign bit),它的值决定了数值其余部分的格式。

    1. 一个数的负数(补码, 二补数)是以二进制编码存储;
    • 确定绝对值的二进制表示(如,对于-18,先确定 18 的二进制表示);
    • 找到数值的一补数(或反码),换句话说,就是每个 0 都变成 1,每个 1 都变成 0;
    • 给结果加 1。
按位非:
  • 按位非 操作符用波浪符(~)表示,它的作用是返回数值的一补数(每位取反)。

    1
    2
    3
    4
    // 在位上进行操作数度较快
    let num1 = 25; // 二进制 00000000000000000000000000011001
    let num2 = ~num1; // 二进制 11111111111111111111111111100110
    console.log(num2); // -26
按位与:
  • 按位与 操作符用和号(&)表示,有两个操作数。本质上,按位与就是将两个数的每一个位按照0对齐;

  • 可以适当将 1 看作 true;

    第一位 第二位 结果
    1 1 1
    1 0 0
    0 1 0
    0 0 0
    1
    2
    let a = 25 & 3;
    console.log(a); // 1
按位或:
  • 按位或操作符用管道符(|)表示,同样有两个操作数。按位或遵循如下真值表:
  • 可以适当将 1 看作 true;
    第一位 第二位 结果
    1 1 1
    1 0 1
    0 1 1
    0 0 0
1
2
let result = 25 | 3; 
console.log(result); // 27
按位异或:
  • 按位异或用脱字符(^)表示,同样有两个操作数。下面是按位异或的真值表:
  • 按位异或与按位或的区别是,它只在一位上是 1 的时候返回 1(两位都是 1 或 0,则返回 0)
    第一位 第二位 结果
    1 1 0
    1 0 1
    0 1 1
    0 0 0
1
2
let result = 25 ^ 3; 
console.log(result); // 26
左移:
  • 左移操作符用两个小于号(<<)表示,会按照指定的位数将数值的所有位向左移动,左移会以 0 填充这些空位;

  • 左移会保留它所操作数值的符号;

    1
    2
    let oldValue = 2; // 10
    let res = oldValue << 5; // 1000000,十进制的64
有符号右移:
  • 有符号右移由 2 个大于号(>>)表示,会将数值的所有 32 位都向右移,同时保留符号(正或负)。

  • 同样,移位后就会出现空位。不过,右移后空位会出现在左侧,且在符号位之后;

  • ECMAScript 会用符号位的值来填充这些空位,以得到完整的数值。

    1
    2
    let oldValue = 64; // 等于二进制 1000000 
    let newValue = oldValue >> 5; // 等于二进制 10,即十进制 2
无符号右移:
  • 无符号右移用 3 个大于号表示(>>>),会将数值的所有 32 位都向右移。
  • 对于正数,无符号右移与有符号右移结果相同。
  • 对于负数,与有符号右移不同,无符号右移会给空位补 0,而不管符号位是什么。有符号右移空位的数值是根据符号的数值补充的;
布尔操作符:
逻辑非:
  • 逻辑非操作符由一个叹号(!)表示,可应用给 ECMAScript 中的任何值。

    类型 使用逻辑非
    数值 0返回true,非0返回false
    对象 返回false
    字符串 空字符串返回true, 其他非空返回false
    Null true
    Undefined true
    NaN true
  • 同时使用!!表示了调用了转型函数Boolean()

1
2
3
console.log(!!0); // false
console.log(!!"Hello"); // true
consoel.log(!!""); // false
逻辑与:
  • 逻辑与操作符由两个和号(&&)表示

    第一位 第二位 结果
    true true true
    true false false
    false true false
    false false false
  • 逻辑与操作符是一种短路操作符,意思就是如果第一个操作数决定了结果,那么永远不会对第二个操作数求值;即使第二个参数未声明定义;

  • ⚠️逻辑与可以适用于任何类型的数据:

    1. 如果第一个操作数是对象,则返回第二个操作数。
    2. 如果第二个操作数是对象,则只有第一个操作数求值为 true 才会返回该对象。
    3. 如果两个操作数都是对象,则返回第二个操作数。
    4. 如果有一个操作数是 null,则返回 null;
    5. 如果有一个操作数是 NaN,则返回 NaN;
    6. 如果有一个操作数是 undefined,则返回 undefined;
逻辑或:
  • 逻辑或操作符由两个管道符(||)表示

    第一位 第二位 结果
    true true true
    true false true
    false true true
    false false false
  • 同样与逻辑与类似,逻辑或操作符也具有短路的特性。只不过对逻辑或而言,第一个操数求值为true,第二个操作数就不会再被求值了。

  • ⚠️逻辑或可以适用于任何类型的数据:

    1. 如果第一个操作数是对象,则返回第一个操作数。
    2. 如果第一个操作数求值为 false,则返回第二个操作数。
    3. 如果两个操作数都是对象,则返回第一个操作数。
    4. 如果两个操作数都是 null,则返回 null;
    5. 如果两个操作数都是 NaN,则返回 NaN;
    6. 如果两个操作数都是 undefined,则返回 undefined;
乘性操作符:
乘法操作符
  • 乘法操作符由一个星号(*)表示,可以用于计算两个数值的乘积;

    1
    let a = 21 * 34;
  • 如果操作数都是数值,则执行常规的乘法运算;如果 ECMAScript 不能表示乘积,则返回 Infinity 或-Infinity。

  • 如果有任一操作数是 NaN,则返回 NaN;

  • 如果是 Infinity 乘以 0,则返回 NaN。

  • 如果是 Infinity 乘以非 0的有限数值,则根据第二个操作数的符号返回 Infinity 或-Infinity。

  • 如果有不是数值的操作数,则先在后台用 Number()将其转换为数值,然后再应用上述规则。

除法操作符:
  • 除法操作符由一个斜杠(/)表示,用于计算第一个操作数除以第二个操作数的商:

    1
    let res = 66 / 11;
  • 如果操作数都是数值,则执行常规的除法运算,即两个正值相除是正值,两个负值相除也是正值,符号不同的值相除得到负值。如果ECMAScript不能表示商,则返回Infinity或-Infinity。

  • 如果有任一操作数是 NaN,则返回 NaN。

  • 如果是 Infinity 除以 Infinity,则返回 NaN。

  • 如果是 0 除以 0,则返回 NaN。

  • 如果是非 0 的有限值除以 0,则根据第一个操作数的符号返回 Infinity 或-Infinity。

  • 如果是 Infinity 除以任何数值,则根据第二个操作数的符号返回 Infinity 或-Infinity。

  • 如果有不是数值的操作数,则先在后台用 Number()函数将其转换为数值,然后再应用上述规则;

取模操作符:
  • 取模(余数)操作符由一个百分比符号(%)表示;

    1
    let res = 26 % 5;
  • 如果操作数是数值,则执行常规除法运算,返回余数。

  • 如果被除数是无限值,除数是有限值,则返回 NaN。

  • 如果被除数是有限值,除数是无限值,则返回被除数。

  • 如果被除数是有限值,除数是 0,则返回 NaN。

  • 如果是 Infinity 除以 Infinity,则返回 NaN。

  • 如果被除数是 0,除数不是 0,则返回 0。

  • 如果有不是数值的操作数,则先在后台用 Number()函数将其转换为数值,然后再应用上述规则。

指数操作符:

ECMAScript 7 新增了指数操作符,Math.pow()现在有了自己的操作符**;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Math.pow(3, 2); // 9
3 ** 2; // 9

Math.pow(16, 0.5); // 4
16 ** 0.5; // 4

// 除此之外,指数操作符还可直接赋值:

let a = 3;
a **= 2; // a = a ** 2;
console.log(a); // 9

let b = 16;
b **= 0.5; // b = b ** 0.5;
加性操作符:
加法操作符:

加法操作符(+)用于求两个数的和:

1
let a = 1 + 2;
  1. 如果有任一操作数是 NaN,则返回 NaN;

  2. 如果是 Infinity 加 Infinity,则返回 Infinity;

  3. 如果是-Infinity 加-Infinity,则返回-Infinity;

  4. 如果是 Infinity 加-Infinity,则返回 NaN;

  5. 如果是+0 加+0,则返回+0;

  6. 如果是-0 加+0,则返回+0;

  7. 如果是-0 加-0,则返回-0。

    如果有一个操作数是字符串,则要应用如下规则:

    • 如果两个操作数都是字符串,则将第二个字符串拼接到第一个字符串后面;
    • 如果只有一个操作数是字符串,则将另一个操作数转换为字符串,再将两个字符串拼接在一起。
    • 如果有任一操作数是对象、数值或布尔值,则调用它们的 toString()方法以获取字符串;
    • 对于 undefined 和 null,则调用 String()函数;分别获取”undefined”和”null”
减法操作符:

减法操作符(-)也是使用很频繁的一种操作符:

1
let b = 3 - 1;
  1. 如果两个操作数都是数值,则执行数学减法运算并返回结果。
  2. 如果有任一操作数是 NaN,则返回 NaN。
  3. 如果是 Infinity 减 Infinity,则返回 NaN。
  4. 如果是-Infinity 减-Infinity,则返回 NaN。
  5. 如果是 Infinity 减-Infinity,则返回 Infinity。
  6. 如果是-Infinity 减 Infinity,则返回-Infinity。
  7. 如果是+0 减+0,则返回+0。
  8. 如果是+0 减-0,则返回-0。
  9. 如果是-0 减-0,则返回+0;
  10. 如果有任一操作数是字符串、布尔值、null 或 undefined,则先在后台使用 Number()将其转换为数值,然后再根据前面的规则执行数学运算。如果转换结果是 NaN,则减法计算的结果是NaN。
  11. 如果有任一操作数是对象,则调用其 valueOf()方法取得表示它的数值。如果该值是 NaN,则减法计算的结果是 NaN。如果对象没有 valueOf()方法,则调用其 toString()方法,然后再将得到的字符串转换为数值。
关系操作符:

关系操作符执行比较两个值的操作,包括小于(<)、大于(>)、小于等于(<=)和大于等于(>=);

  1. 如果操作数都是数值,则执行数值比较
  2. 如果操作数都是字符串,则逐个比较字符串中对应字符的编码;注意这里并不会转换成数字而是直接比较字符串编码的大小;
  3. 如果有任一操作数是数值,则将另一个操作数转换为数值,执行数值比较;
  4. 如果有任一操作数是对象,则调用其 valueOf()方法,取得结果后再根据前面的规则执行比较。如果没有 valueOf()操作符,则调用 toString()方法,取得结果后再根据前面的规则执行比较。
  5. 如果有任一操作数是布尔值,则将其转换为数值再执行比较;
  6. 任何值与NaN比较都会返回false; "a" < 3, 该操作也会返回false, 因为 “a”会转换成数值时候返回NaN;
相等操作符:
  • 等于和不等于:

    ECMAScript 中的等于操作符用两个等于号(==)表示,如果操作数相等,则会返回 true。

    不等于操作符用叹号和等于号(!=)表示,如果两个操作数不相等,则会返回 true。

    该操作符是转换成数值;

    • 如果任一操作数是布尔值,则将其转换为数值再比较是否相等。false 转换为 0,true 转换为 1。
    • 如果一个操作数是字符串,另一个操作数是数值,则尝试将字符串转换为数值,再比较是否相等。
    • 如果一个操作数是对象,另一个操作数不是,则调用对象的 valueOf()方法取得其原始值,再根据前面的规则进行比较。
    • null 和 undefined 相等。
    • null 和 undefined 不能转换为其他类型的值再进行比较;
    • 如果有任一操作数是 NaN,则相等操作符返回 false,不相等操作符返回 true。记住:即使两个操作数都是 NaN,相等操作符也返回 false,因为按照规则,NaN 不等于 NaN。
    • 如果两个操作数都是对象,则比较它们是不是同一个对象。如果两个操作数都指向同一个对象,则相等操作符返回 true。否则,两者不相等。
  • 全等和全不等

    全等和不全等操作符与相等和不相等操作符类似,只不过它们在比较相等时不转换操作数。

    全等操作符由 3 个等于号(===)表示,只有两个操作数在不转换的前提下相等才返回 true;即需也要判断两者数据类型是否相等;

    不全等操作符用一个叹号和两个等于号(!==)表示,只有两个操作数在不转换的前提下不相等才返回 true。

    null === undefined 返回false;

条件操作符:
1
2
3
// 条件表达式 boolean_expression 的值决定将哪个值赋给变量 variable
// 如果 boolean_expression 是 true ,则赋值 true_value ;如果boolean_expression 是 false,则赋值 false_value。
variable = boolean_expression ? true_value : false_value;
赋值操作符:

简单赋值用等于号(=)表示,将右手边的值赋给左手边的变量;

复合赋值使用乘性、加性或位操作符后跟等于号(=)表示。

以上基本操作符都可以复合操作;

1
2
3
4
5
let num = 10;
num = num + 10;

// 复合赋值
num += 10;
逗号操作符:

逗号操作符可以用来在一条语句中执行多个操作;

1
2
3
let num1 = 1, num2 = 2, num3 = 3;

let num = (5, 1, 4, 8, 0); // num的值是0, 以最后一个为准;

语句

if语句
1
2
3
// condition可以是任何表达式,不一定是布尔值,ECMAScript 会自动调用 Boolean()函数将这个表达式的值转换为布尔值。
// 如果条件求值为 true,则执行语句statement1;如果条件求值为 false,则执行语句 statement2。
if (condition) statement1 else statement2
1
2
// 多个if使用
if (condition1) statement1 else if (condition2) statement2 else statement3
do-while语句

do-while 语句是一种后测试循环语句,即循环体中的代码执行后才会对退出条件进行求值;

换句话说,循环体内的代码至少执行一次。

1
2
3
do { 
statement
} while (expression);
while语句

while 语句是一种先测试循环语句,即先检测退出条件,再执行循环体内的代码。

因此,while 循环体内的代码有可能不会执行。

1
2
3
4
5
6
7
while(expression) statement

// 例子
let i = 0;
while (i < 10) {
i += 2;
}
for语句

for 语句也是先测试语句,只不过增加了进入循环之前的初始化代码,以及循环执行后要执行的表达式;

1
for (initialization; expression; post-loop-expression) statement

根据之前定义的变量关键字,最好在for循环中使用let关键字定义变量,这样就可以将这个变量的作用域限定在循环中。

初始化、条件表达式和循环后表达式都不是必需的;

1
2
3
4
5
6
7
8
// 无穷循环
for(;;) { doSomething(); }
// 只包含了循环条件的for循环,相当于while循环
let i = 0;
for (; i < 10; ) {
console.log(i);
i++;
}
for-in语句

for-in 语句是一种严格的迭代语句,用于枚举对象中的非符号键属性

ECMAScript 中对象的属性是无序的,因此 for-in 语句不能保证返回对象属性的顺序;

如果 for-in 循环要迭代的变量是 null 或 undefined,则不执行循环体;

1
2
3
4
5
6
for (property in expression) statement

// 举例,该循环遍历出windows所有的属性名称
for (const propName in window) {
document.write(propName);
}
for-of 语句

for-of 语句是一种严格的迭代语句,用于遍历可迭代对象的元素 ;

如果尝试迭代的变量不支持迭代,则 for-of 语句会抛出错误。

1
2
// 该方法返回值,但是不可迭代对象
for (property of expression) statement
标签语句

标签语句用于给语句加标签,语法如下:

标签语句的典型应用场景是嵌套循环。

1
2
3
4
5
label: statement;
// 举例
start: for (let i = 0; i < count; i++) {
console.log(i);
}
break和continue语句

break 语句用于立即退出循环,强制执行循环后的下一条语句;

而 continue 语句也用于立即退出循环,但会再次从循环顶部开始执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// break
let num = 0;
for (let i = 1; i < 10; i++) {
if (i % 5 == 0) {
break;
}
num++;
}
console.log(num); // 4


// continue
let num = 0;
for (let i = 1; i < 10; i++) {
if (i % 5 == 0) {
continue;
}
num++;
}
console.log(num); // 8

break 和 continue 都可以与标签语句一起使用,返回代码中特定的位置;

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
// break
// 标签语句一般用来嵌套循环跳出,因为一般的break只能跳出当层循环,不能跳出最外层的循环
let num = 0;
outermost:
for (let i = 0; i < 10; i++) {
for (let j = 0; j < 10; j++) {
if (i == 5 && j == 5) {
break outermost;
}
num++;
}
}
console.log(num); // 55


// 例子2
let num_2 = 0;
for (let i = 0; i < 10; i++) {
for (let j = 0; j < 10; j++) {
if (i == 5 && j == 5) {
// 该条件成立时候,跳过了内层循环剩下的五次,5, 6, 7, 8, 9
break;
}
num_2++;
}
}
console.log(num_2); // 95

1
2
3
4
5
6
7
8
9
10
11
12
13
// continue
let num = 0;
outermost:
for (let i = 0; i < 10; i++) {
for (let j = 0; j < 10; j++) {
if (i == 5 && j == 5) {
// 该条件成立时候,跳过了内层循环剩下的五次,5, 6, 7, 8, 9,但是从外层循环继续
continue outermost;
}
num++;
}
}
console.log(num); // 95
with语句

使用 with 语句的主要场景是针对一个对象反复操作;

严格模式不允许使用 with 语句,否则会抛出错误;

1
with (expression) statement;
1
2
3
4
5
6
7
8
9
let qs = location.search.substring(1); 
let hostName = location.hostname;
let url = location.href;
// 上述都使用到了location对象,可以使用with对象简化
with(location) {
let qs = search.substring(1);
let hostName = hostname;
let url = href;
}
switch语句

这里的每个 case(条件/分支)相当于:“如果表达式等于后面的值,则执行下面的语句。”

break关键字会导致代码执行跳出 switch 语句。

如果没有 break,则代码会继续匹配下一个条件;

default关键字用于在任何条件都没有满足时指定默认执行的语句(相当于 else 语句)。

switch 语句可以用于所有数据类型(在很多语言中,它只能用于数值),因此可以使用字符串甚至对象。

其次,条件的值不需要是常量,也可以是变量或表达式。看下面的例子:

  • ⚠️:switch 语句在比较每个条件的值时会使用全等操作符,因此不会强制转换数据类型(”10” !== 10)
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
switch (expression) { 
case value1:
statement
break;
case value2:
statement
break;
case value3:
statement
break;
case value4:
statement
break;
default:
statement
}

// 例子
let num = 25;
switch (true) {
case num < 0:
console.log("Less than 0.");
break;
case num >= 0 && num <= 10:
console.log("Between 0 and 10.");
break;
case num > 10 && num <= 20:
console.log("Between 10 and 20.");
break;
default:
console.log("More than 20.");
}

JS语言基础
http://www.clearluv.com/2020/11/07/语言基础/
发布于
2020年11月7日
许可协议