2024-11-25 日报 Day17

2024-11-25 日报 Day17

Yuyang 前端小白🥬

今日的鸡汤

把行动交给现在,把结果交给时间。

今日学习内容

1、JS红皮书P38-47 第三章:语言基础

今日笔记

1、String类型: 表示零或多个 16 位 Unicode 字符序列。字符串可以使用双引号(”)、单引号(’)或反引号(`)标示,因此下面的代码都是合法的:

1
2
3
let firstName = "Nicholas";
let lastName = 'Zakas';
let message = `Hello, ${firstName}!`;
  • 字符字面量: 如下表所示:
    字面量 含义
    \n 换行
    \t 制表
    \b 退格
    \r 回车
    \f 换页
    \ 反斜杠
    ' 单引号
    " 双引号
    ` 反引号
    \xnn 以十六进制代码nn表示的一个字符
    \unnnn 以十六进制代码nnnn表示的一个 Unicode 字符
    2、字符串的特点:变量中的字符串值,必须先销毁原始的字符串,然后将包含新值的另一个字符串保存到该变量,如下所示:
    let lang = “Java”;
    lang = lang + “Script”;
    这里,变量 lang 一开始包含字符串”Java”。紧接着,lang 被重新定义为包含”Java”和”Script”的组合,也就是”JavaScript”。整个过程首先会分配一个足够容纳 10 个字符的空间,然后填充上”Java”和”Script”。最后销毁原始的字符串”Java”和字符串”Script”,因为这两个字符串都没有用了。所有处理都是在后台发生的,而这也是一些早期的浏览器(如 Firefox 1.0 之前的版本和 IE6.0)在拼接字符串时非常慢的原因。这些浏览器在后来的版本中都有针对性地解决了这个问题
    3、转换为字符串:有三种方法可以把值转换为字符串:toString()、String()和模板字面量。
  • toString()方法: 该方法是所有引用类型中都具有的方法,其基本思想是返回一个表示该对象的字符串。
    1
    2
    3
    4
    let age = 11;
    let ageAsString = age.toString(); // 字符串"11"
    let found = true;
    let foundAsString = found.toString(); // 字符串"true"
    null和undefined值没有toString()方法,如果调用这两个值的这个方法会导致错误。如果不确定一个值是不是null或undefined,可以使用String()转型函数。String()函数遵循如下规则:
  • 如果值有toString()方法,则调用该方法(没有参数)并返回结果。
  • 如果值是null,则返回”null”。
  • 如果值是undefined,则返回”undefined”。
    1
    2
    3
    4
    5
    6
    7
    8
    let value1 = 10;
    let value2 = true;
    let value3 = null;
    let value4;
    console.log(String(value1)); // "10"
    console.log(String(value2)); // "true"
    console.log(String(value3)); // "null"
    console.log(String(value4)); // "undefined"
    注意 用加号操作符给一个值加上一个空字符串””也可以将其转换为字符串
    3、模版字面量:ECMAScript 6 新增了使用模板字面量定义字符串的能力。与使用单引号或双引号不同,模板字面量保留换行字符,可以跨行定义字符串:
    顾名思义,模板字面量在定义模板时特别有用,比如下面这个 HTML 模板:
    let pageHTML = ` `; 由于模板字面量会保持反引号内部的空格,因此在使用时要格外注意。格式正确的模板字符串看起来可能会缩进不当: 4、字符串插值:字符串插值通过在${}中使用一个 JavaScript 表达式实现: `${ value } to the ${ exponent } power is ${ value * value }` 所有插入的值都会使用 toString()强制转型为字符串,而且任何 JavaScript 表达式都可以用于插值。嵌套的模板字符串无须转义: 将表达式转换为字符串时会调用 toString(): let foo = { toString: () => 'World' }; console.log(`Hello, ${ foo }!`); // Hello, World! 5、模版字面量标签函数:标签函数可以自定义插值行为。标签函数 会接收被插值记号分隔后的模板和对每个表达式求值的结果。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    let a = 6;
    let b = 9;

    function simpleTag(strings, aValExpression, bValExpression, sumExpression) {
    console.log(strings); // ["The sum of ", " + ", " = ", ""]
    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)将它们收集到一个数组中:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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"
    6、原始字符串:使用模板字面量也可以直接获取原始的模板字面量内容(如换行符或 Unicode 字符),而不是被转换后的字符表示。为此,可以使用默认的 String.raw 标签函数:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // unicode示例
    console.log(`\u00A9`); // ©
    console.log(String.raw`\u00A9`); // \u00A9
    // 换行符示例
    console.log(String.raw`Hello\nWorld`); // "Hello\nWorld"
    function printRaw(strings) {
    console.log('Actual characters:');
    for (const string of strings) {
    console.log(string);
    }
    console.log('Escaped characters;');
    for (const rawString of strings.raw) {
    console.log(rawString);
    }
    }
    printRaw`\u00A9${ 'and' }\n`;
    // Actual characters:
    // ©
    //(换行符)
    // Escaped characters:
    // \u00A9
    // \n
    🔹 模板字面量的行为

当你使用标签模板时,字符串部分和插值部分是分开的,具体来说:
• strings 是一个 数组,包含字符串部分。
• strings.raw 是原始字符串数组,不会对 \u00A9 或 \n 进行转义解析。
• 插值的 ${‘and’} 不会包含在 strings 数组中,而是作为函数的额外参数传递。

🔹 解析 printRaw\u00A9${ ‘and’ }\n``

printRaw 函数的实际接收参数是:

1
printRaw(["\u00A9", "\n"], "and");

参数解析

1
2
strings = ["\u00A9", "\n"]; // 解析后是 ["©", "\n"]
strings.raw = ["\\u00A9", "\\n"]; // 原始字符串

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

1
2
let sym = Symbol();
console.log(typeof sym); // "symbol"

符号没有字面量语法,这也是它们发挥作用的关键。按照规范,你只要创建 Symbol()实例并将其用作对象的新属性,就可以保证它不会覆盖已有的对象属性,无论是符号属性还是字符串属性。
最重要的是,Symbol()函数不能与 new 关键字一起作为构造函数使用。这样做是为了避免创建符号包装对象,像使用 Boolean、String 或 Number 那样,它们都支持构造函数且可用于初始化包含原始值的包装对象:
let myBoolean = new Boolean();
console.log(typeof myBoolean); // “object”
let myString = new String();
console.log(typeof myString); // “object”
let myNumber = new Number();
console.log(typeof myNumber); // “object”

1
2
3
4
- 使用全局符号注册表:如果运行时的不同部分需要共享和重用符号实例,那么可以用一个字符串作为键,在全局符号注册表中创建并重用符号。使用Symbol.for()方法
```javascript
let fooGlobalSymbol = Symbol.for('foo');
console.log(typeof fooGlobalSymbol); // "symbol"

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

1
2
3
let fooGlobalSymbol = Symbol.for('foo');
let otherFooGlobalSymbol = Symbol.for('foo');
console.log(fooGlobalSymbol === otherFooGlobalSymbol); // true

即使采用相同的符号描述,在全局注册表中定义的符号跟使用 Symbol()定义的符号也并不等同:

1
2
3
let localSymbol = Symbol('foo');
let globalSymbol = Symbol.for('foo');
console.log(localSymbol === globalSymbol); // false

可以使用 Symbol.keyFor()来查询全局注册表,这个方法接收符号,返回该全局符号对应的字
符串键。如果查询的不是全局符号,则返回 undefined。

1
2
3
4
5
6
7
8
// 创建全局符号
let s = Symbol.for('foo');
console.log(Symbol.keyFor(s)); // foo
// 创建普通符号
let s2 = Symbol('bar');
console.log(Symbol.keyFor(s2)); // undefined
如果传给 Symbol.keyFor()的不是符号,则该方法抛出 TypeError
Symbol.keyFor(123); // TypeError: 123 is not a symbol
  • 使用符号作为属性:凡是可以使用字符串或数值作为属性的地方,都可以使用符号。这就包括了对象字面量属性和Object.defineProperty()/Object.defineProperties()定义的属性。对象字面量只能在计算属性语法中使用符号作为属性。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    let s1 = Symbol('foo'), 
    s2 = Symbol('bar'),
    s3 = Symbol('baz'),
    s4 = Symbol('qux');
    let o = {
    [s1]: 'foo val'
    };
    // 这样也可以:o[s1] = 'foo val';
    console.log(o);
    // {Symbol(foo): foo val}
    Object.defineProperty(o, s2, {value: 'bar val'});
    console.log(o);
    // {Symbol(foo): foo val, Symbol(bar): bar val}
    Object.defineProperties(o, {
    [s3]: {value: 'baz val'},
    [s4]: {value: 'qux val'}
    });
    console.log(o);
    // {Symbol(foo): foo val, Symbol(bar): bar val,
    // Symbol(baz): baz val, Symbol(qux): qux val}
    类似于 Object.getOwnPropertyNames()返回对象实例的常规属性数组,Object.getOwnPropertySymbols()返回对象实例的符号属性数组。这两个方法的返回值彼此互斥。Object.getOwnPropertyDescriptors()会返回同时包含常规和符号属性描述符的对象。Reflect.ownKeys()会返回两种类型
    的键
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    let s1 = Symbol('foo'), 
    s2 = Symbol('bar');
    let o = {
    [s1]: 'foo val',
    [s2]: 'bar val',
    baz: 'baz val',
    qux: 'qux val'
    };
    console.log(Object.getOwnPropertySymbols(o));
    // [Symbol(foo), Symbol(bar)]
    console.log(Object.getOwnPropertyNames(o));
    // ["baz", "qux"]
    console.log(Object.getOwnPropertyDescriptors(o));
    // {baz: {...}, qux: {...}, Symbol(foo): {...}, Symbol(bar): {...}}
    console.log(Reflect.ownKeys(o));
    // ["baz", "qux", Symbol(foo), Symbol(bar)]
此页目录
2024-11-25 日报 Day17