在ES6(ECMAScript 2015)之前,JavaScript 并没有像其他面向对象的编程语言那样的类(class)的概念。相反,它使用了一种基于原型的继承模型来实现面向对象编程。然而,这种模型对于许多开发者来说可能比较难以理解,特别是在与更传统的面向对象语言(如Java或C++)相比较时。
为了简化面向对象的编程范式并提供更直观的代码组织方式,ES6引入了类的概念。虽然JavaScript的类实际上是基于原型的继承和构造函数的语法糖,但它提供了一种更加清晰、简洁的方式来定义对象的行为和结构。
面向对象的特性:封装性、继承性、多态性。
面向对象的优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统 更加灵活、更加易于维护。
面向对象的缺点:性能比面向过程低。
面向对象的思维特点:
(1) 抽取(抽象)对象共用的属性和行为组织(封装)成一个类(模板) 。
(2) 对类进行实例化, 获取类的对象。
在 JavaScript 中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象,例如字符串、数值、数组、函数等,即万物皆对象。
对象是由属性和方法组成的:
属性:事物的特征,在对象中用属性来表示(常用名词)。
方法:事物的行为,在对象中用方法来表示(常用动词)。
类抽象了对象的公共部分,它泛指某一大类(class)。
对象特指某一个,通过类实例化一个具体的对象。
语法:
class Name {
// class body
}
创建实例:
const xx = new Name();
constructor() 方法是类的构造函数(默认方法),用于传递参数,返回实例对象,通过 new 命令生成对象实例时,自动调用该方法。如果没有显式定义,类内部会自动给我们创建一个constructor()。
// 1. 创建类 class 创建一个人类
class Person {
constructor(name, age) {
this.name = name;
this.age = age
}
}
// 2. 利用类创建对象 new
const p1 = new Person('前端技术营', 18)
console.log(p1) //Person {age: 18, name: "前端技术营"}
console.log(p1.name) //前端技术营
const p2 = new Person('张三', 22)
console.log(p2) //Person {age: 22, name: "张三"}
console.log(p2.age) // 22
注意:
通过class 关键字创建类,类名我们还是习惯性定义首字母大写。
类里面有个constructor 函数,可以接受传递过来的参数,同时返回实例对象。
constructor 函数只要 new 生成实例时,就会自动调用这个函数,如果我们不写这个函数,类也会自动生成这个函数。
生成实例 new 不能省略,类必须使用new实例化对象。
最后注意语法规范,创建类类名后面不要加小括号,生成实例类名后面加小括号,构造函数不需要加function。
// 1. 创建类 class 创建一个人类
class Person {
constructor(name, age) {
this.name = name;
this.age = age
}
coding(code) {
// something
console.log(`${code}, ${this.name}`)
}
}
// 2. 利用类创建对象 new
const p1 = new Person('前端技术营', 18)
console.log(p1)
//Person {age: 18, name: "前端技术营"}
p1.coding('Hello,World')
// Hello,World, 前端技术营
const p2 = new Person('张三', 22)
console.log(p2)
//Person {age: 22, name: "张三"}
p2.coding('JavaScript是世界上最好的语言之一')
// JavaScript是世界上最好的语言之一, 张三
注意:
我们类里面所有的函数不需要写function。
多个函数方法之间不需要添加逗号分隔。
继承:子类可以继承父类的一些属性和方法。
语法:
class Father { //父类
// ...
}
class Son extends Father { //子类继承父类
// ...
}
例如:
class Father {
constructor() {
// ...
}
money() {
console.log(1000000)
}
}
class Son extends Father { // 这样子类就继承了父类的属性和方法
// ...
}
var son = new Son()
son.money() // 1000000
super 关键字用于访问和调用对象父类上的函数。可以调用父类的构造函数,也可以调用父类的普通函数。
// 例如:
class Father {
constructor(x, y) {
this.x = x;
this.y = y
}
sum() {
console.log(this.x + this.y)
}
}
class Son extends Father {
constructor(x, y) {
super(x, y) //调用了父类中的构造函数
}
}
var son = new Son(1, 2);
var son1 = new Son(10, 20);
son.sum() // 3
son1.sum() // 30
注意:
子类在构造函数中使用super, 必须放到 this 前面 (必须先调用父类的构造方法,在使用子类构造方法)。
class Person {
constructor(name) {
this.name = name
}
say() {
console.log('我是' + this.name)
}
}
class Programmer extends Person {
// 这样子类就继承了父类的属性和方法
constructor(name, language) {
super(name); //调用父类的constructor(name)
this.language = language
}
writing() {
console.log(`我是${this.name},正在疯狂写${this.language}`)
}
}
const p1 = new Programmer('前端技术营', 'JavaScript')
p1.say(); //我是前端技术营
p1.writing() //我是前端技术营,正在疯狂写JavaScript
class Father {
say() {
return '我是爸爸';
}
}
// 这样子类就继承了父类的属性和方法
class Son extends Father {
say() {
// super.say() super 调用父类的方法
return super.say() + '的儿子';
}
}
const son = new Son();
console.log(son.say()); //我是爸爸的儿子
注意:
在 ES6 中类没有变量提升,所以必须先定义类,才能通过类实例化对象。
类里面的共有属性和方法一定要加this使用。
类里面的this指向问题。
constructor 里面的this指向实例对象, 方法里面的this 指向这个方法的调用者。
<button>点击</button>
let that;
let _that;
class Person {
constructor(name, age) {
// constructor 里面的this 指向的是 创建的实例对象
that = this;
console.log(this);
this.name = name;
this.age = age;
// this.say();
this.btn = document.querySelector('button');
// this.say不加(),加()会自动调用
this.btn.onclick = this.say;
}
say() {
// 这个say方法里面的this 指向的是 btn 这个按钮,因为这个按钮调用了这个函数
console.log(this); //
// that里面存储的是constructor里面的this
console.log(that.name); // 前端技术营
}
study() {
// 这个study里面的this 指向的是实例对象 ldh 因为ldh 调用了这个函数
_that = this;
console.log(this);
// Person {name: '前端技术营', age: undefined, btn: button}
}
}
var p1 = new Person('前端技术营');
console.log(that === p1); // true
p1.study();
console.log(_that === p1); // true