JavaScript 常用知识点全面指南
1. 变量声明
在 JavaScript 中,变量用于存储数据。你可以使用 var
、let
或 const
来声明变量。
-
var
:早期使用的变量声明方式,有函数作用域。声明的变量可以在其所在的函数内任何地方访问,存在变量提升。var x = 10;
-
let
:推荐的声明方式,有块级作用域,不存在变量提升。只能在声明后使用。let y = 20;
-
const
:用于声明常量,声明后不能重新赋值。也有块级作用域。const z = 30;
2. 数据类型
JavaScript 中有多种数据类型,主要包括以下几类:
-
原始类型(Primitive Types):
- 字符串 (String):用于表示文本,如
"Hello"
,'World'
。 - 数字 (Number):用于表示整数和浮点数,如
42
,3.14
。 - 布尔值 (Boolean):用于表示真值 (
true
) 和假值 (false
)。 - 空值 (Null):表示一个空对象引用。
- 未定义 (Undefined):表示变量未赋值。
- 符号 (Symbol):用于创建唯一的标识符。
- 字符串 (String):用于表示文本,如
-
引用类型(Reference Types):
- 对象 (Object):用于存储键值对的集合或更复杂的实体。
- 数组 (Array):特殊类型的对象,用于存储有序集合。
- 函数 (Function):可调用的对象,用于封装逻辑代码。
3. 运算符
JavaScript 提供了多种运算符,用于执行不同的操作:
-
算术运算符:用于执行数学运算,如加 (
+
)、减 (-
)、乘 (*
)、除 (/
)、取余 (%
)。let sum = 10 + 20; // 30
-
比较运算符:用于比较两个值的大小或相等性,如
==
、===
、!=
、!==
、>
、<
等。let isEqual = (5 === 5); // true
-
逻辑运算符:用于布尔值的逻辑操作,如与 (
&&
)、或 (||
)、非 (!
)。let result = true && false; // false
-
赋值运算符:用于给变量赋值,如
=
、+=
、-=
、*=
等。let x = 10; x += 5; // 15
4、 模板字符串
4.1 模板字符串的基本概念
模板字符串是用反引号(` `)包裹的字符串,可以包含插值表达式、换行符以及其他更复杂的字符串处理功能。
定义模板字符串
模板字符串由反引号(` `)包裹,支持多行字符串和嵌入表达式。
// 定义模板字符串
const greeting = `Hello, world!`;
const multiLine = `This is a string
that spans multiple lines.`;
嵌入表达式
使用 ${}
语法可以在模板字符串中嵌入变量和表达式。表达式会被计算,并将结果插入到字符串中。
const name = "Alice";
const age = 30;
const message = `Hello, ${name}. You are ${age} years old.`;
console.log(message); // 输出 "Hello, Alice. You are 30 years old."
4.2 模板字符串的功能
多行字符串
模板字符串允许直接书写多行字符串,无需使用换行符或转义字符。
const poem = `Roses are red,
Violets are blue,
Sugar is sweet,
And so are you.`;
console.log(poem);
插入变量和表达式
通过 ${}
语法插入 JavaScript 表达式,支持动态生成字符串内容。
const price = 100;
const discount = 20;
const finalPrice = `The final price after discount is $${price - discount}.`;
console.log(finalPrice); // 输出 "The final price after discount is $80."
字符串插值和格式化
可以利用模板字符串进行复杂的字符串插值和格式化操作,例如生成动态 SQL 查询、HTML 内容等。
const userId = 42;
const query = `SELECT * FROM users WHERE id = ${userId};`;
console.log(query); // 输出 "SELECT * FROM users WHERE id = 42;"
4.3 模板字符串的应用场景
动态内容生成
模板字符串非常适合用来生成动态内容,例如用户界面、报告生成等。
const userName = "John";
const userAge = 25;
const profile = `<div>
<h1>${userName}</h1>
<p>Age: ${userAge}</p>
</div>`;
document.body.innerHTML = profile;
多行文本处理
处理多行文本时,模板字符串的自然换行和格式化功能使代码更加整洁。
const htmlTemplate = `
<header>
<h1>Welcome to my website</h1>
</header>
<main>
<p>This is the main content area.</p>
</main>
<footer>
<p>© 2024 My Website</p>
</footer>`;
5. 控制结构
控制结构决定代码的执行流程。常用的控制结构包括条件语句和循环语句。
-
条件语句 (
if-else
):用于根据条件的真假来执行不同的代码块。let age = 18; if (age >= 18) { console.log("You are an adult."); } else { console.log("You are a minor."); }
-
switch
语句:用于基于不同的情况执行代码块。let day = 3; switch (day) { case 1: console.log("Monday"); break; case 2: console.log("Tuesday"); break; case 3: console.log("Wednesday"); break; default: console.log("Unknown day"); }
-
循环语句 (
for
,while
):用于重复执行代码块,直到条件不成立。-
for
循环:for (let i = 0; i < 5; i++) { console.log(i); // 输出 0 到 4 }
-
while
循环:let i = 0; while (i < 5) { console.log(i); i++; }
-
6. 函数
1. 函数的定义
在 JavaScript 中,可以通过多种方式定义函数:
-
函数声明:使用
function
关键字定义函数。这种方式定义的函数会在代码执行之前被提升,因此可以在函数声明之前调用它。function greet(name) { return "Hello, " + name; } console.log(greet("Alice")); // 输出 "Hello, Alice"
-
函数表达式:将函数赋值给变量。这种方式定义的函数不会被提升,因此只能在函数表达式之后调用。
const greet = function(name) { return "Hello, " + name; }; console.log(greet("Bob")); // 输出 "Hello, Bob"
-
箭头函数
箭头函数的基本语法
箭头函数的语法非常简洁,用
=>
符号代替了传统函数的function
关键字。基本形式如下:const functionName = (parameters) => { // 函数体 };
如果函数体只有一行表达式,并且需要返回这个表达式的值,可以省略大括号
{}
和return
关键字:const add = (a, b) => a + b; console.log(add(2, 3)); // 输出 5
如果没有参数,可以用空括号
()
表示:const greet = () => "Hello, World!"; console.log(greet()); // 输出 "Hello, World!"
如果只有一个参数,括号
()
也可以省略:const square = x => x * x; console.log(square(4)); // 输出 16
箭头函数与
this
箭头函数最大的特点之一是它没有自己的
this
绑定,而是从定义时的上下文中继承this
。这意味着在箭头函数中,this
的值与箭头函数定义所在的作用域中的this
相同。-
传统函数中的
this
:在传统函数中,
this
的值取决于函数的调用方式。function TraditionalFunction() { console.log(this); } const obj = { method: TraditionalFunction }; obj.method(); // 输出 obj 对象
-
箭头函数中的
this
:在箭头函数中,
this
是静态的,即它不会随调用方式改变,而是继承自箭头函数定义时的上下文。const obj = { name: "Alice", greet: function() { const innerFunc = () => { console.log(this.name); // 继承自 obj }; innerFunc(); } }; obj.greet(); // 输出 "Alice"
箭头函数中的
this
如果将示例中的箭头函数
innerFunc
替换为传统的function
函数,它的行为会有所不同。具体来说,传统的function
函数会有自己的this
绑定,而不是继承自外部函数。因此,在使用传统函数时,this
的值将不再指向obj
对象,而是根据函数调用的方式来决定。示例代码及其解释
const obj = { name: "Alice", greet: function() { const innerFunc = function() { console.log(this.name); }; innerFunc(); } }; obj.greet(); // 输出 undefined
解释
-
this
在greet
方法中:
在greet
方法中,this
绑定到obj
对象,因此this.name
正确地指向了obj
的name
属性,即"Alice"
。 -
this
在innerFunc
中:
当innerFunc
使用传统函数(function
)定义时,它的this
绑定与其调用方式有关。由于innerFunc
是作为普通函数调用的(即没有通过对象调用),它的this
默认为全局对象(在浏览器中是window
,在严格模式下是undefined
)。
因此,this.name
在innerFunc
中会指向undefined
,因为全局对象(或严格模式下的undefined
)没有name
属性。
修复方法
要解决这个问题并确保
innerFunc
中的this
继续指向obj
对象,可以使用以下几种方法:-
使用箭头函数(推荐):
继续使用箭头函数,因为箭头函数不会有自己的this
绑定,始终继承自外部作用域的this
。const innerFunc = () => { console.log(this.name); // 继承自 obj };
-
使用
bind
方法:
可以通过Function.prototype.bind
方法显式地将this
绑定到obj
。const innerFunc = function() { console.log(this.name); }.bind(this);
-
使用闭包:
在外部函数中保存this
的引用,然后在内部函数中使用这个引用。const greet = function() { const self = this; const innerFunc = function() { console.log(self.name); // 使用 self 来指代外部函数的 this }; innerFunc(); };
-
2. 函数的参数
函数可以接收多个参数,并在函数体内使用这些参数。参数在定义时指定,在调用函数时传递实际的值。
-
参数与默认值:在函数定义时可以为参数指定默认值,如果调用时没有传递该参数,则使用默认值。
function greet(name = "Guest") { return "Hello, " + name; } console.log(greet()); // 输出 "Hello, Guest"
-
剩余参数 (
...rest
):当你不确定函数会接收多少参数时,可以使用剩余参数语法来处理。function sum(...numbers) { return numbers.reduce((acc, curr) => acc + curr, 0); } console.log(sum(1, 2, 3, 4)); // 输出 10
3. 函数的返回值
函数可以通过 return
语句返回一个值。return
语句会终止函数的执行,并将结果返回给调用者。
-
返回单个值:
function square(x) { return x * x; } console.log(square(4)); // 输出 16
-
返回多个值:JavaScript 中不能直接返回多个值,但可以使用数组或对象来实现类似的功能。
-
使用数组:
function getCoordinates() { return [10, 20]; } const [x, y] = getCoordinates(); console.log(x, y); // 输出 10 20
-
使用对象:
function getUser() { return { name: "Alice", age: 25 }; } const { name, age } = getUser(); console.log(name, age); // 输出 "Alice" 25
-
4. 函数的作用域
-
局部作用域:在函数内部声明的变量具有局部作用域,只能在函数内部访问。
function greet() { let message = "Hello!"; console.log(message); // 输出 "Hello!" } greet(); // console.log(message); // 报错:message 未定义
-
全局作用域:在函数外部声明的变量具有全局作用域,可以在任何地方访问。
let message = "Hello, World!"; function greet() { console.log(message); // 输出 "Hello, World!" } greet();
-
嵌套函数:函数可以嵌套在其他函数中,内部函数可以访问外部函数的变量,但外部函数无法访问内部函数的变量。
function outer() { let outerVar = "Outside"; function inner() { let innerVar = "Inside"; console.log(outerVar); // 输出 "Outside" } inner(); // console.log(innerVar); // 报错:innerVar 未定义 } outer();
6. 回调函数
回调函数是作为参数传递给另一个函数的函数,通常在异步操作中使用。
-
异步回调:
function fetchData(callback) { setTimeout(() => { callback("Data received"); // 回调函数 }, 1000); } fetchData((data) => { console.log(data); // 输出 "Data received" });
7. 高阶函数
高阶函数是指接受函数作为参数或返回一个函数的函数。在 JavaScript 中,函数是一等公民,因此你可以将函数作为参数传递给其他函数,也可以从函数中返回一个新的函数。
-
函数作为参数:
function repeat(action, times) { for (let i = 0; i < times; i++) { action(); } } repeat(() => console.log("Hello!"), 3); // 输出三次 "Hello!"
-
函数返回函数:
function createGreeting(message) { return function(name) { return message + ", " + name; }; } const greet = createGreeting("Good morning"); console.log(greet("Alice")); // 输出 "Good morning, Alice"
8、 JavaScript 数组
数组的定义与初始化
数组是一个有序的数据集合,其中的每个元素都有一个索引。数组中的元素可以是任意数据类型,例如字符串、数字、对象,甚至其他数组。
// 数组的创建方式
let arr1 = []; // 创建一个空数组
let arr2 = [1, 2, 3]; // 创建一个包含数字的数组
let arr3 = ["apple", "banana", "cherry"]; // 创建一个包含字符串的数组
let arr4 = [1, "hello", true, { name: "Alice" }, [2, 3]]; // 混合类型数组
访问和修改数组元素
通过索引可以访问数组中的元素,索引从 0 开始计数。可以使用索引来获取或修改元素的值。
let fruits = ["apple", "banana", "cherry"];
console.log(fruits[0]); // 输出 "apple"
fruits[1] = "blueberry"; // 修改第二个元素
console.log(fruits); // 输出 ["apple", "blueberry", "cherry"]
数组的常用方法
JavaScript 提供了多种数组方法来操作数组,包括添加、删除元素,查找元素,排序等。
-
push()
和pop()
:push()
:向数组末尾添加一个或多个元素。pop()
:删除数组末尾的元素并返回该元素。
let numbers = [1, 2, 3]; numbers.push(4); // 添加元素 console.log(numbers); // 输出 [1, 2, 3, 4] let last = numbers.pop(); // 删除末尾元素 console.log(last); // 输出 4
-
unshift()
和shift()
:unshift()
:在数组开头添加一个或多个元素。shift()
:删除数组第一个元素并返回该元素。
let colors = ["red", "green", "blue"]; colors.unshift("yellow"); // 在开头添加元素 console.log(colors); // 输出 ["yellow", "red", "green", "blue"] let first = colors.shift(); // 删除第一个元素 console.log(first); // 输出 "yellow"
-
slice()
和splice()
:slice()
:返回数组的一个子数组,不修改原数组。splice()
:可以删除、替换或添加元素到数组中,会修改原数组。
let arr = [1, 2, 3, 4, 5]; let sliced = arr.slice(1, 3); // 提取索引 1 到 3 之间的元素,不包括索引 3 console.log(sliced); // 输出 [2, 3] arr.splice(2, 2, "a", "b"); // 从索引 2 开始删除 2 个元素,然后插入 "a", "b" console.log(arr); // 输出 [1, 2, "a", "b", 5]
-
forEach()
:用于对数组中的每个元素执行一次提供的函数。let names = ["Alice", "Bob", "Charlie"]; names.forEach(name => { console.log(name); }); // 输出 "Alice", "Bob", "Charlie"
-
map()
:返回一个新数组,新数组中的元素是原数组中每个元素经过函数处理后的结果。let numbers = [1, 2, 3]; let doubled = numbers.map(x => x * 2); console.log(doubled); // 输出 [2, 4, 6]
-
filter()
:返回一个新数组,包含所有通过指定函数测试的元素。let numbers = [1, 2, 3, 4, 5]; let evenNumbers = numbers.filter(x => x % 2 === 0); console.log(evenNumbers); // 输出 [2, 4]
数组的遍历
数组遍历是指依次访问数组中的每个元素。常用的遍历方式包括 for
循环、for...of
循环、forEach()
方法。
let fruits = ["apple", "banana", "cherry"];
// 使用 for 循环
for (let i = 0; i < fruits.length; i++) {
console.log(fruits[i]);
}
// 使用 for...of 循环
for (let fruit of fruits) {
console.log(fruit);
}
// 使用 forEach 方法
fruits.forEach(fruit => console.log(fruit));
9、 对象
对象的定义与初始化
对象是 JavaScript 中的一种数据结构,用于存储键值对。每个键(也称为属性名)都对应一个值,值可以是任何数据类型,包括另一个对象或函数。
let person = {
name: "Alice",
age: 25,
greet: function() {
console.log("Hello, my name is " + this.name);
}
};
访问和修改对象属性
可以通过点符号或方括号符号来访问或修改对象的属性。
let person = {
name: "Alice",
age: 25
};
console.log(person.name); // 输出 "Alice"
console.log(person["age"]); // 输出 25
person.name = "Bob"; // 修改属性值
console.log(person.name); // 输出 "Bob"
动态添加和删除属性
可以随时为对象添加或删除属性。
let person = {
name: "Alice"
};
// 动态添加属性
person.age = 25;
console.log(person); // 输出 { name: "Alice", age: 25 }
// 删除属性
delete person.name;
console.log(person); // 输出 { age: 25 }
对象的常用方法
对象是 JavaScript 中的核心概念之一,因此有许多方法可以用于操作对象。
-
Object.keys()
:返回对象自身可枚举属性的键组成的数组。let person = { name: "Alice", age: 25 }; let keys = Object.keys(person); console.log(keys); // 输出 ["name", "age"]
-
Object.values()
:返回对象自身可枚举属性的值组成的数组。let values = Object.values(person); console.log(values); // 输出 ["Alice", 25]
-
Object.entries()
:返回对象自身可枚举属性的键值对数组。let entries = Object.entries(person); console.log(entries); // 输出 [["name", "Alice"], ["age", 25]]
-
hasOwnProperty()
:判断对象是否拥有指定的属性(不包括原型链上的属性)。let person = { name: "Alice" }; console.log(person.hasOwnProperty("name")); // 输出 true console.log(person.hasOwnProperty("age")); // 输出 false
对象的遍历
可以使用 for...in
循环来遍历对象的属性。
let person = {
name: "Alice",
age: 25
};
for (let key in person) {
console.log(key + ": " + person[key]);
}
// 输出 "name: Alice" 和 "age: 25"
10. 输入与输出
JavaScript 提供了多种方式与用户交互,获取输入或显示输出。
-
prompt()
:获取用户输入。let name = prompt("Enter your name:"); console.log("Your name is " + name);
-
alert()
:显示警告框。alert("This is an alert!");
-
console.log()
:在控制台输出信息,常用于调试。console.log("Hello, World!");
11、JavaScript 类详解
JavaScript 的类为开发者提供了一种更清晰、更面向对象的方式来组织和管理代码。通过类,你可以创建对象的模板,封装数据和行为,并通过继承实现代码复用。
1. 类的定义与实例化
类的定义
类通过 class
关键字定义,类的主体包括构造函数(constructor
)和方法。构造函数在创建类的实例时被调用,用于初始化对象的属性。
class Person {
constructor(name, age) {
this.name = name; // 定义实例属性
this.age = age;
}
greet() { // 定义方法
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
}
}
实例化类
使用 new
关键字可以实例化一个类,这会调用构造函数并返回一个新对象。
let alice = new Person("Alice", 25); // 创建一个 Person 类的实例
alice.greet(); // 输出 "Hello, my name is Alice and I am 25 years old."
2. 类的属性与方法
实例属性与方法
-
实例属性:在类的构造函数中使用
this
关键字定义的属性称为实例属性,每个实例都有自己独立的实例属性。 -
实例方法:在类中定义的函数被称为方法,这些方法可以通过实例调用。
class Animal {
constructor(type, sound) {
this.type = type; // 实例属性
this.sound = sound;
}
makeSound() { // 实例方法
console.log(`${this.type} makes a ${this.sound} sound.`);
}
}
let dog = new Animal("Dog", "bark");
dog.makeSound(); // 输出 "Dog makes a bark sound."
静态属性与方法
静态属性和方法是类本身拥有的,而不是实例拥有的。静态方法使用 static
关键字定义,可以通过类名直接调用,而不是通过实例。
class MathUtil {
static count = 0; // 静态变量
static add(a, b) {
return a + b;
}
}
console.log(MathUtil.add(3, 4)); // 输出 7
console.log(MathUtil.count); // 输出 0
3. 继承与多态
类的继承
JavaScript 支持类的继承,可以使用 extends
关键字创建一个继承自另一个类的新类。继承使得子类能够继承父类的属性和方法,还可以重写父类的方法或添加新的方法。
class Animal {
constructor(type) {
this.type = type;
}
makeSound() {
console.log(`${this.type} makes a sound.`);
}
}
class Dog extends Animal {
constructor(name) {
super("Dog"); // 调用父类的构造函数
this.name = name;
}
makeSound() {
console.log(`${this.name} barks.`);
}
}
let myDog = new Dog("Buddy");
myDog.makeSound(); // 输出 "Buddy barks."
super
关键字
super
关键字用于调用父类的构造函数或方法。在子类的构造函数中,调用 super()
必须先于使用 this
关键字。
class Cat extends Animal {
constructor(name) {
super("Cat"); // 调用父类的构造函数
this.name = name;
}
makeSound() {
super.makeSound(); // 调用父类的方法
console.log(`${this.name} meows.`);
}
}
let myCat = new Cat("Whiskers");
myCat.makeSound();
// 输出:
// "Cat makes a sound."
// "Whiskers meows."
多态
多态性允许我们使用相同的方法名但在不同的类中实现不同的行为。当调用一个方法时,实际执行的是该方法在对象所属类中的实现。这使得代码更加灵活和可扩展。
class Animal {
makeSound() {
console.log("Some generic animal sound.");
}
}
class Cow extends Animal {
makeSound() {
console.log("Moo!");
}
}
let genericAnimal = new Animal();
let cow = new Cow();
genericAnimal.makeSound(); // 输出 "Some generic animal sound."
cow.makeSound(); // 输出 "Moo!"
4. 类的访问控制与封装
JavaScript 在类中提供了对属性和方法的访问控制,通过使用 #
前缀来定义私有属性或方法,这些属性和方法只能在类的内部访问。
class BankAccount {
#balance = 0; // 私有属性
deposit(amount) {
this.#balance += amount;
console.log(`Deposited: $${amount}. New balance: $${this.#balance}`);
}
withdraw(amount) {
if (amount <= this.#balance) {
this.#balance -= amount;
console.log(`Withdrew: $${amount}. New balance: $${this.#balance}`);
} else {
console.log("Insufficient funds.");
}
}
getBalance() { // 公有方法访问私有属性
return this.#balance;
}
}
let myAccount = new BankAccount();
myAccount.deposit(100);
myAccount.withdraw(30);
console.log(myAccount.getBalance()); // 输出 70