JavaScript 面向对象基础
JavaScript是一门多范式的编程语言,它支持面向对象编程,尽管其实现方式与传统的基于类的面向对象语言(如Java或C++)有所不同。在JavaScript中,面向对象编程是基于原型的,这为初学者提供了一种灵活但有时令人困惑的编程范式。
什么是面向对象编程?
面向对象编程(OOP)是一种编程范式,它使用"对象"来组织代码。对象是数据和操作数据的方法的集合。JavaScript中的对象可以看作是键值对的集合,其中值可以是数据或函数(方法)。
JavaScript中的面向对象编程是基于原型的,而不是基于类的。虽然ES6引入了class
关键字,但这只是语法糖,底层仍然是基于原型的。
JavaScript 中的对象基础
创建对象
在JavaScript中,有多种创建对象的方式:
- 对象字面量:最简单直接的方式
const person = {
firstName: "张",
lastName: "三",
fullName: function() {
return this.firstName + this.lastName;
}
};
console.log(person.fullName()); // 输出: 张三
- 使用构造函数:允许创建多个类似的对象
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
this.fullName = function() {
return this.firstName + this.lastName;
};
}
const person1 = new Person("张", "三");
const person2 = new Person("李", "四");
console.log(person1.fullName()); // 输出: 张三
console.log(person2.fullName()); // 输出: 李四
- ES6 Class语法:更现代的方式,但底层仍是基于原型的
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
fullName() {
return this.firstName + this.lastName;
}
}
const person = new Person("张", "三");
console.log(person.fullName()); // 输出: 张三
访问对象属性和方法
JavaScript提供两种访问对象属性和方法的方式:
// 点符号
console.log(person.firstName);
// 方括号符号(对于键名含有空格或特殊字符时特别有用)
console.log(person["firstName"]);
// 调用方法
console.log(person.fullName());
原型和原型链
JavaScript的面向对象系统是基于原型的,理解原型对于掌握JavaScript至关重要。
什么是原型?
每个JavaScript对象都有一个指向另一个对象的链接,这个对象就是原型。当试图访问一个对象的属性时,如果该对象本身没有这个属性,JavaScript会尝试在原型对象上查找这个属性。
原型链示例
// 使用构造函数和原型
function Student(name, grade) {
this.name = name;
this.grade = grade;
}
// 在原型上添加方法
Student.prototype.introduce = function() {
return `我是${this.name},我在${this.grade}年级`;
};
const student1 = new Student("小明", "三");
console.log(student1.introduce()); // 输出: 我是小明,我在三年级
// 原型链查找
console.log(student1.toString()); // 从Object.prototype继承的方法
当调用student1.introduce()
时,JavaScript首先在student1
对象上查找introduce
方法。如果找不到,则会查找其原型(即Student.prototype
),在那里能找到这个方法。
同样,当调用student1.toString()
时,由于在student1
和Student.prototype
上都找不到这个方法,JavaScript会继续沿着原型链查找,最终在Object.prototype
上找到它。
继承
JavaScript中,继承是通过原型链实现的。有几种实现继承的方式:
1. 原型继承
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {
return `${this.name}发出声音`;
};
function Dog(name, breed) {
Animal.call(this, name); // 调用父构造函数
this.breed = breed;
}
// 设置Dog的原型为Animal的实例
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
// 覆盖继承的方法
Dog.prototype.speak = function() {
return `${this.name}汪汪叫`;
};
const dog = new Dog("小黑", "拉布拉多");
console.log(dog.speak()); // 输出: 小黑汪汪叫
console.log(dog.name); // 输出: 小黑
console.log(dog.breed); // 输出: 拉布拉多
2. ES6类继承(语法糖)
class Animal {
constructor(name) {
this.name = name;
}
speak() {
return `${this.name}发出声音`;
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // 调用父类构造函数
this.breed = breed;
}
speak() {
return `${this.name}汪汪叫`;
}
}
const dog = new Dog("小黑", "拉布拉多");
console.log(dog.speak()); // 输出: 小黑汪汪叫
封装和私有属性
在ES2022之前,JavaScript没有内置的私有属性语法,开发者通常使用命名约定(如下划线前缀)或闭包来模拟私有属性: