Class类
1、简介
JavaScript 语言中,生成实例对象的传统方法是通过构造函数。 ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。基本上,ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰更像面向对象编程的语法而已。所以ES6 的类,完全可以看作构造函数的另一种写法。例如:
ES5中写法
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype.speak = function(){
console.log("我可以说话!!");
}
let p1 = new Person("小明",29);
p1.speak();
console.log(p1);
在ES6中可以写成
class Person{
constructor(name,age){
this.name = name;
this.age = age;
}
speak(){
console.log("我可以说话!!");
}
}
let p1 = new Person("小明",29);
p1.speak();
console.log(p1);
2、构造函数construct
constructor 方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。
class Person{
}
class Person{
constructor(){}
}
3、实例属性和方法
定义在类体中的属性称为实例属性,定义在类体中的方法称为实例方法。实例方法本质上是在prototype原型中的,所以所有的实例都可以调用。
class Person{
temp = 'hello';
constructor(name,age){
this.name = name;
this.age = age;
this.sayName = function(){
console.log("i am ",this.name);
}
}
}
let p1 = new Person('Ronda',22);
let p2 = new Person('Fairy',23);
console.log(p1.sayName === p2.sayName);
console.log(p1.name);
console.log(p1.temp);
p1.sayName();
console.log(p2.name);
console.log(p2.temp);
p2.sayName();
学到这里可以发现js和java语言的形式越来越像了
4、原型属性和方法
就是在构造函数的原型对象上存在的属性和方法。
class Person{
constructor(name,age){
this.name = name;
this.age = age;
}
sayName(){
console.log("i am ",this.name);
}
}
Person.prototype.temp = "hello";
let p1 = new Person('Randa',22);
let p2 = new Person('fairy',23);
p1.sayName();
console.log(p1.sayName === p2.sayName);
console.log(p1.temp);
console.log(p2.temp);
5、静态属性和方法
通过static 关键字来定义静态属性和静态方法。静态属性和静态方法是定义在类上的,所以可以通过类直接访问。在静态方法中,this 指向当前类。
class Person {
static num = 200;
static number() {
console.log(this);
return this.num;
}
}
console.log(Person);
console.log(Person.number());
console.log(Person.num);
6、继承
class 可以通过extends 关键字实现继承,这比 ES5 的通过修改原型链实现继承,要简洁很多。 子类中可以没有构造函数,系统会默认提供一个。子类如果提供了构造函数就必须显示调用super。super函数类似于之前的借用构造函数,并且子类必须在constructor 方法中调用super 方法,否则新建实例时会报错。因为子类自己的this 对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super 方法,子类就得不到this 对象
class Animal {
constructor(name){
this.name = name;
}
sayName(){
console.log("my name is ",this.name);
}
}
class Dog extends Animal{
constructor(name,age){
super(name);
this.age = age;
}
}
let dog = new Dog('乐乐','1');
dog.sayName();
console.log(Dog);
console.log(Dog.prototype);
console.log(Object.getPrototypeOf(Dog));
console.log(Object.getPrototypeOf(Dog.prototype));
console.log(Dog.__proto__ === Animal);
console.log(Dog.prototype.__proto__ === Animal.prototype);
class 作为构造函数的语法糖,同时有prototype属性和__proto__属性,因此同时存在两条继承链 1.子类的__proto__属性,表示构造函数的继承,总是指向父类。 2.子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性。
下面是ES5中的对象继承的实现
function Phone(brand,price){
this.brand = brand;
this.price = price;
}
Phone.prototype.call = function(){
console.log("打电话");
}
function SmartPhone(brand,price,color,size){
Phone.call(this,brand,price);
this.color = color;
this.size = size;
}
SmartPhone.prototype = new Phone;
SmartPhone.prototype.constructor = SmartPhone;
SmartPhone.prototype.photo = function(){
console.log("拍照");
}
SmartPhone.prototype.playGames = function(){
console.log("打游戏");
}
const oppo = new SmartPhone("oppo",2000,"white",'5.5inch');
console.log(oppo);
结果: SmartPhone { brand: ‘oppo’, price: 2000, color: ‘white’, size: ‘5.5inch’ }
super关键字使用
super 这个关键字,既可以当作函数使用,也可以当作对象使用。在这两种情况下,它的用法完全不同。
- 第一种情况,
super 作为函数调用时,代表父类的构造函数。ES6 要求,子类的构造函数必须执行一次super 函数。
class A {}
class B extends A {
constructor() {
super();
}
}
- 第二种情况,
super 作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。
class A {
p() {
return 2;
}
}
class B extends A {
constructor() {
super();
console.log(super.p());
}
}
let b = new B();
需要注意,由于super 指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过super 调用的,例如:
class A {
constructor() {
this.p = 2;
}
}
class B extends A {
m() {
return super.p;
}
}
let b = new B();
console.log(b.m())
console.log(b.p);
例如super指向父类本身的情况
class A {
static num = 200;
constructor() {
this.num = 20;
}
static number() {
return this.num;
}
}
class B extends A {
static getNumber() {
return super.number();
}
}
console.log(B.getNumber());
7、子类对父类方法的重写
class Phone{
constructor(brand,price){
this.brand = brand;
this.price = price;
}
call(){
console.log("我可以打电话");
}
}
class SmartPhone extends Phone{
constructor(brand,price,color){
super(brand,price);
this.color = color;
}
call(){
console.log("我可以打视频电话");
}
}
const oppo = new SmartPhone("oppo","2000","white");
oppo.call();
console.log(oppo);
需要注意: (1)子类并不能使用super关键字直接调用父类中的成员方法 (2)子类只能完完全全的重写父类中的成员方法,并且在调用时调用的是重写后的方法
8、setter和getter
和java中的get方法和set方法类似,get获取值,set设置值,并且是动态的
class Phone{
get price(){
console.log("价格属性被读取了");
return '返回值';
}
set price(price){
console.log("价格属性被修改了");
}
}
let phone = new Phone();
phone.price = 'free';
console.log(phone.price);
结果: 价格属性被修改了 价格属性被读取了 返回值
9、私有属性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
class Person {
name;
#age;
#weight;
constructor(name, age, weight) {
this.name = name;
this.#age = age;
this.#weight = weight;
}
}
const girl = new Person("小红", 12, '45kg');
console.log(girl);
console.log(girl.name);
console.log(girl.#age);
console.log(girl.#weight);
girl.intro();
</script>
</body>
</html>
|