ES6中的class关键字
欢迎访问我的博客 ,祝码农同胞们早日走上人生巅峰,迎娶白富美~~~
声明 :本文参考业界大佬阮一峰老师的ES6标准入门
ES5 中的面向对象
1 2 3 4 5 6 7 8 9 10 11 12 13 function People (name, age ) { this .name = name this .age = age } People.prototype.say = function ( ) { return 'hello everyone' } var a = new People('zhangsan' , 10 )
ES6 中的 class 关键字
用 ES6
中的 class
类去改写上面的例子看看
1 2 3 4 5 6 7 8 9 class People { constructor (name, age) { this .name = name this .age = age } say() { return 'hello everyone' } }
两个例子可以说实际功能一模一样,可以看到里面有一个constructor
方法,这就是构造方法,而this
关键字则代表实例对象,且方法之间不需要逗号分隔,加了会报错
ES6
的 class
类,完全可以看作构造函数的另一种写法,证明如下:
1 2 3 class People { }typeof People People.prototype.constructor === People
类的实例
同样的,也是用new
关键字
1 2 3 4 5 6 7 8 9 10 class People { constructor (name, age) { this .name = name this .age = age } say() { return 'hello everyone' } } var people = new People('zhangsan' , 18 )
与 ES5
一样,实例的属性除非显式定义在其本身(即定义在this
对象上),否则都是定义在原型上(即定义在class
上)
类的所有实例共享一个原型对象
1 2 3 4 5 6 7 8 9 10 11 12 class People { constructor (name, age) { this .name = name this .age = age } say() { return 'hello everyone' } } var people1 = new People('zhangsan' , 18 )var people2 = new People('zhaosi' , 20 )people1._proto_ === people2._proto
这就意味着可以通过实例的__proto__
属性为“类”添加方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class People { constructor (name, age) { this .name = name this .age = age } say() { return 'hello everyone' } } var people1 = new People('zhangsan' , 18 )var people2 = new People('zhaosi' , 20 )people1._proto_.eat = function ( ) { return 'banner' } people1.eat() people2.eat()
取值函数(getter)和存值函数(setter)
案例借用阮一峰老师的《ES6标准入门》
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class CustomHTMLElement { constructor (element) { this .element = element; } get html() { return this .element.innerHTML; } set html(value) { this .element.innerHTML = value; } } var descriptor = Object .getOwnPropertyDescriptor( CustomHTMLElement.prototype, "html" ); "get" in descriptor "set" in descriptor
存值函数和取值函数是定义在html
属性的描述对象上面,这与 ES5
完全一致
一些需要注意的点
不存在变量提升
类不存在变量提升
1 2 new Foo(); class Foo {}
如上代码,定义Foo
在后,使用在前,则报错
name
由于本质上,ES6
的类只是ES5
的构造函数的一层包装,所以函数的许多特性都被Class
继承,包括name
属性
1 2 class People {}People.name
name
属性总是返回紧跟在class
关键字后面的类名
Generator 方法
如果某个方法之前加上星号(*
),就表示该方法是一个 Generator 函数
generator
可以在执行过程中多次返回,所以它看上去就像一个可以记住执行状态的函数,利用这一点,写一个generator
就可以实现需要用面向对象才能实现的功能,详细请移步廖雪峰老师的generator
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Foo { constructor (...args) { this .args = args; } * [Symbol .iterator]() { for (let arg of this .args) { yield arg; } } } for (let x of new Foo('hello' , 'world' )) { console .log(x); }
上面代码中,Foo
类的Symbol.iterator
方法前有一个星号,表示该方法是一个 Generator 函数。Symbol.iterator
方法返回一个Foo
类的默认遍历器,for...of
循环会自动调用这个遍历器,不断返回多次,一次打印出一个参数,直到遍历出所有的参数
this
类的方法内部如果含有this
,它默认指向类的实例。但是,必须非常小心,一旦单独使用该方法,很可能报错
1 2 3 4 5 6 7 8 9 10 11 class People { getPeople(name = 'zhangsan' ) { this .print(`hello ${ name } ` ) } print(text) { console .log(text) } } const people = new People()const { getPeople } = peoplegetPeople()
解决办法一:在constructor
中绑定this
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class People { constructor () { this .getPeople = this .getPeople.bind(this ) } getPeople(name = 'zhangsan' ) { this .print(`hello ${ name } ` ) } print(text) { console .log(text) } } const people = new People()const { getPeople } = peoplegetPeople()
解决方法二:使用箭头函数
1 2 3 4 5 6 7 8 9 10 11 class People { constructor () { this .getPeople = (name = 'zhangsan' ) => this .print(`hello ${ name } ` ) } print(text) { console .log(text) } } const people = new People()const { getPeople } = peoplegetPeople()
静态方法
static
关键字,带有static
关键字的方法不会被继承,且实例不能调用,必须用过类直接调用
1 2 3 4 5 6 7 8 9 10 11 12 class People { constructor (name, age) { this .name = name this .age = age } static say() { console .log('say everyone' ) } } People.say() var people = new Peoplepeople.say()
注意 ,如果静态方法包含this
关键字,这个this
指的是类,而不是实例
1 2 3 4 5 6 7 8 9 10 11 12 class People { static hello() { this .say() } static say() { console .log('我是静态' ) } say() { console .log('我是非静态' ) } } People.hello()
静态属性
将static
关键字,加在属性前面,即定义了静态属性
1 2 3 class Foo { static prop = 1 ; }
私有方法
私有方法是常见需求,但 ES6
不提供,只能通过变通方法模拟实现
方法一:在命名上加以区别
1 2 3 4 5 6 7 8 9 10 11 class People { foo (name) { this ._getName(name) } _getName(name) { return this .peop = name } }
上面代码中,getName
方法前面的下划线,表示这是一个只限于内部使用的私有方法。但是,这种命名是不保险的,在类的外部,还是可以调用到这个方法
方法二:将私有方法移出模块
1 2 3 4 5 6 7 8 9 10 class People { foo (name) { getName.call(this , name) } } getName(name) { return this .peop = name }
上述代码中,利用call
使this
即foo
调用了getName
方法,这使得getName
实际上成为了当前模块的私有方法
方法三:Symbol
值的唯一性
对于 Symbol 可以参考ES6中的Symbol
1 2 3 4 5 6 7 8 9 10 11 12 13 const a = Symbol ('a' )const b = Symbol ('b' )export default class People { foo (name) { this [a](name) } [a](name) { return this [b] = name } }
私有属性
请参照http://es6.ruanyifeng.com/#docs/class
类的继承
Class 可以通过extends
关键字实现继承,先看个例子
extends
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Person { constructor (name, age){ this .name = name this .age = age } } class American extends Person {} const a1 = new American('Jack' , 20 )console .log(a1.name) class Chinese extends Person {} const c1 = new Chinese('张三' , 22 )console .log(c1.name)
super
既可以当作函数使用,也可以当作对象使用。在这两种情况下,它的用法完全不同
情况一:super
作为函数调用时 ,代表父类的构造函数。ES6
要求,子类的构造函数必须执行一次super
函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 class Person { constructor (name, age){ this .name = name this .age = age } sayHello(){ console .log('大家好' ) } } class American extends Person { constructor (name, age){ super (name, age) } } const a1 = new American('Jack' , 20 )console .log(a1)a1.sayHello() class Chinese extends Person { constructor (name, age, IDNumber){ super (name, age) this .IDNumber = IDNumber } } const c1 = new Chinese('张三' , 22 , '130428******' )console .log(c1)c1.sayHello()
注意 :
如果需要添加自己独有的属性,则不能挂在到父类上 在子类中, this
只能放到 super
之后使用
情况二:super
作为对象 时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Person { constructor (name, age){ this .name = name this .age = age } sayHello(){ console .log('大家好' ) } } class American extends Person { constructor (name, age){ super (name, age) console .log(super .sayHello()) } } let amer = new American()
上面代码中,子类American
当中的super.sayHello()
,就是将super
当作一个对象使用。这时,super
在普通方法之中,指向Person.prototype
,所以super.sayHello()
就相当于Person.prototype.sayHello()
。