JavaScript 类

类觉不爱【大误】


系列文章

这是JavaScript系列文章:



知识点

思维导图

什么是类?

JavaScript中的类是语法糖,一种基于原型的继承的语法糖。

类有什么作用?

类是面向对象编程的必备条件。

怎么使用类?

使用new来使用一个之前已经声明好的类。
类里面的方法体不需要添加逗号或者分号隔离

声明

class(类声明)

  1. 基础语法

    // extends是继承的语法,书写后 `子类 extends 父类`
    class name [extends] {
      // class body
    }
    
  2. 构造函数可选

  3. 类声明不可以提升
    与函数声明不同的是类声明不能提升,所以类声明必须先定义,而函数声明可以先使用再定义。

  4. 示例一

    class Human {
      constructor(name, weight, height) {
      	this.name = name;
      	this.weight = weight;
      	this.height = height;
        this.bmi = weight / height / height;
      }
    }
    
    console.log('bmi: ' + new Human('匿旅', 71, 1.72).bmi);
    // expected output: bmi: 23.99945916711736
    
  5. 示例二

    class Human {
    	constructor(weight, height) {
    		this.name = '人类';
    		this.weight = weight;
    		this.height = height;
    		this.bmi = weight / height / height;			
    	}
    }
    
    class Programmer extends Human {
    	constructor(weight, height) {
    		super(weight, height);
    		this.name = '程序员';
    	}			
    }	
    
    let iter = new Programmer(71, 1.72);
    console.log('name: ' + iter.name);  // name: 程序员
    console.log('bmi: ' + iter.bmi);  // bmi: 23.99945916711736
    
  6. 注意

    • 不要重复声明一个类会报错
      class Human { }
      class Human { }
      
    • 类表达式定义一个类后再声明一个类同样会报错
      let Human = class { }
      class Human { }
      
      OR
      class Human { }
      let Human = class { }
      
    • 报错图示
      重复声明类报错

类表达式

  1. 基础语法
    /** 
     * className 写的话只能在后面的类体内访问到,不写的话就是一个匿名
     * exteds 继承父类
     * 这个类可以理解为名字为 MyClass
     */ 
    const MyClass = class [className] [extends] {
      // class body
    };
    
  2. 示例一:使用类表达式
    let World = class {
    	constructor() { }
    	bar () {
    		return 'Hello World!';
    	}
    }
    
    let instance = new World();
    instance.bar();  // "Hello World!"
    
  3. 示例二:命名类表达式
    NamedWorld是不对外的,可以理解为一个私有类名,在外部无法访问,对外访问只能通过World或者new World()使用
    let World = class NamedWorld {
    	constructor() { }
    	whoIsThere() {
    		return NamedWorld.name;
    	}
    }
    
    let asia = new World();
    
    asia.whoIsThere();
    
    NamedWorld.name;  // 会报错
    	
    World.name;
    

class(类声明) 和 类表达式 区别

  1. 类声明必须有类名,类表达式可以没有类名。

构造方法

  1. constructor 创建初始化 class创建对象的特殊方法,类似JAVA、C#的构造函数,只不过javascript需要借助关键字constructor .
    一个类中只能有 一个 构造方法constructor
    JavaScript内的构造方法是可选的,可以不写构造函数,这样JavaScript会声明一个默认的构造函数。

  2. 基础语法

    constructor([arguments]) { ... }
    
  3. 默认构造函数

    • 基类构造函数
      consturctor() { }
      
    • 派生类构造函数
      constructor(...args) { 
      	super(...args);
      }
      
  4. 示例一

    • 问题:如何调用 getset 函数?
      • get
        直接使用 实例对象.属性
        let p = new Parent();
        console.log(p.age);
        
      • set
        直接赋值 实例对象.属性 = xxx
        let p = new Parent();
        p.age = 23;
        
    class Polygon {
    	// ..and an (optional) custom class constructor. If one is
      	// not supplied, a default constructor is used instead:
      	// constructor() { }
      	constructor(height, width) {
        	this.name = 'Polygon';
    	    this.height = height;
    	    this.width = width;
      	}
    }
    
    class Square extends Polygon {
    	constructor(length) {		
    		// 在这里, 它调用了父类的构造函数, 并将 length 提供给 Polygon 的"width"和"height"	
    		super(length, length);
    		
    		// 注意: 在派生类中, 必须先调用 super() 才能使用 "this"。
        	// 忽略这个,将会导致一个引用错误。
    		this.name = '正方形';
    	}
    	
    	get area () {
    		return this.height * this.width;
    	}
    		
    	set area(value) {
    		// 注意:不能使用 this.area = value 为area赋值
    		// 否则会导致循环call setter方法导致爆栈
    		this._area = value;
    	}
    }
    
  5. 示例二

    polygon
    prototype

    Square 类的原型被改变,但是在正在创建一个新的正方形实例时,仍然调用前一个基类 Polygon 的构造函数。

    class Polygon {
    	constructor() {
    		this.name = 'Polygon';
    	}
    }
    
    class Square extends Polygon {
    	constructor() {
    		super();
    	}
    }
    
    class Rectangle { }	
    
    Object.setPrototypeOf(Square.prototype, Rectangle.prototype);
    
    console.log(Object.getPrototypeOf(Square.prototype) === Polygon.prototype);  // false
    console.log(Object.getPrototypeOf(Square.prototype) === Rectangle.prototype);  // true
    
    let newInstance = new Square();
    console.log(newInstance.name);  // Polygon
    

super

  1. 简介

    • super关键字 至多出现 一次
    • 必须在使用 this 关键字之前使用
    • super关键字 也可以用来调用父对象上的函数。
  2. 基础语法

    // 调用 父对象/父类 构造函数
    super([ arguments ]);	
    
    // 调用 父对象/父类 上的方法
    super.functionOfParent([ arguments ]);
    
  3. 调用父类上的静态方法

    class Human {
    	constructor() { }
    	static ping() {
    		return 'ping';
    	}
    }
    
    class Computer extends Human {
    	constructor() { }
    	static pingpong() {
    		return super.ping() + ' pong';
    	}
    }
    
    Computer.pingpong();  // "ping pong"
    
  4. 不能使用 delete 删除父类的属性

    class Base {
    	constructor() { }
    	foo() { }
    }
    
    class Derived extends Base {
    	constructor() { }
    	delete () {
    		delete super.foo;
    	}
    }
    
    new Derived().delete();
    

extends

  1. 简介
    类声明类表达式 都可以使用 extends ,用来创建一个子类继承另一个类

    • 还有一段我开始不太懂得话:

      • 继承的.prototype必须是一个Object 或者 null。
        let Parent = 8; // Number/Boolean/String
        
        class Child extends Parent {}  // 这里会报错
        
        继承的原型必须是对象类型或者null
  2. 示例一
    继承内置的 Date 对象

    class MyDate extends Date {
    	constructor() {
    		super();
    	}
    
    	getFormattedDate() {
    		let months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
    		return this.getDate() + "-" + months[this.getMonth()] + "-" + this.getFullYear();
    	}
    }
    
  3. 示例二

    class nullExtends extends null {
    	constructor() { }
    }
    
    Object.getPrototypeOf(nullExtends);
    Object.getPrototypeOf(nullExtends.prototype);
    
    new nullExtends();
    

static

static

  1. 简介

    • static 关键字声明静态方法
    • 不能在类的实例上调用静态方法
    • 只能使用类本身调用静态方法
  2. 基础语法
    static methodName() { ... }

  3. 示例一:同一个类中调用其他静态方法

    class StaticMethods {
    	static outputLog() {
    		return 'output log';
    	}
    	
    	static debug() {
    		return this.outputLog() + ' debug';
    	}
    }
    
    // 使用
    StaticMethods.outputLog();  // "output log"
    
    StaticMethods.debug();  // "output log debug"
    
  4. 示例二:在构造函数或者其他方法内调用静态方法

    class Parent {
    	constructor() {
    		console.log(Parent.staticMethod());
    		console.log(this.constructor.staticMethod());
    	}
    
    	static staticMethod() {
    		return 'static';
    	}
    }
    
    new Parent();	// 调用
    
  5. 示例三:子类静态方法调用父类静态方法

    class Parent {
    	static staticMethod() {
    		return 'parent static';
    	}
    }
    
    class Child {
    	static staticMethod1() {
    		return super.staticMethod + ' child static';
    	}
    }
    
    console.log(Parent.staticMethod());
    console.log(Child.staticMethod1());
    

鸣谢

我们之所以看得远,是因为我们站在了巨人的肩膀上。参考列表:

  1. 阮一峰
  2. MDN
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值