JavaScript 对象与面向对象

JavaScript 是一种基于原型的语言,这意味着它没有传统的类继承结构,而是通过对象与原型链来实现继承和代码复用。随着 ES6 的到来,JavaScript 引入了 class 语法糖,让代码更接近面向对象的风格。

1. 构造函数

1.1 基本构造函数

构造函数是一种用于创建特定类型对象的函数。常见的命名约定是首字母大写:

1
2
3
4
5
6
7
function Person(name, age) {
this.name = name;
this.age = age;
}

let alice = new Person("Alice", 25);
console.log(alice); // 输出 Person { name: "Alice", age: 25 }

1.2 构造函数的属性和方法

可以在构造函数中添加方法,但这样做会为每个实例单独创建方法。更好的方法是使用 原型,这样所有实例可以共享方法。

1
2
3
4
5
6
7
8
9
10
11
12
function Person(name, age) {
this.name = name;
this.age = age;
}

// 添加方法到原型上
Person.prototype.greet = function() {
console.log(`Hello, my name is ${this.name}`);
};

let bob = new Person("Bob", 30);
bob.greet(); // 输出 "Hello, my name is Bob"

2. 原型与原型链

2.1 什么是原型

每个 JavaScript 对象(除了 null)都与另一个对象关联,这个对象就是 原型。通过原型对象,我们可以实现属性和方法的继承。

1
console.log(bob.__proto__ === Person.prototype); // true

2.2 原型链

当我们访问一个对象的属性或方法时,如果该对象本身没有这个属性或方法,JavaScript 会沿着原型链向上查找,直到找到属性或达到原型链的顶端(null)。

1
console.log(bob.toString()); // toString 方法来自 Object.prototype

在上面的例子中,bob 没有定义 toString 方法,JavaScript 会在其原型链上找到 Object.prototype 并调用它的 toString 方法。

2.3 hasOwnProperty() 方法

用于检查对象本身是否拥有某个属性,而不是从原型链中继承的:

1
2
console.log(bob.hasOwnProperty("name"));     // true
console.log(bob.hasOwnProperty("toString")); // false

3. ES6 类

3.1 基本类的创建

ES6 引入了 class 关键字,它是构造函数的语法糖,使代码更具结构化和可读性:

1
2
3
4
5
6
7
8
9
10
11
12
class Animal {
constructor(name) {
this.name = name;
}

speak() {
console.log(`${this.name} makes a sound`);
}
}

let dog = new Animal("Dog");
dog.speak(); // 输出 "Dog makes a sound"

3.2 类的继承

使用 extends 可以创建子类,子类会继承父类的所有属性和方法。子类中的构造函数中需使用 super 调用父类的构造函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
}

bark() {
console.log(`${this.name} barks`);
}
}

let labrador = new Dog("Buddy", "Labrador");
labrador.speak(); // 输出 "Buddy makes a sound"
labrador.bark(); // 输出 "Buddy barks"

3.3 静态方法

静态方法是直接定义在类上的方法,而不是在实例上。使用 static 关键字定义静态方法,可以直接通过类来调用。

1
2
3
4
5
6
7
class MathUtility {
static square(x) {
return x * x;
}
}

console.log(MathUtility.square(5)); // 输出 25

4. this 关键字的作用

在 JavaScript 中,this 的值取决于 它被调用的方式。在面向对象编程中,this 通常指向当前对象的实例。

4.1 箭头函数与 this

箭头函数不绑定 this,它会捕获所在作用域的 this 值。在对象方法中,如果用箭头函数定义方法,this 会指向父作用域,而不是对象实例。

1
2
3
4
5
6
7
8
9
10
class Counter {
count = 0;

increment = () => {
console.log(this.count++);
}
}

let counter = new Counter();
counter.increment(); // 输出 1

5. 私有属性与方法

在 JavaScript 中,私有属性并不是天然支持的。不过可以通过以下几种方法实现:

5.1 使用闭包

可以在构造函数中使用局部变量模拟私有属性:

1
2
3
4
5
6
7
8
9
10
function SecretAgent(name) {
let codeName = "Secret";

this.getName = function() {
return `${name} is known as ${codeName}`;
};
}

let agent = new SecretAgent("James Bond");
console.log(agent.getName()); // 输出 "James Bond is known as Secret"

5.2 ES2021 中的 # 符号

使用 # 定义私有属性,无法通过对象实例访问:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class User {
#password;

constructor(name, password) {
this.name = name;
this.#password = password;
}

checkPassword(password) {
return this.#password === password;
}
}

let user = new User("Alice", "secret123");
console.log(user.checkPassword("secret123")); // true
// console.log(user.#password); // 报错,无法访问私有属性

JavaScript 对象与面向对象
https://blog.pangcy.cn/2020/05/03/前端编程相关/JavaScript/JavaScript 对象与面向对象/
作者
子洋
发布于
2020年5月3日
许可协议