ES6——class和继承,保护对象

一、class

今后,只要在es6中创建一种新的类型,包含构造函数+原型对象方法,用class来创建

1. 定义class:

class:
集中保存一种类型的所有子对象的统一属性结构和方法定义的程序结构。

步骤:

  1. class{} 包裹原构造函数+原型对象方法

  2. 原构造函数名升级为整个class的名字,所有构造函数统一更名为"constructor"

  3. 原型对象中的方法,不用再加prototype前缀,也不用=function,直接简写为:
    方法名(){ ... ...}
    直接放在class{}内的方法定义,其实还是保存在原型对象中的

使用class:

var 对象名=new  class(属性值,...);

2. 本质

a. 构造函数中的属性,依然会成为子对象的自有属性
b. 直接定义在class中的方法,保存在子对象的原型对象中
c. 子对象依然使用_ _proto_ _指向原型对象

示例:

// class{} 包裹原构造函数+原型对象方法 原构造函数名升级为整个class的名字 
class Student{
    //1. 构造函数统一更名为constructor
    constructor(sname,sage) {
        //仍然为自有属性
        this.sname=sname;
        this.sage=sage;
    }
    //2. 原型对象: 直接放在class{}内的方法,保存在原型对象中的方法
    intr(){
        console.log(`I'm ${this.sname}, I'm ${this.sage}`)
    }
}

//创建两个学生: 
var lilei=new Student("Li Lei",11);
var hmm=new Student("Han Meimei",12);
console.log(lilei);
console.log(hmm);
lilei.intr();
hmm.intr();

3.定义共用属性

直接在class中定义的方法,都默认保存在原型对象中。但是直接在class中定义的属性,却不会成为共有属性,不会保存在原型对象中,而是成为每个子对象的自有属性

那么如果我们想在class中定义多个子对象的共用属性呢?

方法一:构造函数prototype方法

在构造函数中添加原型对象的属性:
类名.prototype.属性名=属性值

tip:但是要注意的是应在构造函数中设置该属性时加上一个判断,防止外界更改该共用属性后,再new新对象时,该共用属性又变回构造函数内的默认值!

if (类型名.prototype.共有属性名=== undefined) {
	类型名.prototype.共有属性 = 属性值;
}

示例:

class Student{
    //className="幼儿园小班" //自有属性
    
    constructor(sname, sage) {
      this.sname = sname;
      this.sage = sage;
      //如果当前类型的原型对象中不包含指定的共有属性
      if (Student.prototype.className===undefined) {
        //就自动向原型对象中添加共有属性
        Student.prototype.className="幼儿园小班"
      }
    }
    intr() {
      console.log(`I'm ${this.sname}, I'm ${this.sage}`)
    }
}

//创建两个学生: 
var erya = new Student("erya", 5);
var yuanyuan = new Student("yuanyuan", 6);
console.log(erya, yuanyuan);

//过了一年,两人共同升了一级
Student.prototype.className="幼儿园中班"
console.log(erya.className, yuanyuan.className);

方法二:静态属性

静态属性: 不需要创建子对象,单靠类型名就可直接访问的属性

今后,在ES6中,如果希望所有子对象都可使用一个共同的属性值时,都要用静态属性代替原来的原型对象属性!

定义静态属性:

class 类型名{
  static 共有属性名=属性值
  ... 
  ...
}

访问静态属性:
错误: this.静态属性
正确: 类型名.静态属性

原理:
标有static的静态属性,都是保存在构造函数对象身上。

构造函数在程序中不会重复=>静态属性也不会重复!
任何时候,任何地点,访问一个类型的静态属性,永远访问的都是同一份!

示例:

class Student{
    constructor(sname, sage) {
      this.sname = sname;
      this.sage = sage;
    }
    intr() {
        console.log(`I'm ${this.sname}, I'm ${this.sage}, I'm from ${Student.className}`);
    }
    static className = '幼儿园小班';
}

//创建两个学生: 
var erya = new Student("erya", 5);
var yuanyuan = new Student("yuanyuan", 6);
erya.intr();
yuanyuan.intr();

//过了一年,两个学生都升级
Student.className="幼儿园中班";
erya.intr();
yuanyuan.intr();

二、class中保护对象

2.1 保护单个属性

a.用开关保护

保护对象的代码要写在构造函数中,且要保护的对象用this代替!

"use strict";
class Emp{
    constructor(eid, ename, salary) { //妈妈
        this.eid=eid;//只读
        this.ename=ename;//禁止删除
        this.salary=salary;//禁止for in遍历
        //当程序执行到这里时,正在创建子对象过程中
        //如果在正在创建子对象过程中,想获得正在创建的子对象,保护正在创建的子对象
        //                      传值!
        //                       ↓
        Object.defineProperties(this, {
            //哪些属性: 
            eid:{
                writable:false,
                configurable:false
            },
            ename:{
                configurable:false
            },
            salary:{
                enumerable:false,
                configurable:false
            }
        })
    }
}
var erya=new Emp(1001, "erya", 12000);

尝试做一些非法操作:

//尝试打开eid的writable开关
Object.defineProperty(erya,"eid",{
    writable:true
});//报错: Cannot redefine property: eid
    
//尝试修改eid 
erya.eid=-2;//报错: Cannot assign to read only property 'eid'
    
//尝试删除ename
delete erya.ename;//报错:Cannot delete property 'ename'

//尝试遍历所有属性(包括salary)
for (var key in erya) {
    console.log(`属性名: ${key}, 属性值:${erya[key]}`)
}//虽然可以遍历,但是没有salary

b.访问器属性保护(ES6新做法)

  1. class{}中定义访问器属性
  2. get 函数set函数变为两个独立的函数,但是名称却是相同的,都是访问
    器属性名。
class Emp{
    constructor(eid, ename, eage) {
        this.eid=eid;
        this.ename=ename;
        //先创建一个半隐藏的_eage属性,但是暂时不要给属性值
        Object.defineProperty(this, "_eage", {
            //value:undefined
            writable:true,
            enumerable:false,
            configurable:false
        })
        //再修改Emp类型的原型对象中的访问器属性eage的enumerable为true
        //Object.defineProperty(this,"eage",{...})//错误: eage是定义在class中的,保存在原型对象中。要想修改原型对象中的成员,都要用原型对象本身去修改!而不应该用某一个子对象去修改!
        Object.defineProperty(Emp.prototype,"eage",{
            enumerable:true
        });
        this.eage=eage;
    }
    //创建访问器属性eage,保护eage的值必须介于18~65之间
    get eage() {
        return this._eage;
    }
    set eage(value) {
        if (value>=18&&value<=65) {
            this._eage=value
        } else {
            throw Error(`年龄超范围`)
        }
    }
  }
var erya=new Emp(1001, "erya", 25);
console.log(erya);

2.2 保护多个属性

三、 两种类型间的继承:

两种class之间包含部分相同的属性结构和方法定义时,可以使两种class类型间继承

3.1 步骤

1). 额外创建一个父级class

i. 父级class的构造函数中包含子类型class中相同部分的属性结构定义

ii. 父级class的原型对象中包含子类型class中相同部分的方法定义

iii. 既然父级class中保存了相同的属性结构和方法定义,则子类型class中可以删除所有重复的属性结构和方法定义

iv. 问题: 子类型class的子对象缺少必要的属性,而且有些共有方法也无法使用了!

2) 让子类型class继承父类型的class

i. extends 子类型的原型对象继承父类型的原型对象:

class 子类型 extends 父类型{ 
   ... 
}

extends 父类型取代 Object.setPrototypeOf(xxx, xxx):

在这里插入图片描述

原理: 只是设置子类型的原型对象继承父类型的原型对象,只能保证孙子对象可以使用爷爷类型原型对象中的共有方法!暂时无法为孙子对象补全缺少的自有属性.

ii. 使用super,在子类型构造函数中调用父类型构造函数

extends附赠一个关键词supersuper 指向父类型的构造函数
调用super(),就等效于调用父类型的构造函数了

d. 结果:
1). 孙子对象可使用3处保存的方法: 自己的,父级class的,爷爷class的
2). 孙子对象中拥有两处规定的属性:父类型构造函数+爷爷类型构造函数。

示例:
在这里插入图片描述
示例:

// 父类型class
class Enemy {
    constructor(x,y) {
        this.x=x;
        this.y=y;
    }
    // 父类型class的原型对象
    fly() {
        console.log(`飞到:x=${this.x},y=${this.y}`)
    }
}

// 子类型class plane继承父类型class enemy
class Plane extends Enemy{
    constructor(x,y,score) {
        super(x,y);//->父类型Enemy的constructor()
        //相当于执行了
        //Enemy的constructor(){
        // this.x=x;
        // this.y=y;
        //}
        this.score=score;
    }
    getScore() {
        console.log(`击落敌机得${this.score}分`)
    }
}
// 子类型class san继承父类型class enemy
class San extends Enemy{
    constructor(x, y, award) {
        super(x,y);
        this.award=award;
    }
    getAward() { 
        console.log(`击落降落伞,得${this.award}奖励`)
    }      
}

var p1=new Plane(50,100,5);
console.log(p1);
p1.fly();//p1的爷爷Enemy给的
p1.getScore();//p1的爸爸Plane给的

var s1=new San(20,80,"1 life");
console.log(s1);
s1.fly();//s1的爷爷Enemy给的
s1.getAward();//s1的爸爸San给的
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你脸上有BUG

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值