本文是 ES6
系列的第四篇,能够在 这里 查看 往期全部内容javascript
这篇文章主要记录了一些 class
相关的内容,都是咱们平常开发中可能会遇到的知识点java
若是文章中有出现纰漏、错误之处,还请看到的小伙伴多多指教,先行谢过git
如下↓es6
ES6
以前,咱们生成实例对象的方法都是经过构造函数github
function Person(name) {
this.name = name
}
Person.prototype.say = function() {
console.log(this.name)
}
var p = new Person('游荡de蝌蚪')
复制代码
ES6
引入了 类
的概念,经过 class
关键字来定义。上面的代码就能够这样改写web
class Person {
constructor(name) {
this.name = name
}
say() {
console.log(this.name)
}
}
let p = new Person('游荡de蝌蚪')
复制代码
class
的这种写法更接近传统语言,不管是对某个属性设置存储函数和取值函数,仍是实现继承,都要更加清晰和方便编程
类的本质是一个函数,类自身指向的就是构造函数segmentfault
class Person {}
typeof Person // function
Person.prototype.constructor == Person // true
复制代码
ES6
的 class
能够看做只是一个语法糖,它的绝大部分功能,ES5
均可以作到,新的 class
写法只是让对象原型的写法更加清晰、更像面向对象编程的语法微信
类的构造方法 constructor
对应的就是构造函数,定义在类中的方法都定义在类的 prototype
属性上面babel
class Person {
constructor(){},
say() {},
run() {}
...
}
// 等同于
Person.prototype = {
constructor(){},
say(){},
run(){}
...
}
let p = new Person()
p.say == Person.prototype.say // true
复制代码
console.log(Person)
class Person {}
// Uncaught ReferenceError:
// Cannot access 'Person' before initialization
复制代码
let Person = {}
class Person {}
// Uncaught SyntaxError:
// Identifier 'Person' has already been declared
复制代码
constructor
是类的默认方法,就算不定义,也会默认添加一个空的 constructor
new
关键字初始化,不然会报错class Person {}
let p1 = new Person()
let p2 = new Person()
p1.__proto__ == p2.__proto__ //true
复制代码
prototype
默认不可重写class Person {}
Object.getOwnPropertyDescriptor(Person, 'prototype')
/*
configurable: false
enumerable: false
value: {constructor: ƒ}
writable: false
*/
复制代码
class Person {
static say() {
console.log('Hello')
}
}
复制代码
class
中若是一个方法前添加了 static
关键字,那么就表示这个方法是静态方法。它不能被实例继承,只能经过类来调用
let p = new Person()
p.say() // Uncaught TypeError: p.say is not a function
Person.say() // Hello
复制代码
须要注意的是:父类的静态方法,能够被子类继承
class Tadpole extends Person {}
Tadpole.say() // Hello
复制代码
所谓静态属性,就是类自己的属性,也就是说属性不能经过添加在 this
上的方式定义
// 属性不能定义在 this 上面,会被实例继承
class Person {
constructor(name) {
this.name = name
}
}
复制代码
ES6
明确规定,Class
内部只有静态方法,没有静态属性
通常状况下,咱们实现静态属性的方式就是直接将属性添加到类上面:
Person.age = 18
复制代码
可也以用一种变通的方式实现:
// 经过 static 静态方法与 get 方式的结合
class Person {
static get age() {
return 18
}
}
复制代码
类的静态属性和静态方法的表现基本一致:不能被实例继承,只能经过类来使用
提案 提供了一种类静态属性的另外一种方式,也是使用 static
关键字
class Person {
static name = 18
}
复制代码
所谓私有,通常须要具有如下特征:
class
内部访问,不能在外部使用ES6
并无提供 class
的私有属性及方法的实现方式,可是咱们能够经过如下几种方式来约定
_
,可是这种方式不是很保险,由于在类的外部仍是能够访问到这个方法class Person {
// 公有方法
fn(age) {
this.age = age
}
// 私有方法
_foo(age){
return this.age = age
}
}
复制代码
this
创造出一个相对封闭的空间class Person{
foo(age) {
bar.call(this, age)
}
}
function bar(age) {
return this.age = age
}
复制代码
提案 提供了一种实现 class
私有属性的方式:使用 #
关键字
class Person {
#age = 18
}
复制代码
若是咱们在外部使用这个属性就会报错
let p = new Person()
Person.#age
p.#age
// Uncaught SyntaxError:
// Private field '#age' must be declared in an enclosing class
复制代码
另外,私有属性也支持 getter
和 setter
的方式以及静态 static
的方式
class Person {
#age = 18
get #x() {
return #age
}
set #x(value) {
this.#age = value
}
static #say() {
console.log(#age)
}
}
复制代码
在 class
出现以前,咱们通常都会使用原型以及构造函数的方式实现继承,更多实现继承的方式参考 JavaScript中的继承
类的继承也是经过原型实现的
ES5
的继承,实质是先创造子类的实例对象this
,而后再将父类的方法添加到this
上面
ES6
的继承,实质是先将父类实例对象的属性和方法,加到this
上面(因此必须先调用super
方法),而后再用子类的构造函数修改this
class
经过extends
关键字实现继承
经过 extends
关键字,子类将继承父类的全部属性和方法
class Person {}
class Tadpole extends Person {}
复制代码
extends
关键字后面不只能够跟类,也能够是表达式
function Person(){
return class {
say(){
alert('Hello')
}
}
}
class Tadpole extends Person(){}
new Tadpole().say() // Hello
复制代码
extends
关键字后面还能够跟任何具备 prototype
属性的函数(这个特性可让咱们很轻松的复制一个原生构造函数,好比 Object
)
function Fn() {
this.name = 'tadpole'
}
// 注意,这里 constructor 的指向变了
Fn.prototype = {
say() {
console.log('My name is tadpole')
}
}
class Tadpole extends Fn {}
let t = new Tadpole()
t.name // tadpole
t.say() // My name is tadpole
复制代码
子类经过继承会获取父类的全部属性和方法,因此下面的写法能够获得正确的结果
class Person {
constructor() {
this.name = '游荡de蝌蚪'
}
}
class Tadpole extends Person{}
let t = new Tadpole()
t.name // 游荡de蝌蚪
复制代码
可是,若是咱们在子类中定义了 constructor
属性,结果就是报错
class Tadpole extends Person {
constructor(){}
}
let t = new Tadpole()
// Must call super constructor in derived class before accessing 'this' or returning from derived constructor
复制代码
若是咱们想要在子类中定义 constructor
属性,那么就必须调用 super
方法
// 正常
class Tadpole extends Person {
constructor(){
super()
}
}
let t = new Tadpole()
复制代码
super表明了父类的构造函数,返回的是子类的实例,至关于 Person.prototype.constructor.call(this)
因此,上面代码中 super()
的做用实际上就是将 this
添加到当前类,而且返回
super
有两种使用方式:
super()
只能用在子类的构造函数之中,用在其余地方就会报错
super
指向父类的原型对象,因此定义在父类实例上的方法或属性,没法经过 super
调用)class Person {
constructor() {
this.name = '游荡de蝌蚪'
}
say() {
console.log('My name is' + this.name)
}
}
class Tadpole extends Person {
constructor() {
super()
console.log(super.say()) // My name is 游荡de蝌蚪
console.log(super.name) // undefined
}
}
复制代码
class Person {
constructor() {
this.name = '游荡de蝌蚪'
}
say() {
console.log('My name is' + this.name)
}
static say() {
console.log('My name in tadpole')
}
}
class Tadpole extends Person {
static say() {
super.say()
}
say() {
super.say()
}
}
Person.say() // My name is tadpole
Tadpole.say() // My name is tadpole
let t = new Tadpole()
t.say() // My name is 游荡de蝌蚪
复制代码
this
,默认指向类的实例class Person {
say() {
this.run()
}
run() {
console.log('Run!')
}
}
复制代码
this
关键字,这个 this
指的是类,而不是实例class Person {
static say() {
console.log(this) // Person
}
}
复制代码
constructor
中调用 super()
以后才能使用 this
class
中默认使用严格模式,若是将其中的方法单独调用,那么方法中的 this
指向 undefined
(默认指向全局对象)class
的出现为咱们的编程提供了不少便利,可是 class
自己也存在一些问题
class
,居然还只是 语法糖
,你说气人不气人prototype
,因此 class
也存在原型所具备的一些问题,好比修改父类上面的属性可能会影响到全部子类(固然,私有属性的出现仍是解决了一些问题)class
,好比那个啥啥啥,固然了也有解决的方式:babel
尽管 class
仍是存在些许问题,但它必定会愈来愈丰富…
以上就是关于 class
的所有内容,但愿对看到的小伙伴有些许帮助
大胆地在你的项目中使用 class
吧,相信你绝对会爱上它
感兴趣的小伙伴能够 点击这里 ,也能够扫描下方二维码关注个人微信公众号,查看往期更多内容,欢迎 star
关注