2025.1.16学习笔记(红宝书)

2025.1.16

1.for循环中的let声明

使用 let 声明变量

  • 块级作用域let 声明的变量具有块级作用域,这意味着在每次 for 循环的迭代中,都会创建一个新的 i 变量。

  • 独立的变量 :每次迭代中的 i 是独立的,每个 setTimeout 回调函数捕获的是当前迭代中的 i 的值。因此,每个回调函数在执行时都能正确地访问到它所在迭代的 i 的值。

  • 示例

    javascript 复制代码
    for (let i = 0; i < 5; i++) {
        setTimeout(() => console.log(i), 0); // 输出 0, 1, 2, 3, 4
    }
    • 在这个例子中,每次迭代都会创建一个新的 i,每个 i 都是独立的。因此,每个 setTimeout 回调函数捕获的 i 的值分别是 0, 1, 2, 3, 4。

使用 var 声明变量

  • 函数级作用域var 声明的变量具有函数级作用域,这意味着在整个 for 循环中,只有一个 j 变量。

  • 共享的变量 :所有 setTimeout 回调函数捕获的是同一个 j 变量。当回调函数执行时,循环已经结束,j 的值为 5。因此,所有回调函数都打印 5。

  • 示例

    javascript 复制代码
    for (var j = 0; j < 5; j++) {
        setTimeout(() => console.log(j), 0); // 输出 5, 5, 5, 5, 5
    }
    • 在这个例子中,整个 for 循环中只有一个 j 变量。所有 setTimeout 回调函数捕获的都是这个共享的 j 变量。当回调函数执行时,j 的值已经是 5,因此所有回调函数都打印 5。
  • 宏任务队列 :无论是使用 let 还是 var,所有的 setTimeout 回调函数都被放入同一个宏任务队列中。

  • 事件循环:事件循环会按顺序从宏任务队列中取出任务并执行。每次执行一个宏任务后,会清空并执行微任务队列中的所有任务。

2.标签函数

定义:标签函数(Tagged Function)通常是指与模板字符串结合使用的函数。

基本语法

javascript 复制代码
function tag(strings, ...values) {
    // strings 是一个数组,包含模板字符串中的字面量部分
    // values 是一个数组,包含模板字符串中的表达式部分
    let result = '';
    for (let i = 0; i < strings.length; i++) {
        result += strings[i];
        if (i < values.length) {
            result += values[i];
        }
    }
    return result;
}
const name = 'Alice';
const age = 30;
const output = tag`Hello, ${name}. You are ${age} years old.`;
console.log(output); // 输出: Hello, Alice. You are 30 years old.

工作原理

  1. 模板字符串 :模板字符串使用反引号(`````)定义,并可以包含嵌入的表达式(用```${}` 包围)。

  2. 标签函数:标签函数是紧跟在模板字符串前面的函数。这个函数会接收到两个参数:

    • strings:一个数组,包含模板字符串中的字面量部分。

    • values:一个数组,包含模板字符串中的表达式部分。

  3. 处理逻辑 :标签函数可以对 stringsvalues 进行任意处理,然后返回最终的结果。

常见用途

=》HTML转义防止XSS攻击

javascript 复制代码
function safe(strings, ...values) {
    let result = '';
    for (let i = 0; i < strings.length; i++) {
        result += strings[i];
        if (i < values.length) {
            result += escapeHtml(values[i]);
        }
    }
    return result;
}
function escapeHtml(unsafe) {
    return unsafe
        .replace(/&/g, '&amp;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#039;');
}
const name = '<script>alert("XSS")</script>';
const output = safe`Hello, ${name}.`;
console.log(output); // 输出: Hello, &lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;

=》防止SOL注入

javascript 复制代码
function sql(strings, ...values) {
    let query = '';
    for (let i = 0; i < strings.length; i++) {
        query += strings[i];
        if (i < values.length) {
            query += `'${values[i].replace(/'/g, "''")}'`;
        }
    }
    return query;
}
const name = "O'Brien";
const age = 30;
const query = sql`SELECT * FROM users WHERE name = ${name} AND age = ${age};`;
console.log(query); // 输出: SELECT * FROM users WHERE name = 'O''Brien' AND age = 30;

=》国际化i18n:

vue中的多语言支持原理之一

javascript 复制代码
const translations = {
    en: {
        greeting: 'Hello, ',
        age: 'You are ',
        years: ' years old.'
    },
    fr: {
        greeting: 'Bonjour, ',
        age: 'Vous avez ',
        years: ' ans.'
    }
};

function i18n(lang) {
    return function(strings, ...values) {
        const dict = translations[lang];
        let result = '';
        for (let i = 0; i < strings.length; i++) {
            result += dict[strings[i].trim()] || strings[i];
            if (i < values.length) {
                result += values[i];
            }
        }
        return result;
    };
}
const name = 'Alice';
const age = 30;
const outputEn = i18n('en')`greeting ${name} age ${age} years`;
const outputFr = i18n('fr')`greeting ${name} age ${age} years`;
console.log(outputEn); // 输出: Hello, Alice You are 30 years old.
console.log(outputFr); // 输出: Bonjour, Alice Vous avez 30 ans.

=》调试和日志记录

javascript 复制代码
function log(level) {
    return function(strings, ...values) {
        const timestamp = new Date().toISOString();
        let message = strings.reduce((acc, str, i) => {
            return acc + str + (i < values.length ? values[i] : '');
        }, '');
        console.log(`[${timestamp}] [${level}] ${message}`);
    };
}
const name = 'Alice';
const age = 30;
log('INFO')`User ${name} is ${age} years old.`;
// 输出: [2025-01-16T12:00:00.000Z] [INFO] User Alice is 30 years old.

3.Symbol数据类型

Symbol 是一种基本数据类型,用于创建唯一的、不可变的值。Symbol 类型的值可以作为对象属性的键,可避免键名冲突。

1 .创建Symbol:

javascript 复制代码
const mySymbol = Symbol('mySymbol');
console.log(mySymbol); // 输出: Symbol(mySymbol)

2.Symbol具有唯一性

即使创建Symbol的字符串的值一样,所创建的Symbol仍然是不同的Symbol

javascript 复制代码
const symbol1 = Symbol('mySymbol');
const symbol2 = Symbol('mySymbol');
console.log(symbol1 === symbol2); // 输出: false

3.Symbol作为对象属性名的键,避免键名冲突

javascript 复制代码
const mySymbol = Symbol('mySymbol');
const obj = {
    [mySymbol]: 'Hello, Symbol!'
};
console.log(obj[mySymbol]); // 输出: Hello, Symbol!

4.全局Symbol注册表:(幂等操作)

JavaScript 提供了一个全局 Symbol 注册表,可以通过 Symbol.for(key) 方法从注册表中检索或创建 Symbol 值。如果 key 已经存在于注册表中,则返回该 Symbol;否则,创建一个新的 Symbol 并将其添加到注册表中。

javascript 复制代码
const symbol1 = Symbol.for('mySymbol');
const symbol2 = Symbol.for('mySymbol');
const symbol3 = Symbol('mySymbol');
console.log(symbol1 === symbol2); // 输出: true
console.log(symbol2 === symbol3); //输出:false

5.Symbol内置值:

Symbol.iterator 用于定义对象的默认迭代器

=>value:表示当前迭代到的值。

=>done:表示迭代是否已经完成。

false:表示还有更多的值可以迭代。

true:表示迭代已经完成,没有更多的值可以迭代。

javascript 复制代码
const myArray = [1, 2, 3];
const iterator = myArray[Symbol.iterator]();
console.log(iterator.next()); // 输出: { value: 1, done: false }
console.log(iterator.next()); // 输出: { value: 2, done: false }
console.log(iterator.next()); // 输出: { value: 3, done: false }
console.log(iterator.next()); // 输出: { value: undefined, done: true }

6.Symbol不可枚举:

Symbol 作为对象属性的键时,该属性是不可枚举的,不会出现在 for...in 循环或 Object.keys() 方法中。但是,可以使用 Object.getOwnPropertySymbols() 方法获取对象上的所有 Symbol 属性键。

javascript 复制代码
const mySymbol = Symbol('mySymbol');
const obj = {
    [mySymbol]: 'Hello, Symbol!',
    prop: 'Hello, Prop!'
};
for (const key in obj) {
    console.log(key); // 输出: prop
}
console.log(Object.keys(obj)); // 输出: ['prop']
console.log(Object.getOwnPropertySymbols(obj)); // 输出: [Symbol(mySymbol)]

4.操作符

1.短路操作符:

逻辑与:&&

逻辑或:||

javascript 复制代码
let foo=false;
let bar=true;
let result1=(foo && undeclare);
console.log(result1);//输出:false
let result2=(bar || undeclare);
console.log(result2);//输出:true

2.指数操作符:**

javascript 复制代码
console.log(3**2);//输出:9
let squared=4;
squared**=2;
console.log(squared);//输出:16

5.原始值和引用值

原始值:原始值是基本的数据段,直接包含数据。

  1. String :字符串,例如 "Hello"

  2. Number :数字,例如 42

  3. Boolean :布尔值,例如 truefalse

  4. Undefined :未定义,例如 let x;

  5. Null :空值,例如 let x = null;

  6. Symbol:唯一且不可变的数据类型,用于创建对象的唯一属性名。

  7. BigInt :大整数,用于表示大于 2^53 - 1 的整数。

特点:不可变,存储在栈内存中,赋值时复制值。

引用值:引用值是存储在堆内存中的对象。引用值包括所有对象类型。

  1. Object :普通对象,例如 { name: 'John' }

  2. Array :数组,例如 [1, 2, 3]

  3. Function :函数,例如 function greet() { console.log('Hello'); }

  4. 其他对象类型 :如 DateRegExp 等。

特点:可变,存储在堆内存中,赋值时复制引用。

字符串字面量 vs 字符串对象

  • 字符串字面量name1 = "Nicholas" 是一个字符串字面量。字符串字面量是原始数据类型(primitive type),它们是不可变的(immutable),这意味着你不能改变字符串的内容。

  • 字符串对象name2 = new String("Matt") 是通过 String 构造函数创建的字符串对象。字符串对象是引用数据类型(reference type),它们是可变的(mutable),可以添加属性和方法。

javascript 复制代码
let name1 = "Nicholas"; // 字符串字面量
let name2 = new String("Matt"); // 字符串对象
name1.age = 27; // 尝试给字符串字面量添加属性,会被忽略
name2.age = 26; // 给字符串对象添加属性,成功
console.log(name1.age); // undefined,因为字符串字面量不能添加属性
console.log(name2.age); // 26,因为字符串对象可以添加属性
console.log(typeof name1); // "string",字符串字面量
console.log(typeof name2); // "object",字符串对象

6.传递参数

函数参数的传递方式是按值传递.

传递的是引用的副本,修改引用指向的对象的属性会影响外部变量。

在函数内部重新赋值不会改变外部变量的引用。

javascript 复制代码
function setName(obj){
    obj.name="Nicholas";
    obj=new Object();
    obj.name="Greg";
}
let person=new Object();
setName(person);
console.log(person.name);//输出:Nicholas

7.执行上下文与作用域

执行上下文:

->全局上下文

->函数上下文

->块级上下文

在浏览器中,全局上下文就是window对象

通过var定义的全局变量和函数会成为window对象的属性和方法

javascript 复制代码
var color = "blue";
function changeColor(){
    let anotherColor = "red";
    function swapColors(){
        let tempColor = anotherColor;
        anotherColor = color;
        color = tempColor;
        //这里可以访问color,anotherColor,tempColor
    }
    //这里可以访问color,anotherColor
    swapColors();
}
//这里只能访问color
changeColor();

=>全局上下文:变量color和函数changeColor()

=>changeColor()的局部上下文:变量anotherColor和函数swapColors()

=>swapColors()的局部上下文:变量tempColor

内部上下文可通过作用域链访问外部上下文中的一切,

外部上下文无法访问内部上下文中的任何东西。

相关推荐
LiuIleCPP_Golang4 分钟前
【2025 Rust学习 --- 16 集合:Rust的STL】
学习·rust
qq_3667406023 分钟前
【学习笔记】各种强化学习环境
人工智能·笔记·python·ubuntu
LuckyLay37 分钟前
Golang学习笔记_26——通道
笔记·学习·golang·通道·channel
隼玉1 小时前
【STM32-学习笔记-4-】PWM、输入捕获(PWMI)
笔记·stm32·学习
Jiaberrr1 小时前
基于 Vue 的拖拽缩放卡片组件:实现思路、方法及使用指南
前端·javascript·vue.js·前端框架
Future_yzx1 小时前
1️⃣Java中的集合体系学习汇总(List/Map/Set 详解)
java·学习·list
创小匠1 小时前
创客匠人老蒋:创始人IP如何为传统产业注入新活力?
大数据·前端·网络·人工智能·tcp/ip·sass
后端转全栈_小伵1 小时前
Redis 缓存穿透、击穿、雪崩 的区别与解决方案
redis·学习·缓存·面试
黄团团1 小时前
Vue2+OpenLayers实现点位拖拽功能(提供Gitee源码)
开发语言·前端·javascript·gitee·html
QBorfy1 小时前
00篇 AI系统学习前准备知识
前端·人工智能