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
内部上下文可通过作用域链访问外部上下文中的一切,
外部上下文无法访问内部上下文中的任何东西。