第七章:对象与面向对象编程
7.1 对象的基本概念
对象的定义
对象是 JavaScript 中的一种复杂数据类型,它是一组无序的属性和方法的集合。属性是与对象相关的值,方法是可以在对象上执行的函数。例如:
var person = {
name: 'Alice',
age: 30,
sayHello: function() {
console.log('Hello!');
}
};
在这个例子中,person是一个对象,它有name和age两个属性,以及sayHello一个方法。
对象的创建方式
对象字面量:使用花括号{}包裹属性和方法来创建对象,就像上面person对象的例子。这是最直接和常用的方式。
构造函数:通过定义一个函数,使用new关键字来创建对象。例如:
function Person(name, age) {
this.name = name;
this.age = age;
this.sayHello = function() {
console.log('Hello, my name is'+ this.name);
};
}
var anotherPerson = new Person('Bob', 25);
这里Person是一个构造函数,anotherPerson是通过new关键字调用Person构造函数创建的对象。
访问对象的属性和方法
点语法:可以使用点(.)来访问对象的属性和方法。例如,对于person对象,可以使用person.name访问name属性,使用person.sayHello()调用sayHello方法。
方括号语法:当属性名是一个变量或者包含特殊字符时,需要使用方括号([])来访问。例如:
var propertyName = 'age';
console.log(person[propertyName]);
7.2 对象的属性
属性的添加和修改
可以在对象创建后添加或修改属性。例如:
var car = {
brand: 'Toyota'
};
car.model = 'Corolla'; // 添加一个新属性
car.brand = 'Honda'; // 修改已有的属性
属性的枚举
可以使用for…in循环来枚举对象的可枚举属性。例如:
var student = {
name: 'John',
grade: 'A',
age: 20
};
for (var property in student) {
console.log(property + ':'+ student[property]);
}
需要注意的是,for…in循环会遍历对象自身的和继承的可枚举属性(如果有继承的情况)。在 ES6 中,还可以使用Object.keys()方法来获取对象自身的可枚举属性的键名数组。例如:Object.keys(student)会返回[‘name’, ‘grade’, ‘age’]。
属性的删除
可以使用delete关键字来删除对象的属性。例如:
var product = {
name: 'Phone',
price: 500
};
delete product.price;
console.log(product);
删除属性后,再次访问该属性会返回undefined。
7.3 对象的方法
方法的定义和调用
方法是对象的属性,其值为函数。定义方法时,可以像定义普通函数一样在对象内部定义。调用方法时,通过对象来调用,就像前面例子中的person.sayHello()。
方法内部的this关键字指向调用该方法的对象。例如:
var rectangle = {
width: 10,
height: 5,
area: function() {
return this.width * this.height;
}
};
console.log(rectangle.area());
在area方法中,this指向rectangle对象。
方法中的 this 指向问题
当方法作为回调函数传递给其他函数时,this的指向可能会发生变化。例如:
var counter = {
count: 0,
increment: function() {
setTimeout(function() {
this.count++;
console.log(this.count);
}, 1000);
}
};
counter.increment();
在这个例子中,setTimeout中的回调函数里的this并不指向counter对象,而是指向window(在浏览器环境下)。为了解决这个问题,可以使用箭头函数或者保存this的引用。例如:
var counter = {
count: 0,
increment: function() {
var self = this;
setTimeout(function() {
self.count++;
console.log(self.count);
}, 1000);
}
};
counter.increment();
或者使用箭头函数:
var counter = {
count: 0,
increment: function() {
setTimeout(() => {
this.count++;
console.log(this.count);
}, 1000);
}
};
counter.increment();
7.4 面向对象编程(OOP)概念在 JavaScript 中的应用
封装
封装是指将数据(属性)和操作数据的方法封装在一个单元(对象)中,并且限制对数据的直接访问,通过对象提供的方法来操作数据。例如:
function BankAccount(balance) {
var _balance = balance;
this.deposit = function(amount) {
_balance += amount;
return _balance;
};
this.withdraw = function(amount) {
if (_balance >= amount) {
_balance -= amount;
return _balance;
} else {
console.log('Insufficient balance');
return _balance;
}
};
}
var myAccount = new BankAccount(1000);
myAccount.deposit(500);
myAccount.withdraw(300);
在这个BankAccount对象中,balance是一个私有变量(实际上在 JavaScript 中没有真正的私有变量,这只是一种模拟),外部不能直接访问,只能通过deposit和withdraw方法来操作账户余额。
继承
原型链继承:JavaScript 中对象有一个内部属性[[Prototype]](可以通过__proto__属性访问,虽然不推荐在正式代码中使用__proto_,因为它不是标准的),当访问对象的一个属性或方法时,如果对象本身没有,就会在其原型对象(通过[[Prototype]]链接的对象)中查找。可以通过设置原型来实现继承。例如:
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {
console.log(this.name +'makes a sound');
};
function Dog(name, breed) {
Animal.call(this, name);
this.breed = breed;
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
var myDog = new Dog('Buddy', 'Labrador');
myDog.speak();
在这个例子中,Dog继承了Animal的属性和方法。Object.create方法用于创建一个新对象,其原型为Animal.prototype。然后将Dog.prototype.constructor重新指向Dog,以保持正确的构造函数引用。
类继承(ES6):ES6 引入了class关键字,提供了一种更像传统面向对象语言的语法来实现继承。例如:
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(this.name +'makes a sound');
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
}
}
var myDog = new Dog('Buddy', 'Labrador');
myDog.speak();
这里Dog类通过extends关键字继承了Animal类,在Dog的构造函数中通过super关键字调用父类的构造函数来初始化继承的属性。
多态
多态是指不同类型的对象对同一操作可以有不同的行为。在 JavaScript 中,可以通过覆盖方法来实现多态。例如:
function Shape() {}
Shape.prototype.draw = function() {
console.log('Drawing a shape');
};
function Circle() {}
Circle.prototype = Object.create(Shape.prototype);
Circle.prototype.draw = function() {
console.log('Drawing a circle');
};
function Square() {}
Square.prototype = Object.create(Shape.prototype);
Square.prototype.draw = function() {
console.log('Drawing a square');
};
var shapes = [new Circle(), new Square()];
for (var i = 0; i < shapes.length; i++) {
shapes[i].draw();
}
在这个例子中,Circle和Square都继承自Shape,它们都有draw方法,但是具体的行为(输出的内容)不同,这就是多态的体现。