《JavaScript高级程序设计(第四版)》读书笔记(二) | 七日打卡

语言: CN / TW / HK

第 03 章 语言基础

1、语法

标识符第一个字符必须是一个字母、下划线(_)或美元符号($),剩下的其他字符可以是字母、下划线、美元符号或数字。标识符中的字母可以是扩展 ASCII(Extended ASCII)中的字母,也可以是 Unicode 的字母字符,如 À 和 Æ(但不推荐使用)。

按照惯例,ECMAScript 标识符使用驼峰大小写形式。

ECMAScript 5 增加了严格模式(strict mode)的概念。严格模式是一种不同的 JavaScript 解析和执行模型,ECMAScript 3 的一些不规范写法在这种模式下会被处理,对于不安全的活动将抛出错误。要对整个脚本启用严格模式,在脚本开头加上这一行:

 "use strict";
复制代码

虽然看起来像个没有赋值给任何变量的字符串,但它其实是一个预处理指令。

3、变量

使用 var 操作符定义的变量会成为包含它的函数的局部变量。使用 var 在一个函数内部定义一个变量,就意味着该变量将在函数退出时被销毁。 在函数内定义变量时省略 var 操作符,可以创建一个全局变量

提升(hoist): 使用 var 关键字声明的变量会自动提升到函数作用域顶部。

let 声明的范围是块作用域。let 也不允许同一个块作用域中出现冗余声明。

暂时性死区,let 声明的变量不会在作用域中被提升,JavaScript 引擎也会注意出现在块后面的 let 声明,只不过在此之前不能以任何方 式来引用未声明的变量。在 let 声明之前的执行瞬间被称为“暂时性死区”(temporal dead zone),在此阶段引用任何后面才声明的变量都会抛出 ReferenceError。

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

在使用 var 声明变量时,由于声明会被提升,JavaScript 引擎会自动将多余的声明在作用域顶部合并为一个声明。因为 let 的作用域是块,所以不可能检查前面是否已经使用 let 声明过同名变量,同时也就不可能在没有声明的情况下声明它。

const 声明变量时必须同时初始化变量,且尝试修改 const 声明的变量会导致运行时错误。如果 const 变量引用的是一个对象, 那么修改这个对象内部的属性并不违反 const 的限制。

4、数据类型

Object 是一种无序名值对的集合。

typeof操作符

  • "undefined"表示值未定义;
  • "boolean"表示值为布尔值;
  • "string"表示值为字符串;
  • "number"表示值为数值;
  • "object"表示值为对象(而不是函数)或 null;
  • "function"表示值为函数;
  • "symbol"表示值为符号。

Number类型

对于八进制字面量, 第一个数字必须是零(0),然后是相应的八进制数字(数值 0~7)。如果字面量中包含的数字超出了应 有的范围,就会忽略前缀的零,后面的数字序列会被当成十进制数,ECMAScript 2015 或 ES6 中的八进制值通过前缀 0o 来表示。

要创建十六进制字面量,必须让真正的数值前缀 0x(区分大小写),然后是十六进制数字(0~9 以及 A~F)。十六进制数字中的字母大小写均可。

要定义浮点值,数值中必须包含小数点,而且小数点后面必须至少有一个数字。因为存储浮点值使用的内存空间是存储整数值的两倍,所以 ECMAScript 总是想方设法把值转换为整数。在小数点后面没有数字的情况下,数值就会变成整数。 类似地,如果数值本身就是整数,只是小数点后面跟着 0(如 1.0),那它也会被转换为整数。

之所以存在这种舍入错误,是因为使用了IEEE754数值,这种错误并非ECMAScript 所独有。其他使用相同格式的语言也有这个问题。

可表示最小值 Number.MIN_VALUE 5e^-324 最大值Number.MAX_VALUE 超出范围会被转为Infinity/-Infinity

isFinite() 函数确定一个值是不是有限大

有一个特殊的数值叫 NaN,意思是“不是数值” (Not a Number),任何涉及 NaN 的操作始终返回 NaN,NaN 不等于包括 NaN 在内的任何值。

parseFloat() 函数的工作方式跟 parseInt() 函数类似,都是从位置 0 开始检测每个字符。同样, 它也是解析到字符串末尾或者解析到一个无效的浮点数值字符为止。

String类型

字符串可以使用双引号(")、 单引号(')或反引号(`)标示

字符串数据类型包含一些字符字面量,用于表示非打印字符或有其他用途的字符 \n \b 这些字符字面量可以出现在字符串中的任意位置,且可以作为单个字符被解释

ECMAScript 中的字符串是不可变的(immutable)

使用 String() 转型函数,它始终会返回表 示相应类型值的字符串。用加号操作符给一个值加上一个空字符串""也可以将其转换为字符串

  • 如果值有 toString() 方法,则调用该方法(不传参数)并返回结果。
  • 如果值是 null,返回 "null"
  • 如果值是 undefined,返回 "undefined"

字符串插值通过在 ${} 中使用一个 JavaScript 表达式实现,所有插入的值都会使用 toString() 强制转型为字符串。

模板字面量也支持定义标签函数(tag function),而通过标签函数可以自定义插值行为。标签函数会接收**值记号分隔后的模板和对每个表达式求值的结果。

let a = 6;
let b = 9;

function simpleTag(strings, aValExpression, bValExpression, sumExpression) {
    console.log(strings);       // ["", " + ", " = ", ""]
    console.log(aValExpression);// 6
    console.log(bValExpression);// 9
    console.log(sumExpression); // 15
    return 'foobar';
}

let untaggedResult = `${ a } + ${ b } = ${ a + b }`;
let taggedResult = simpleTag`${ a } + ${ b } = ${ a + b }`;

console.log(untaggedResult); // "6 + 9 = 15"
console.log(taggedResult);   // "foobar"
复制代码

剩余操作符(rest operator)

let a = 6;
let b = 9;

function simpleTag(strings, ...expressions) {

  console.log(strings);
  for(const expression of expressions) {

    console.log(expression);
  }

  return 'foobar';
}

let taggedResult = simpleTag`${ a } + ${ b } = ${ a + b }`;
// ["", " + ", " = ", ""]
// 6
// 9

// 15
console.log(taggedResult);  // "foobar"
复制代码

原始字符串,使用模板字面量也可以直接获取原始的模板字面量内容(如换行符或 Unicode 字符),而不是被转换后的字符表示。为此,可以使用默认的 String.raw 标签函数

console.log(`\u00A9`); // ©
console.log(String.raw`\u00A9`); // \u00A9
复制代码

Symbol类型

Symbol(符号)是 ECMAScript 6 新增的数据类型。符号是原始值,且符号实例是唯一、不可变的。 符号的用途是确保对象属性使用唯一标识符,不会发生属性冲突的危险。

调用 Symbol()函数时,也可以传入一个字符串参数作为对符号的描述(description),将来可以通过这个字符串来调试代码。但是,这个字符串参数与符号定义或标识完全无关

let genericSymbol = Symbol();
let otherGenericSymbol = Symbol();

let fooSymbol = Symbol('foo');
let otherFooSymbol = Symbol('foo');

console.log(genericonsole.log(fooSymbol == otherFooSymbol); // false
console.log(fooSymbol == otherFooSymbol); // false
复制代码

Symbol() 函数不能与 new 关键字一起作为构造函数使用。

使用全局符号注册表

如果运行时的不同部分需要共享和重用符号实例,那么可以用一个字符串作为键,在全局符号注册 表中创建并重用符号。

Symbol.for() 对每个字符串键都执行幂等操作。第一次使用某个字符串调用时,它会检查全局运 行时注册表,发现不存在对应的符号,于是就会生成一个新符号实例并添加到注册表中。后续使用相同 字符串的调用同样会检查注册表,发现存在与该字符串对应的符号,然后就会返回该符号实例。 使用 Symbol.keyFor() 来查询全局注册表

let fooGlobalSymbol = Symbol.for('foo'); // 创建新符号
let otherFooGlobalSymbol = Symbol.for('foo'); // 重用已有符号
console.log(fooGlobalSymbol === otherFooGlobalSymbol); // true

console.log(Symbol.keyFor(fooGlobalSymbol)); // foo
复制代码

凡是可以使用字符串或数值作为属性的地方,都可以使用符号。

let s1 = Symbol('foo');

let o = {
      [s1]: 'foo val'
};

console.log(o) // {Symbol(foo): "foo val"}
复制代码

Object类型

每个 Object 实例都有如下属性和方法。

  • constructor
  • hasOwnProperty(propertyName)
  • isPrototypeOf(object) 用于判断当前对象是否为另一个对象的原型
  • propertyIsEnumerable(propertyName) 用于判断给定的属性是否可以使用 for-in 语句枚举。与 hasOwnProperty()一样,属性名必须是字符串。
  • toLocaleString():返回对象的字符串表示,该字符串反映对象所在的本地化执行环境。
  • toString():返回对象的字符串表示。
  • valueOf():返回对象对应的字符串、数值或布尔值表示。通常与 toString()的返回值相同。

操作符

二补数(或补码) = 反码+1

无符号右移(>>>

特殊值 NaN 和 Infinity 在位操作中都会被当成 0 处理。

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

在转换操作数的类型时,相等和不相等操作符遵循如下规则

  • 如果任一操作数是布尔值,则将其转换为数值再比较是否相等。false 转换为 0,true 转换为 1。
  • 如果一个操作数是字符串,另一个操作数是数值,则尝试将字符串转换为数值,再比较是否相等。
  • 如果一个操作数是对象,另一个操作数不是,则调用对象的 valueOf()方法取得其原始值,再根据前面的规则进行比较。

语句

for-in 语句是一种严格的迭代语句,用于枚举对象中的非符号键属性,ECMAScript 中对象的属性是无序的,因此 for-in 语句不能保证返回对象属性的顺序。返回的顺序可能会因浏览器而异。

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

标签语句

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

label: statement
复制代码

举例

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;
        }
    }
}
console.log(num); // 55
复制代码

with语句

with 语句的用途是将代码作用域设置为特定的对象

with (expression) statement;
复制代码