欢迎访问我的博客https://qqqww.com/,祝码农同胞们早日走上人生巅峰,迎娶白富美~~~
声明:本文参考业界大佬阮一峰老师的ES6标准入门
目录:
- ES5 中的面向对象
- ES6 中的 class 关键字
- 类的实例
- 取值函数(getter)和存值函数(setter)
类的继承- 一些需要注意的点
- 不存在变量提升
- name
- Generator
- this
- 静态方法
- 静态属性
- 私有方法
- 私有属性
- 类的继承
- extends
- super
ES5 中的面向对象
// 创建一个构造函数
function People (name, age) {
this.name = name
this.age = age
}
// 向构造函数的原型对象中添加 say 方法
People.prototype.say = function () {
return 'hello everyone'
}
// 实例化 People 构造函数,并保存在变量 a 中
var a = new People('zhangsan', 10)
ES6 中的 class 关键字
用
ES6
中的class
类去改写上面的例子看看
class People {
constructor(name, age) {
this.name = name
this.age = age
}
say() {
return 'hello everyone'
}
}
两个例子可以说实际功能一模一样,可以看到里面有一个
constructor
方法,这就是构造方法,而this
关键字则代表实例对象,且方法之间不需要逗号分隔,加了会报错
ES6
的class
类,完全可以看作构造函数的另一种写法,证明如下:
class People { }
typeof People // "function"
People.prototype.constructor === People // true
类的实例
同样的,也是用
new
关键字
class People {
constructor(name, age) {
this.name = name
this.age = age
}
say() {
return 'hello everyone'
}
}
var people = new People('zhangsan', 18)
与
ES5
一样,实例的属性除非显式定义在其本身(即定义在this
对象上),否则都是定义在原型上(即定义在class
上)
类的所有实例共享一个原型对象
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 // true
这就意味着可以通过实例的
__proto__
属性为“类”添加方法
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() // 'banner'
people2.eat() // 'banner'
取值函数(getter)和存值函数(setter)
案例借用阮一峰老师的《ES6标准入门》
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 // true
"set" in descriptor // true
存值函数和取值函数是定义在
html
属性的描述对象上面,这与ES5
完全一致
一些需要注意的点
不存在变量提升
类不存在变量提升
new Foo(); // ReferenceError
class Foo {}
如上代码,定义
Foo
在后,使用在前,则报错
name
由于本质上,
ES6
的类只是ES5
的构造函数的一层包装,所以函数的许多特性都被Class
继承,包括name
属性
class People {}
People.name // "People"
name
属性总是返回紧跟在class
关键字后面的类名
Generator 方法
如果某个方法之前加上星号(
*
),就表示该方法是一个 Generator 函数
generator
可以在执行过程中多次返回,所以它看上去就像一个可以记住执行状态的函数,利用这一点,写一个generator
就可以实现需要用面向对象才能实现的功能,详细请移步廖雪峰老师的generator
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);
}
// hello
// world
上面代码中,
Foo
类的Symbol.iterator
方法前有一个星号,表示该方法是一个 Generator 函数。Symbol.iterator
方法返回一个Foo
类的默认遍历器,for...of
循环会自动调用这个遍历器,不断返回多次,一次打印出一个参数,直到遍历出所有的参数
this
类的方法内部如果含有
this
,它默认指向类的实例。但是,必须非常小心,一旦单独使用该方法,很可能报错
class People {
getPeople(name = 'zhangsan') {
this.print(`hello ${ name }`)
}
print(text) {
console.log(text)
}
}
const people = new People()
const { getPeople } = people
getPeople() // TypeError: Cannot read property 'print' of undefined
解决办法一:在
constructor
中绑定this
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 } = people
getPeople() // 'hello zhangsan'
解决方法二:使用箭头函数
class People {
constructor() {
this.getPeople = (name = 'zhangsan') => this.print(`hello ${ name }`)
}
print(text) {
console.log(text)
}
}
const people = new People()
const { getPeople } = people
getPeople() // 'hello zhangsan'
静态方法
static
关键字,带有static
关键字的方法不会被继承,且实例不能调用,必须用过类直接调用
class People {
constructor(name, age) {
this.name = name
this.age = age
}
static say() {
console.log('say everyone')
}
}
People.say() // 'hello everyone'
var people = new People
people.say() // TypeError: people.say is not a function
注意,如果静态方法包含
this
关键字,这个this
指的是类,而不是实例
class People {
static hello() {
this.say()
}
static say() {
console.log('我是静态')
}
say() {
console.log('我是非静态')
}
}
People.hello() // 我是静态
静态属性
将
static
关键字,加在属性前面,即定义了静态属性
class Foo {
static prop = 1;
}
私有方法
私有方法是常见需求,但
ES6
不提供,只能通过变通方法模拟实现
方法一:在命名上加以区别
class People {
// 公有方法
foo (name) {
this._getName(name)
}
// 私有方法
_getName(name) {
return this.peop = name
}
}
上面代码中,
getName
方法前面的下划线,表示这是一个只限于内部使用的私有方法。但是,这种命名是不保险的,在类的外部,还是可以调用到这个方法
方法二:将私有方法移出模块
class People {
// 公有方法
foo (name) {
getName.call(this, name)
}
}
// 私有方法
getName(name) {
return this.peop = name
}
上述代码中,利用
call
使this
即foo
调用了getName
方法,这使得getName
实际上成为了当前模块的私有方法
私有属性
请参照http://es6.ruanyifeng.com/#docs/class
类的继承
Class 可以通过
extends
关键字实现继承,先看个例子
extends
// 这是父类
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) // Jack
// 这是子类 中国人 可继承父类的一些属性和方法
class Chinese extends Person{
}
const c1 = new Chinese('张三', 22)
console.log(c1.name) // 张三
super
既可以当作函数使用,也可以当作对象使用。在这两种情况下,它的用法完全不同
情况一:super
作为函数调用时,代表父类的构造函数。ES6
要求,子类的构造函数必须执行一次super
函数
// 这是父类
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{
// name 姓名
// age 年龄
// IDNumber 身份证号 【中国人独有的】,既然是独有的,就不适合 挂载到 父类上;
constructor(name, age, IDNumber){
super(name, age)
this.IDNumber = IDNumber
}
}
const c1 = new Chinese('张三', 22, '130428******')
console.log(c1)
c1.sayHello()
注意:
- 如果需要添加自己独有的属性,则不能挂在到父类上
- 在子类中,
this
只能放到super
之后使用
情况二:super
作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类
// 这是父类
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()
。