在现代前端开发中,JavaScript的面向对象编程成为了主流。ES6引入了class关键字,使得开发者可以更方便地使用面向对象的方式编写代码,更接近传统语言的写法。ES6的class可以看作是一个语法糖,它的绝大部分功能ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法。
一、定义和使用class
在JavaScript中,class是创建对象的一种方式,它定义了一个类的模板,包含了属性和方法的声明,在底层中仍然是使用原型和基于原型的继承。
// ES5写法
function ShuiGuo(name, price){this.name = name;this.price = price;
}
// 添加方法
ShuiGuo.prototype.eat = function(){console.log("吃水果啦!");
}
// 实例化对象
let shuiguo = new ShuiGuo("杨梅", 50);
console.log(shuiguo);
shuiguo.eat();
// ES6写法
class Fruit{// 构造方法constructor(name, price){this.name = name;this.price = price;}eat(){console.log("吃水果啦!");}
}
let fruit = new Fruit("西瓜", 50);
console.log(fruit);
fruit.eat();
在上面的代码示例中,我们定义了一个Fruit类,它有一个constructor构造函数和一个eat方法。构造函数用于初始化对象的属性,而方法用于定义对象的行为。通过new关键字我们可以创建Fruit类的实例,并调用其方法。
- class 类名{},不要写成class 类名(){},不用加小括号。
- constructor构造函数与eat方法之间不用写逗号。
- 定义方法时直接写eat(){},不要写成eat:function(){},不用冒号也不用写function。
二 、constructor构造函数
在类(class)中,constructor是一个特殊的方法,用于在创建对象时初始化
实例属性和方法。它接受传递给类的参数,并使用它们来创建和初始化类的对象。constructor方法是类中默认的构造函数,constructor这个关键字是固定的,不能随意修改。
当一个实例化对象被创建时,它会自动调用
constructor方法以初始化自己,用于实例化对象时初始化属性和方法。当使用new
关键字创建类的实例时,构造函数会被自动调用
。它是一个默认的方法,如果没有手动定义,则会自动
在类中创建。
class Rectangle {constructor(width, height) {this.width = width;this.height = height;}getArea() {return this.width * this.height;}
}
const rect = new Rectangle(5, 10);
console.log(rect.getArea()); // 输出: 50
在上述的代码示例中,Rectangle类有一个构造函数,它接受width和height参数,并将它们分别赋值给对象的width和height属性。
三、super关键字和extends关键字
function Food(type, price){this.type = type;this.price = price;
}
Food.prototype.eat = function(){console.log("食物挺好吃!");
}
function Fruit(type, price, color){Food.call(this, type, price);this.color = color;
}
// 设置自己构造函数的原型
Fruit.prototype = new Food;
Fruit.prototype.constructor = Fruit;
Fruit.prototype.zhazhi = function(){console.log("将水果榨汁喝!");
}
let fruit = new Fruit("西瓜", 50, "绿色");
console.log(fruit); //Fruit {type: '西瓜', price: 50, color: '绿色'}
fruit.eat(); //食物挺好吃!
fruit.zhazhi(); //将水果榨汁喝!
在类继承中,当子类需要在自己的构造函数中进行一些初始化操作时,可以使用super
关键字来调用父类的构造函数。确保子类实例继承了父类的属性,并且可以在子类中进行进一步的初始化操作。
extends
关键字实现了JavaScript中的类继承,让子类能够继承父类中的所有属性和方法,并且能够添加自己的属性和方法进行扩展。
class Food{constructor(type, price){this.type = type;this.price = price;}// 父类的成员方法eat(){console.log("食物挺好吃!");}youzha(){console.log("油炸食物挺好吃!");}
}
class Fruit extends Food{constructor(type, price, color){super(type, price);this.color = color;}eat(){console.log("水果真好吃!"); //可以重写父类方法super.eat(); //调用父类方法}zhazhi(){console.log("将水果榨汁!");super.eat(); //调用父类方法}
}
let fruit = new Fruit("西瓜",50,"绿色");
console.log(fruit);
fruit.eat(); //水果真好吃! 食物挺好吃!
fruit.zhazhi(); //将水果榨汁! 食物挺好吃!
fruit.youzha(); //油炸食物挺好吃!
如上述代码所示,使用super(type, price)
调用父类的构造函数来初始化父类的属性,子类还有一个额外的属性color。子类Fruit使用extends
关键字继承了父类Food。fruit对象可以调用父类Food
的youzha方法,并且改写
eat方法。
四、Static静态方法和属性
function Fruit(){}
Fruit.NAME = "水果";
Fruit.eat = function(){console.log("水果真好吃!");
}
Fruit.prototype.type = "西瓜";
let fruit = new Fruit();
console.log(Fruit.NAME); //水果
console.log(Fruit.type); //undefined
Fruit.eat(); //水果真好吃!
console.log(fruit.type); //西瓜
console.log(fruit.NAME); //undefined
fruit.eat(); //Uncaught TypeError: fruit.eat is not a function
如上述代码所示,在函数中,NAME属性和eat方法是构造函数Fruit上的属性和方法,实例对象访问不到
,实例对象只能访问定义在函数原型上的type。只有构造函数自己才能访问到NAME属性和eat方法,但是访问不了定义在函数原型上的type。
class ShuiGuo{static type = "西瓜";static eat = function(){console.log("水果真好吃!");}
}
let shuiguo = new ShuiGuo();
console.log(ShuiGuo.type); //西瓜
ShuiGuo.eat(); //水果真好吃!
console.log(shuiguo.type); //undefined
shuiguo.eat(); //Uncaught TypeError: shuiguo.eat is not a function
在class中,可以使用static关键字定义静态方法。静态方法不需要实例化类就可以被调用,它们属于类本身而不是实例。可以通过类名去访问属性和方法,而实例对象访问不了。
五、getter和setter
在 JavaScript 的类中,我们可以使用 getter 和 setter 方法来定义属性的访问器。这允许我们在获取和设置属性值时执行自定义的逻辑。
Getter 方法用于获取属性的值,它被定义为一个没有参数的函数,并使用 get 关键字来声明。Getter 方法的命名通常与要获取的属性的名称相同。
Setter 方法用于设置属性的值,它被定义为一个带有一个参数的函数,并使用 set 关键字来声明。Setter 方法的命名通常与要设置的属性的名称相同。
class Fruit{get type(){console.log("type属性被读取了!");return "西瓜";}set type(newVal){console.log("type属性被修改了!");}
}
let fruit = new Fruit();
console.log(fruit.type); //type属性被读取了! 西瓜
fruit.type = "杨梅"; //type属性被修改了!
如上述代码所示,getter里的返回值就是读取fruit.type时的输出值。