《十八》E6+ 中的 class 类

类:

ES6 引入了 class 类的概念。通过 class 关键字,可以定义类,类名一般首字母大写。通过 new 命令来实例化类,可以得到实例对象。

class 类的正确写法:

  1. 由于属性占用的内存空间小,一般会将属性定义在实例对象上。
  2. 但是为了避免多个实例对象创建多个方法,一般会将方法定义在原型上。

ES6 中的 class 类本质上只是构造函数、原型链的语法糖。类中默认开启了严格模式。class 类不存在函数提升。
语法糖:语法糖的作用其实就是让写的代码更加简单,看起来也更容易理解。

类可以看做是实例对象的模板,用一个类可以创建出许多不同的实例对象。例如:人类。
实例对象可以看做是类的具象化。例如:某个人。

// 定义类
class Person {} // 声明形式
const Person = class {} // 表达式形式:将一个匿名类赋值给一个变量

// 实例化类
const p = new Person() // 正确
Person() // 错误。只能通过 new 来实例化类,不能直接调用

class 类本质上也是函数,因此也有显式原型。

console.log(typeof Person) // function
console.log(p.__proto__ === Person.prototype) // true
console.log(Person.prototype.constructor === Person) // true

constructor 构造方法:

constructor 构造方法是类的默认方法,通过 new 关键字调用类生成实例对象时,会自动执行该方法。可以写,也可以不写,不写的时候会被默认添加。

class Person{
	// 构造方法:new 实例化类的时候会自动执行。执行 constructor 时,和 new 构造函数时内部执行的操作一样
	constructor() {
		console.log('constructor')  // constructor
	}
}
const  p = new Person()

实例化类时传入的参数,会在 constructor 构造方法中接收。

class Person{
	constructor(name) {
		console.log(name) // Lee
	}
}
const  p = new Person('Lee')

constructor 构造方法中的 this 指向实例对象。

class Person{
	constructor(name) {
		// constructor 构造方法中的 this 指向实例对象,给 this 添加属性和方法就相当于给实例对象添加属性和方法
		this.name = name
	}
}
const  p = new Person('Lee')
console.log(p.name) // Lee

静态代码块:

ES13 开始,class 类支持静态块。以 static 开头的大括号就是静态代码块,在 JS 引擎解析 class 类的时候就会执行静态代码块,且只会执行一次。

class Person {
	// 并没有 new Person(),在解析阶段就会执行
	static {
		console.log('执行静态代码块')
	}
}

实例属性、实例方法:

实例属性、实例方法:定义在实例对象身上,通过实例对象来调用,每个实例对象身上都有自己的实例属性和实例方法。

class Person{
	constructor(name, age){
		// 实例属性、实例方法
		this.name = name
		this.age = age
		// 一般不将方法定义为实例方法。因为会直接定义在实例对象上,导致每一个生成的实例对象都有各自独立的方法,不是同一个引用,浪费内存空间
	    this.show = function(){
	    	console.log(`${this.name}:${this.age}`) // 通过实例对象来调用实例方法,因此,此处的 this 指向实例对象
	  }
	}
}

const p= new Person('Lee', 18)
p.show()

请添加图片描述

公共属性、公共方法:

ES13 新增了公共属性和公共方法:定义在实例对象身上,通过实例对象来调用,所有实例对象身上都有相同的公共属性和公共方法。

class Person {
  	// 公共属性、公共方法
	publicName = 'Tom'
	publicShow = function(){
		console.log(this.publicName)
	}
}

const p1 = new Person()
const p2 = new Person()
console.log(p1)
console.log(p2)

请添加图片描述

原型属性、原型方法:

原型属性、原型方法:定义在 class 类的显示原型对象上,会被实例对象继承,可以通过 构类.prototype 或者实例对象来调用。

class Person{
  // 原型方法:方法前面不需要加 function 关键字;方法之间不需要逗号分割
  // 一般将方法定义为原型方法。因为会定义在类的 prototype 上,所有实例对象都共享这一个方法。
  showSex1(){
     console.log(this.sex)
  }
}

// 原型属性、原型方法
Person.prototype.sex = '男'
Person.prototype.showSex2 = function() {
	console.log(this.sex)
}

const p = new Person('Lee', 18)
p.showSex()

请添加图片描述

静态属性、静态方法:

静态属性、静态方法:定义在类自身上,不会被实例对象继承,直接通过类来调用。

class Person{
  // 类的静态属性、类的静态方法:前面加上 static 关键字
  static sex = '男'
  static showSex(){
    console.log(this.sex) // 通过类来调用静态方法,因此,此处的 this 指向 Person 类
  }
}

 // 类的静态属性、类的静态方法
Person.hobby = '吃东西'
Person.showHobby = function(){
  console.log(this.hobby)
}

Person.showSex()

私有属性、私有方法:

私有属性、私有方法:定义的属性和方法只能在类的内部使用,不能在类的外部使用。

可以将一些不希望被外界访问、修改的属性和方法私有化。

ES6 中 class 类没有原生的私有属性和私有方法的语法,只能模拟实现。可以 用 _ 开头表示私有,但这只是一种约定俗成,并没有强制作用,只能一定程度上避免开发人员主观上的误操作。

class Person{
  _showSex(){
    console.log(this.sex)
  }
}

ES13 新增了私有属性和私有方法:以 # 开头的会被定义为私有属性和私有方法。

class Person {
	#privateName = 'Lee'
	#privateShow() {
		console.log(this.#privateName) // 可以访问
	}
   
   static #privateSex = '男'
}
const p = new Person()
console.log(p.#privateName) // 无法访问

访问器方法:

class Person {
	constructor() {
		// 定义一个私有变量
		this._name = ''
	}

	// setter 方法
	set name(value) {
		console.log('设置 name')
		this._name = value
	}

	// gettter 方法
	get name() {
		console.log('获取 name')
		return this._name
	}
 }
 var p = new Person()
 p.name = 'Lee'
 console.log(p.name)

类的属性名可以使用表达式,要用中括号括起来。

let method = 'show'
let str = 'Name'
class Person{
	[method+str](){
	   return 'Lee'
	}
}

let p = new Person()
console.log(p.showName())
console.log(p[method+str]())

类的继承:

class 类可以通过 extends 关键字来实现继承,父类的所有属性和方法都会被子类继承。

ES6 中的 class 类只支持单继承,也就是只能有一个父类。

// 父类
class Person{
	constructor(name, age){
		this.name = name
		this.age = age
	}
    show(){
       console.log(`${this.name}:${this.age}`)
    }

	 static sex = '男'
	 static showSex(){
	     console.log(this.sex)
	  }
}

// 子类
class Student extends Person {
	constructor(name, age) {
	   // 必须在子类的构造方法中调用父类的构造方法,调用 super() 就相当于是调用父类的 constructor()
		super(name, age)
	}
}

const s = new Student('Lee', 18)
console.log(s.name)
s.show()
console.log(Student.sex)
Student.showSex()

添加自己的属性和方法:

// 父类
class Person{
	constructor(name, age){
		this.name = name
		this.age = age
	}
}

// 子类
class Student extends Person {
	constructor(name, age, profession) {
		super(name, age)
		// 添加子类自己的实例属性。this 操作不能放在 super() 前面,因为此时子类还没有 this 对象
		this.profession = profession
	}

	// 添加子类自己的原型方法
	showProfession() {
		 console.log(this.profession)
	}
}
const s = new Student('Lee', 18, '学生')
console.log(s.profession) // 学生
p.showProfession()

改写继承的属性和方法:

如果在子类中添加了和父类相同的属性和方法,则子类方法会覆盖掉父类的属性和方法,这种称为方法重写。

class Person{
   speak(){
       console.log('p speak')
   }

	static speak(){
	   console.log('Person static speak')
	}
}

class Student extends Person {
	// 改写继承自父类的实例方法
	speak(){
       console.log('s speak')
    }

    // 改写继承自父类的静态方法
	static speak(){
	   console.log('Student static speak')
	}
}
const s = new Student()
s.speak() // s speak
Student.speak() // Student static speak

constructor 构造方法:

子类的 constructor 构造方法可以写,也可以不写。

  1. 如果不写的话,constructor 构造方法会被默认添加。

  2. 如果写的话,必须在 constructor 构造方法中明确调用 super 方法,且必须在使用 this 之前。

    这是因为子类没有自己的 this 对象,必须先通过调用父类的构造函数继承父类的 this 对象,然后再对其进行加工,加上子类自己的实例属性和方法,如果不调用 super 方法,子类就得不到 this 对象。

ES5 的继承,实质是先创造子类的实例对象 this,然后再将父类的方法添加到 this 上面(Parent.apply(this))。
ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到 this 上面(所以必须先调用 super 方法),然后再用子类的构造函数修改 this。

如果在子类中添加了和父类相同的属性和方法,则子类方法会覆盖掉父类的属性和方法,这种称为方法重写。
因此,如果在子类中明确写了 constructor 方法,则会覆盖掉父类的 constructor 方法,所以必须在子类的构造函数中手动地调用一下父类的构造函数,super 作为函数用在子类的构造函数之中时,代表的是父类的构造函数,因此调用一下 super() 即可。

class Student extends Person {
	constructor() {
	   super()	   
	}
}

super 关键字:

super 关键字既可以当做函数使用,也可以当做对象使用。在这两种情况下,它的用法完全不同,因此在使用 super 的时候,必须显式地指定 super 是作为函数还是作为对象使用,否则会报错。

super 作为函数时,只能用在子类的构造方法中,代表的是父类的构造方法。内部的 this 指向的是子类的实例。
super 作为对象时,在非静态方法中(构造方法或普通方法),代表的是父类的原型对象。通过 super 调用父类的方法时,方法内部的 this 指向的是子类的实例对象。
super 作为对象,在静态方法中,代表的是父类。通过 super 调用父类的方法时,方法内部的 this 指向的是子类。

class Person {}

class Student extends Person {
  constructor() {
    super()
    console.log(super) // 错误。必须显式地指定 super 是作为函数还是作为对象使用
  }
}
  1. super 作为函数时,只能用在子类的构造方法中,代表的是父类的 constructor 构造方法,用在其他地方就会报错。
    class Person {}
    
    class Student extends Person {
      constructor() {
        super() // 正确
      }
      
      show() {
         super() // 错误
      }
    }
    
    super() 虽然代表了父类的构造方法,但是内部的 this 指向的却是子类的实例,因此 super() 相当于父类.prototype.constructor.call(this)
    class Person{
    	constructor(){
    		console.log(this) // Student{}
    	}
    }
    
    class Student extends Person {
    	constructor() {
    		super()
    	}
    }
    const s = new Student()
    
  2. super 作为对象时,在非静态方法中(构造方法或其他普通方法),代表的是父类的原型对象;在静态方法中,代表的是父类。
    • super 作为对象时,在非静态方法中,代表的是父类的原型对象。
      // super 作为对象在构造方法中,代表的是父类的原型对象
      class Person {
        constructor(name) {
            this.name = name
        }
        show() {
          console.log(this.name)
        }
      }
      
      class Student extends Person {
        constructor(name) {
        	// 必须要先调用 super(),然后才能使用 super,否则会报错
          super(name)
          // 相当于 Person.prototype.show(),show 方法是定义在 Person 类的原型对象上,因此可访问到
          super.show() // Lee
          // 相当于 Person.prototype.name,name 是定义在 Person 类的实例对象上,因此访问不到
      	console.log(super.name) // undefined
        }
      }
      const s = new Student('Lee')
      
      // super 作为对象在普通方法中,代表的是父类的原型对象
      class Person {
      	constructor(name) {
      		this.name = name
      	}
      	show() {
      		console.log(this.name)
      	}
      }
      
      class Student extends Person {
      	extendShow() {
      		// 相当于 Person.prototype.show(),show 方法是定义在 Person 类的原型对象上,因此可访问到
      		super.show() // Lee
      		// 相当于 Person.prototype.name,name 是定义在 Person 类的实例对象上,因此访问不到
      		console.log(super.name) // undefined
      	}
      }
      const s = new Student('Lee')
      s.extendShow()
      
      在子类的非静态方法中通过 super 对象调用父类的方法时,父类方法内部的 this 指向的是子类的实例对象。
      class Person {
      	constructor() {
      		this.name = 'Person'
      	}
      	show() {
      		// 此时父类方法内部的 this 指向的是子类的实例对象
      		console.log(this.name) // Student
      	}
      }
      
      class Student extends Person {
      	constructor() {
      		super()
      		this.name = 'Student'
      	}
      	speak() {
      		// 在子类非静态方法中通过 super 对象调用父类的方法
      		super.show()
      	}
      }
      const s = new Student()
      s.speak()
      
    • super 作为对象,在静态方法中,代表的是父类。
      //  super 作为对象,在静态方法中,代表的是父类
      class Person {
      	show() {
      		console.log('show')
      	}
      
      	static show() {
      		console.log('static show')
      	}
      }
      
      class Student extends Person {
      	static speak() {
      		//  相当于 Person.show()
      		super.show()
      	}
      }	
      Student.speak() // static show
      
      在子类的静态方法中通过 super 调用父类的方法时,父类方法内部的 this 指向的是子类。
      class Person {
      	static name = 'Person'
      	static show() {
      		// 此时父类方法内部的 this 指向的是子类
      		console.log(this.name) // Student
      	}
      }
      
      class Student extends Person {
      	static name = 'Student'
      	static speak() {
      		// 在子类静态方法中通过 super 对象调用父类的方法
      		super.show()
      	}
      }
      Student.speak()
      
1. 箭头函数:箭头函数是ES6的一种新的函数语法。它相对于传统的函数表达式具有更加简洁的语法和更加方便的作用域绑定。例如: ``` // 传统的函数表达式 function (a, b) { return a + b; } // 箭头函数 (a, b) => a + b; ``` 2. 模板字符串:模板字符串是ES6的一种新的字符串语法。它允许在字符串使用表达式,并且可以跨越多行。例如: ``` const name = "Tom"; const message = `Hello ${name}, How are you today?`; ``` 3. let 和 const:let和const是ES6的新的变量声明方式。它们提供了块级作用域,并且const还可以用于声明常量。例如: ``` let x = 10; const y = 20; if (x === 10) { let x = 20; const y = 30; console.log(x, y); // 20, 30 } console.log(x, y); // 10, 20 ``` 4. 解构赋值:解构赋值是ES6的一种新的语法。它允许从对象或数组提取值,并将这些值赋给变量。例如: ``` const obj = {a: 1, b: 2}; const {a, b} = obj; console.log(a, b); // 1, 2 const arr = [1, 2, 3]; const [x, y, z] = arr; console.log(x, y, z); // 1, 2, 3 ``` 5. 和继承:ES6引入了和继承的概念。是一种定义对象的模板,它包含数据和方法。继承允许一个继承另一个的特性。例如: ``` class Animal { constructor(name) { this.name = name; } speak() { console.log(`${this.name} makes a noise.`); } } class Dog extends Animal { constructor(name) { super(name); } speak() { console.log(`${this.name} barks.`); } } const dog = new Dog('Fido'); dog.speak(); // "Fido barks." ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值