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关键字则代表实例对象,且方法之间不需要逗号分隔,加了会报错

ES6class 类,完全可以看作构造函数的另一种写法,证明如下:


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__属性为“类”添加方法


es 插件 评论关键词抽取 es关键字_私有方法

取值函数(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 完全一致

一些需要注意的点
不存在变量提升

类不存在变量提升


es 插件 评论关键词抽取 es关键字_es 插件 评论关键词抽取_02

如上代码,定义Foo在后,使用在前,则报错

name

由于本质上,ES6 的类只是ES5 的构造函数的一层包装,所以函数的许多特性都被Class继承,包括name属性


es 插件 评论关键词抽取 es关键字_私有方法_03

name属性总是返回紧跟在class关键字后面的类名

Generator 方法

如果某个方法之前加上星号(*),就表示该方法是一个 Generator 函数

generator可以在执行过程中多次返回,所以它看上去就像一个可以记住执行状态的函数,利用这一点,写一个generator就可以实现需要用面向对象才能实现的功能,详细请移步廖雪峰老师的generator


es 插件 评论关键词抽取 es关键字_父类_04

上面代码中,Foo类的Symbol.iterator方法前有一个星号,表示该方法是一个 Generator 函数。Symbol.iterator方法返回一个Foo类的默认遍历器,for...of循环会自动调用这个遍历器,不断返回多次,一次打印出一个参数,直到遍历出所有的参数

this

类的方法内部如果含有this,它默认指向类的实例。但是,必须非常小心,一旦单独使用该方法,很可能报错


es 插件 评论关键词抽取 es关键字_父类_05

解决办法一:在constructor中绑定this


es 插件 评论关键词抽取 es关键字_es 插件 评论关键词抽取_06

解决方法二:使用箭头函数


es 插件 评论关键词抽取 es关键字_私有方法_07

静态方法

static关键字,带有static关键字的方法不会被继承,且实例不能调用,必须用过类直接调用


es 插件 评论关键词抽取 es关键字_私有方法_08

注意,如果静态方法包含this关键字,这个this指的是类,而不是实例


class People {
    static hello() {
        this.say()
    }
    static say() {
        console.log('我是静态')
    }
    say() {
        console.log('我是非静态')
    }
}
People.hello() // 我是静态
静态属性

static关键字,加在属性前面,即定义了静态属性


es 插件 评论关键词抽取 es关键字_es 插件 评论关键词抽取_09

私有方法

私有方法是常见需求,但 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使thisfoo调用了getName方法,这使得getName实际上成为了当前模块的私有方法

方法三:Symbol 值的唯一性

对于 Symbol 可以参考ES6中的Symbol

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


// 这是父类
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()

注意

  1. 如果需要添加自己独有的属性,则不能挂在到父类上
  2. 在子类中, 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()