es6 class 语法(实现继承的语法糖)

什么是类?

用通俗易懂的话来说,手机就是一个类,而你手的手机就是这个类的对象;电脑是一个类,而你的电脑就是这个类的对象。也就是说类是一个种类,而对象就是这个类中一个具体的个体。类相当于实例的原型,所有在类中定义的方法,都会被实例继承。

类是面向对象语言的一个概念(比如java),js 中生成实例对象的传统方法是通过构造函数,然后通过原型实现继承。es6 提供了 class 语法糖,让对象原型的写法更加清晰、更像面向对象编程的语法。

使用 class 关键字定义一个类

class Dog {
	constructor(name, age, color) {
		this.name = name
		this.age = age
		this.color = color
	}
	
	handleDetails() {
		return '姓名:'+this.name+', 年龄:'+this.age+'岁, 颜色:'+this.color
	}
}

狗是一个类,某一只狗就是这个类的对象。上面代码定义了一个类,constructor() 就是构造方法,this 关键字则代表实例对象,类里面定义了一个方法。

使用 new 关键字实例化一个对象

const dog = new Dog('大黄', 3, '黄色');
const myDog = new Dog('小虎', 5, '棕色');

dog.handleDetails() // "姓名:大黄, 年龄:3岁, 颜色:黄色"
myDog.handleDetails() // "姓名:小虎, 年龄:5岁, 颜色:棕色"

使用 new 关键字创建一个对象,对象可以使用类中的方法。

constructor 方法

constructor() 是类的默认方法,new 一个对象时会调用该方法,一个类必须要有 constructor() 方法,如果不写,则会默认添加此方法。

class B {
}

// 等同于
class B {
	constructor() {}
}

实例的属性除非显式定义在其本身(即定义在this对象上),否则都是定义在原型上(即定义在class上)。

//定义类
class Dog {

  constructor(name, color) {
    this.name = name;
    this.color= color;
  }

  toString() {
    return '名字:' + this.name + ',  颜色:' + this.color;
  }

}

var dog = new Dog('哮天犬', '黑色');

dog.toString() // '名字:哮天犬,  颜色:黑色'

dog.hasOwnProperty('name') // true
dog.hasOwnProperty('color') // true
dog.hasOwnProperty('toString') // false
dog.__proto__.hasOwnProperty('toString') // true

上面代码中,name 和 color 都是实例对象 dog 自身的属性(因为定义在 this 对象上),而 toString() 是原型对象的属性(因为定义在 Dog 类上)

var dog1 = new Dog('大黄', '黄色')
dog.__proto__ === dog1.__proto__  // true

上面代码中,dog1 和 dog 都是 Dog 的实例,它们的原型都是 Dog.prototype,所以__proto__属性是相等的。

这也意味着,可以通过实例的__proto__属性为“类”添加方法

class 与 es5 构造函数的关系

1. 生成实例对象的不同方式

// es6 class写法
class Dog {
	constructor(name, age, color) {
		this.name = name
		this.age = age
		this.color = color
	}
	
	handleDetails() {
		return '姓名:'+this.name+', 年龄:'+this.age+'岁, 颜色:'+this.color
	}
}

// 生成实例对象
const d = new Dog('大黄', 3, '黄色')
d.handleDetails() // "姓名:大黄, 年龄:3岁, 颜色:黄色"
// es5 构造函数写法
function Dog(name, age, color) {
	this.name = name
	this.age = age
	this.color = color
}

Dog.prototype.handleDetails = function() {
	return '姓名:'+this.name+', 年龄:'+this.age+'岁, 颜色:'+this.color
}

// 生成实例对象
const d = new Dog('啸天犬', '3000', '黑色')
d.handleDetails() // "姓名:啸天犬, 年龄:3000岁, 颜色:黑色"

从上面代码可以看出,ES6 的类,完全可以看作构造函数的另一种写法。

2.类的 prototype 属性

class B {
	constructor() {}
	toString() {}
	handleValue() {}
}

typeof B // "function"

B === B.prototype.constructor // true

B.prototype = {
	constructor() {},
	toString() {}
	handleValue() {}
}

new b = new B()
b.constructor === B.prototype.constructor // true
  • 类的数据类型就是函数
  • 构造函数的 prototype 属性,在 ES6 的类上面继续存在
  • 类的本身就是构造函数
  • 类的所有方法都定义在类的 prototype 属性上面,在类的实例上面调用方法,其实就是调用原型上的方法
  • b是B类的实例对象,它的constructor()方法就是B类原型的constructor()方法。

3. 调用类和调用构造函数的区别

// 类
class Foo {
	constructor() {
		this.name = name
	}
}

// 构造函数
function Bar(name) {
	this.name = name
}

var foo = Foo('job') // Uncaught TypeError: Class constructor Foo cannot be invoked without 'new' at <anonymous>:1:1
var bar = Bar('job') // undefined

类必须使用 new 调用,否则会报错,构造函数不用 new 也可以执行。

静态方法

类中定义的方法都会被实例(对象)继承,如果在一个方法前加上 static 关键字,那么该方法不会被对象继承(可以被子类继承),而是直接通过类来调用,这就是静态方法。类中的公共方法都可以定义为静态方法。

class Dog {
  static getLegs() {
    return '狗有四条腿'
  }
}

Dog.getLegs() // '狗有四条腿'

var dog = new Dog()
dog.getLegs() // TypeError: dog.getLegs is not a function

静态属性

静态属性与静态方法一样,都是 class 本身的属性,都通过类直接调用

class Dog {
  static eyesProp = 2
}

Dog.eyesProp // 2

var dog = new Dog()
dog.eyesProp // undefined

私有属性和私有方法

私有属性和私有方法,只能在类的内部访问,外部不能访问。在属性或者方法名前加上符号 #就表示这是个私有属性或方法。

class Dog {
  #name = '大黄' // 私有属性
  #getName() { // 私有方法
    return '它的名字叫' + this.#name
  }
  printName() {
    console.log(this.#getName())
  }
}

var dog = new Dog()
dog.printName() // 它的名字叫大黄
dog.#name // 报错
dog.#getName() // 报错

Class 的继承

es5 是通过原型链实现继承,Class 通过extends实现继承

class Dog {
  constructor() {
    this.eyes = 'two'
  }
  getLegs() {
    return '狗有四条腿'
  }
}

class SmallDog extends Dog {
}

var smallDog = new SmallDog()
smallDog.eyes // two
smallDog.getLegs() // '狗有四条腿'

上面代码中,子类继承了父类的属性和方法

继承的时候,必须在子类的constructor中调用super方法,否则会报错。因为ES6 的继承机制是先新建父类的this对象,得到与父类同样的实例属性和方法,然后再对其加工创建子类的this。而super表示的就是父类的构造函数,是用来新建父类的this对象的,如果不调用super方法,子类就得不到this对象,如果子类没有写constructor方法,这个方法会被默认添加。

class SmallDog extends Dog {
}

// 等同于
class SmallDog extends Dog {
  constructor() {
    super()
  }
}

因为子类的this对象是通过父类的构造函数完成塑造的,所以子类调用this应该写在super方法之后

class Dog {
  constructor() {
    this.eyes = 'two'
  }
  getLegs() {
    return '狗有四条腿'
  }
}

class SmallDog extends Dog {
  constructor() {
    this.color = 'yellow' // ReferenceError
    super()
    this.color = 'yellow' // 正确
  }
  getSmallDogLegs() {
    return super.getLegs() + ',但是小型犬的腿很短' // '狗有四条腿,但是小型犬的腿很短'
  }
}

super方法应该写在constructor方法的第一行

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值