2025.1.16
1.for循环中的let声明
使用 let 声明变量
-
块级作用域 :
let声明的变量具有块级作用域,这意味着在每次for循环的迭代中,都会创建一个新的i变量。 -
独立的变量 :每次迭代中的
i是独立的,每个setTimeout回调函数捕获的是当前迭代中的i的值。因此,每个回调函数在执行时都能正确地访问到它所在迭代的i的值。 -
示例:
javascriptfor (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。 -
示例:
javascriptfor (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.
工作原理:
-
模板字符串 :模板字符串使用反引号(`````)定义,并可以包含嵌入的表达式(用```${}` 包围)。
-
标签函数:标签函数是紧跟在模板字符串前面的函数。这个函数会接收到两个参数:
-
strings:一个数组,包含模板字符串中的字面量部分。 -
values:一个数组,包含模板字符串中的表达式部分。
-
-
处理逻辑 :标签函数可以对
strings和values进行任意处理,然后返回最终的结果。
常见用途:
=》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, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
const name = '<script>alert("XSS")</script>';
const output = safe`Hello, ${name}.`;
console.log(output); // 输出: Hello, <script>alert("XSS")</script>
=》防止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.原始值和引用值
原始值:原始值是基本的数据段,直接包含数据。
-
String :字符串,例如
"Hello"。 -
Number :数字,例如
42。 -
Boolean :布尔值,例如
true或false。 -
Undefined :未定义,例如
let x;。 -
Null :空值,例如
let x = null;。 -
Symbol:唯一且不可变的数据类型,用于创建对象的唯一属性名。
-
BigInt :大整数,用于表示大于
2^53 - 1的整数。
特点:不可变,存储在栈内存中,赋值时复制值。
引用值:引用值是存储在堆内存中的对象。引用值包括所有对象类型。
-
Object :普通对象,例如
{ name: 'John' }。 -
Array :数组,例如
[1, 2, 3]。 -
Function :函数,例如
function greet() { console.log('Hello'); }。 -
其他对象类型 :如
Date、RegExp等。
特点:可变,存储在堆内存中,赋值时复制引用。
字符串字面量 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
内部上下文可通过作用域链访问外部上下文中的一切,
外部上下文无法访问内部上下文中的任何东西。