原型

原型是啥:https://www.zhihu.com/question/56770432/answer/315342130

原型就是共有属性

JS 只用一个很简单的规则就实现了对象复用

类是啥?

image
image

依据上图我们来自己写个例子:

// 如何用原型模拟类

var 人类共有属性 = {
  walk() {
    console.log('走两步')
  },
  species: '人类'
}

function createPerson(name,age) {
  var obj = {}
  obj.name = name || ''
  obj.age = age || 18
  obj.__proto__ = 人类共有属性
  return obj
}

createPerson('jewel')

结果如下:

image
image

ES 6 中的类

语法如下:

class Person {
  constructor(name,age) {
    this.name = name
    this.age = 18
  }
  walk() {
    console.log('走两步')
  }
}

contructor 里写的是自有属性,外写的是共有属性。我们可以新建一个对象来验证一下。

image
image

更多的语法:

class Animal {
    constructor() {
        this.body = '身体'
    }
    move() {
        console.log('能动')
    }
}

class Person extends Animal {  //基类  超类
    constructor(name) {
        super()
        this.name = name
        this.age = 18
    }
    walk() {
        console.log('走两步')
    }
}
var p1 = new Person('jewel')
console.log(p1)

打印的结果如下:

image
image

回顾一下我们目前为止用到的关键字:

class 类

new 新建某个类的对象

constructor 每个类都需要有这个,用来构造自有属性

extends 一个类想继承一个类就用 extends

super 调用超类。把超类的自有属性继承过来。使用 this 之前必须调用 super()

属性封装

其实 ES 6 中关于 class 的语法还不健全,比如在共有属性区域我们只能使用函数,比如上面例子中的 walk() ,目前并不支持 key: value 之类的语法。

那如何达到使用属性的效果呢?

用原型可以轻松实现。

依然使用上个例子的代码:

class Animal {
    constructor() {
        this.body = '身体'
    }
    move() {
        console.log('能动')
    }
}

class Person extends Animal {  //基类  超类
    constructor(name) {
        super()
        this.name = name
        this.age = 18
    }
    walk() {
        console.log('走两步')
    }
}
var p1 = new Person('jewel')

p1.__proto__.种族 = '人类'

console.log(p1.种族)   // 人类

就目前而言 class 的语法做不到这一点。

但是我们可以近似地做到。

class Animal {
    constructor() {
        this.body = '身体'
    }
    move() {
        console.log('能动')
    }
    race() {
        return '动物'
    }
}

class Person extends Animal {  //基类  超类
    constructor(name) {
        super()
        this.name = name
        this.age = 18
    }
    walk() {
        console.log('走两步')
    }
}
var p1 = new Person('jewel')
p1.race()  //动物

但是这样很不舒服。我明明只想要一个属性,却不得不一直加个括号来执行函数。

ES 6 支持在函数名称之前加个 get ,这样就不用加括号了。

class Animal {
    constructor() {
        this.body = '身体'
    }
    move() {
        console.log('能动')
    }
    get race() {
        return '动物'
    }
}

class Person extends Animal {  //基类  超类
    constructor(name) {
        super()
        this.name = name
        this.age = 18
    }
    walk() {
        console.log('走两步')
    }
}
var p1 = new Person('jewel')
p1.race    //动物

那么问题来了,如果我们一时手贱写错了怎么办?除了直接改原始代码,是否能在后写的代码里来更改 get 之后的函数里的内容呢?

答案是并不能。

class Animal {
    constructor() {
        this.body = '身体'
    }
    move() {
        console.log('能动')
    }
    get race() {
        return '运物'
    }
}

class Person extends Animal {  //基类  超类
    constructor(name) {
        super()
        this.name = name
        this.age = 18
    }
    walk() {
        console.log('走两步')
    }
}
var p1 = new Person('jewel')
console.log(p1.race)    //运物
p1.race = '动物'
console.log(p1.race)    //运物

改不动。ES 6 还提供了 set

我们需要一个隐藏的属性(现在的 JS 还没做到隐藏这个属性,还在开发当中…… 我们先骗一下自己这是隐藏的……),用来保存真正的值,然后用两个函数去操作这个隐藏的属性。

这个叫做属性封装。

class Animal {
    constructor() {
        this.body = '身体'
        this._race = '运物' //隐藏属性
    }
    move() {
        console.log('能动')
    }
    get race() {
        return this._race
    }
    set race(value) {
        this._race = value
    }
}

class Person extends Animal {  //基类  超类
    constructor(name) {
        super()
        this.name = name
        this.age = 18
    }
    walk() {
        console.log('走两步')
    }
}
var p1 = new Person('jewel')
console.log(p1.race)  //运物
p1.race = '动物'
console.log(p1.race)  //动物

这样我们既可以读又可以写了。

利用 get 来做点什么

有时候我们需要控制一下读写的权限。例如年龄不能随便改,应该设置成只读。

class Animal {
    constructor() {
        this.body = '身体'
    }
    move() {
        console.log('能动')
    }
}

class Person extends Animal {  //基类  超类
    constructor(name) {
        super()
        this.name = name
        this._age = 18
    }
    walk() {
        console.log('走两步')
    }
    get age() {
        return this._age
    }
}
var p1 = new Person('jewel')

这样年龄就没有权限改了。

利用 set 来做点什么

控制写

假如我们想改名字,但是不能随便改,只能改成两个字到四个字。

class Animal {
    constructor() {
        this.body = '身体'
    }
    move() {
        console.log('能动')
    }
}

class Person extends Animal {  //基类  超类
    constructor(name) {
        super()
        this._name = name
        this._age = 18
    }
    walk() {
        console.log('走两步')
    }
    get age() {
        return this._age
    }
    get name() {
        return this._name
    }
    set name(v) {
        if ( v.length > 4 ) {
            console.log('不行')
        }
        else if ( v.length < 2 ) {
            console.log('不行')
        } 
        else {
            this._name = v
        }
    }
}
var p1 = new Person('王大锤')

静态方法

通过 class 的名字直接 . 出来

class Animal {
    constructor() {
        this.body = '身体'
    }
    move() {
        console.log('能动')
    }
}

class Person extends Animal {  //基类  超类
    constructor(name) {
        super()
        this.name = name
        this.age = 18
    }
    static die() {
        console.log('地球炸了')
    }
    walk() {
        console.log('走两步')
    }
}

Person.die()    //地球炸了 

其他

Species

Mix-ins

参看下方 MDN 链接。

总结

相对来说,目前的 JavaScript 中的 class 还并不是那么好用,但是预计以后 class 会发展的比原型好很多。

现在就慢慢学起来。以备后用。

资料:

原型是啥

类-MDN

(完)