Bootstrap

javascript(js)入门指南

JavaScript 常用知识点全面指南


1. 变量声明

在 JavaScript 中,变量用于存储数据。你可以使用 varletconst 来声明变量。

  • 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):用于创建唯一的标识符。
  • 引用类型(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>&copy; 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
    
    解释
    • thisgreet 方法中
      greet 方法中,this 绑定到 obj 对象,因此 this.name 正确地指向了 objname 属性,即 "Alice"

    • thisinnerFunc
      innerFunc 使用传统函数(function)定义时,它的 this 绑定与其调用方式有关。由于 innerFunc 是作为普通函数调用的(即没有通过对象调用),它的 this 默认为全局对象(在浏览器中是 window,在严格模式下是 undefined)。
      因此,this.nameinnerFunc 中会指向 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

;