当前位置: 首页 > news >正文

ES6--》读懂JS中—Class类

目录

Class类

初识class

class中getter和setter设置

表达式方式书写

静态属性与静态方法

私有属性和私有方法

class继承

静态属性和方法继承

私有属性和方法继承

class显示原型与隐式原型关系


Class类

ES6提供了更接近传统语言的写法,引入了Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。基本上,ES6的class可以看作只是一个语法糖,它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰,更像面向对象编程的语法。

初识class

之前ES5通过构造函数实现实例化的方法

<script>
    // 定义人类
    function People(name,age){
        this.name = name
        this.age = age
    }
    // 添加方法
    People.prototype.say = function (){
        console.log('hello world');
    }
    // 实例化方法
    let person = new People('张三',18)
    person.say()//hello world
    console.log(person);//People {name: '张三', age: 18}
</script>

ES6 class 方法实现

constructor()方法是类的默认方法,通过 new 命令生成对象实例时,自动调用该方法,一个类必须有constructor()方法,如果没有显示定义,一个空的constructor()方法会被默认添加。

<script>
    // class
    class People {
        // 构造方法 名字是固定格式不能修改
        constructor(name,age){
            this.name = name
            this.age = age
        }
        // 方法必须使用该语法,不能使用 ES5 的对象完整形式
        say(){
            console.log('hello world');
        }
    }
    // 实例化对象
    let person = new People('张三',18)
    person.say()//hello world
    console.log(person);//People {name: '张三', age: 18}
</script>

class中getter和setter设置

在ES6中,类的内部可以使用 getter (取值函数) 和 setter (存值函数) 关键字,即 get 和 set ,对某个属性设置取值函数和存值函数,拦截该函数的存取行为。

<script>
    class People {
        get name(){
            console.log('我是张三');
            return '这是我的名字'//如果不写return,默认是undefined
        }
        set name(Name){//形参必须有
            console.log('我的名字被修改了');
        }
    }
    // 实例化对象
    let p = new People()
    
    // 只要读取 p 的实例化属性,就会执行 get 关键字函数里面代码,而且这个函数的返回值就是属性的一个值
    console.log(p.name);
    
    // 只要对 name 属性进行一个修改,如果有set关键字函数,就会执行该函数
    p.name = 'f'
</script>

表达式方式书写

和函数一样,类可以用表达式定义书写,需要注意的是:定义的类名只能在Class内部使用,指代当前类,在Class外部,类只能用自己定义等于类的常量。

<script>
    const myClass = class Me {
        getClass(){
            return Me.name//返回类名
        }
    }
    let c = new myClass()
    console.log(c.getClass())//Me
    Me.name//Me is not defined 

    // 如果类的内部没用到的话,可以省略Me,也就是可以写成下面的形式
    const MyClass = class {};
</script>

静态属性与静态方法

静态属性是指 Class 本身的属性,即 Class.propName,而不是定义在实例对象 this 上的属性。

实例对象和函数对象的属性是不相通的,实例对象的属性和构造函数的原型对象相通,实例对象只能继承构造函数原型中的属性和方法。

<script>
    function People(){

    }
    // 函数属性和方法
    People.name = '张三'
    People.say = function(){
        console.log('hello world');
    }
    // 原型对象属性和方法
    People.prototype.age = 18
    // 实例化对象
    let p = new People()
    console.log(People.name,People.say());
    console.log(p.age);
    console.log(p.name,p.say());
</script>

以class方法展示,因为ES6明确规定,Class内部只有静态方法,没有静态属性,而要想得到设置静态属性,需要在实例属性前面加上 static 关键字;静态方法也要加上 static 关键字,表示该方法不会被实例继承,而是直接通过类来调用。

<script>
    class People {
        // 静态属性
        static name = '张三'
        static say(){
            console.log('hello world');
        }
    }
    let p = new People()
    console.log(p.name);//undefined
    console.log(People.name);//张三
</script>

私有属性和私有方法

常见需求:私有属性和方法,是只能在类内部访问的属性和方法,外部不能访问,有利于代码的封装。

ES6中正式为class添加了私有属性和方法,方法是在属性和方法名之前使用 # 表示,如果不带 # ,会被当作另一个属性和方法。

<script>
    class person {
        // 私有属性
        #name
        constructor(name){
            this.#name = name
        }
        // 私有方法
        #sayName(){
            return this.#name
        }
        result(){
            console.log(this.#name);
        }
    }
    let p = new person()
    p.result = '张三'
    console.log(p);//person {result: '张三', #sayName: ƒ, #name: undefined}
    p.#name//报错
</script>

当我们想判断某个类的私有属性是否存在时,我们可以用 in 运算符进行判断。

<script>
    class A {
        #foo = 0;
        m() {
            console.log(#foo in this); // true
            console.log(#bar in this); // Private field '#bar' must be declared in an enclosing class(提示我们:私有字段“#bar”必须在封闭类中声明)
        }
    }
    let a = new A()
    a.m()
</script>

class继承

构造函数实现继承

通过原型链进行继承,如果有不熟悉原型链的朋友,可以看一下我之前的文章:原型和原型链

<script>
    // 动物
    function Animals(name,age){
        this.name = name
        this.age = age
    }
    Animals.prototype.call = function(){
        console.log('我是动物');
    }
    // 狗
    function Dog(name,age,color,gender){
        // 改变this的指向,继承父类
        Animals.call(this,name,age)
        this.color = color
        this.gender = gender
    }
    // 设置子类构造函数的原型
    Dog.prototype = new Animals //此时子类的实例对象就会继承父类上面的方法
    Dog.prototype.constructor = Dog

    // 声明子类的方法
    Dog.prototype.say = function(){
        console.log('汪汪汪!!!');
    }
    
    // 子类对象进行实例化
    const d = new Dog('小明',3,'棕色','雄')
    console.log(d);
</script>

class类实现继承

class可以通过 extends 关键字实现继承,让子类继承父类属性和方法,可以看出 extends 的写法比上文 原型链继承 清晰方便的多。

<script>
    class Animals {
        // 构造方法
        constructor(name,age){
            this.name = name
            this.age = age
        }
        // 父类成员的属性
        call(){
            console.log('我是动物');
        }
    }
    class Dog extends Animals {
        // 构造方法
        constructor(name,age,color,gender){
            // 调用父类方法,需要用super(),super()就是父类的constructor()方法
            super(name,age)
            this.color = color
            this.gender = gender
        }
        // 子类独有的方法
        say(){
            console.log('汪汪汪!!!');
        }
        // 当子类和父类重名时,优先调用的是子类的方法
        call(){
            console.log('我也是动物');
            // 如果想调用父类方法,使用如下语句:super.父类方法()
            super.call()
        }
    }
    // 实例化子类对象
    const d = new Dog('小明',3,'棕色','雄')
    console.log(d);
    d.call()
    d.say()
</script>

super 关键字

上面代码用到 super 这个关键字,这里简单说明一下:子类继承父类的 constructor() 构造函数中必须要有 super(),代表调用父类的构造函数,没有就会报错,super虽然代表父类的构造函数,但是返回的是子类的实例,即super内部的this指的是子类的实例。作为函数时,super() 只能用在子类的构造函数中,用在其他地方就会报错。

判断继承是否存在

Object.getPrototypeOf()方法可以用来从子类上获取父类,所以可以用来判断一个类是否继承另一个类。

<script>
    class people {}
    class boy extends people {}
    console.log(Object.getPrototypeOf(boy) === people);//true
</script>

静态属性和方法继承

父类的静态属性和方法也能被子类继续,如下:

<script>
    class people {
        // 父类静态属性 属性为数值
        static age = 18

        // 父类静态属性 属性为对象
        static h = {height:180}
        
        // 父类静态方法
        static say(){
            console.log('hello world');
        }
    }

    // 子类继承父类
    class boy extends people {
        constructor(){
            // 调用父类的构造函数
            super()
            // boy类继承静态属性时,会采用浅拷贝,拷贝父类静态属性的值,因此people.age和boy.age是两个彼此独立的属性。
            boy.age--
            // 如果父类的静态属性的值是一个对象,那么子类的静态属性也会指向这个对象,因为浅拷贝只会拷贝对象的内存地址所以,子类修改这个对象的属性值,会影响到父类。
            boy.h.height--
        }
    }
    
    // 实例化子类
    let b = new boy()
    boy.say()
    console.log(people.age);
    console.log(boy.age);
    console.log(boy.h.height);
    console.log(people.h.height);

    console.log(b);
</script>

私有属性和方法继承

私有属性和方法只能定义在它本身的class里面使用,所以子类会继承父类所有的属性和方法除了私有属性和方法,那么如何让子类访问到父类中的私有属性和方法呢?如果父类定义了私有属性的读写方法,子类就可以通过这些方法,读取私有属性。

<script>
    class people {
        #name = '张三'
        // 定义用来读取私有属性和方法的函数
        getName(){
            return this.#name
        }
    }
    class boy extends people {
        constructor(){
            // 调用父类的构造函数
            super()
            console.log(this.getName());//张三
        }
    }
    let b = new boy()
</script>

class显示原型与隐式原型关系

每个对象都有隐式原型 __proto__ 属性,指向对应的构造函数的显示原型 prototype 属性,class作为构造函数的语法糖,同时也具有 prototype 属性和 __proto__ 属性,所以存在两条继承链。当然这里这做一个了解。

<script>
    class people {}
    class boy extends people{}
    
    // 子类的__proto__属性,表示构造函数的继承,总是指向父类。
    console.log(boy.__proto__ === people); // true

    // 子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性。
    console.log(boy.prototype.__proto__ === people.prototype); // true
</script>

相关文章:

  • 机器学习笔记(三)
  • 【Java 面试题】经典 Java 面试题 200 问(下)
  • 瑞吉外卖之 redis优化缓存
  • [JavaWeb]—前端篇
  • 机器学习感知机原理及python代码实现
  • js 对象
  • 图解Redis 记录
  • 网络安全的行业黑话 ——防守篇之软硬件
  • 使用 CubeMX 配置 RCC 时钟
  • CVPR 2022 Oral 大连理工提出的SCI 快速、超强的低光照图像增强方法 可视化代码
  • CVE-2013-4547 Nginx文件名解析漏洞详解
  • 信息收集之 操作系统识别
  • 程序设计——图书管理系统(附源代码)
  • 纯C实现的贪吃蛇(无EaxyX,详解)
  • 布局管理器案例集锦
  • 【译】理解JavaScript:new 关键字
  • 002-读书笔记-JavaScript高级程序设计 在HTML中使用JavaScript
  • Android 控件背景颜色处理
  • Java 网络编程(2):UDP 的使用
  • Java精华积累:初学者都应该搞懂的问题
  • JS字符串转数字方法总结
  • PHP 7 修改了什么呢 -- 2
  • spring boot下thymeleaf全局静态变量配置
  • 基于Dubbo+ZooKeeper的分布式服务的实现
  • 紧急通知:《观止-微软》请在经管柜购买!
  • 看域名解析域名安全对SEO的影响
  • 聊聊redis的数据结构的应用
  • 猫头鹰的深夜翻译:JDK9 NotNullOrElse方法
  • 爬虫模拟登陆 SegmentFault
  • 批量截取pdf文件
  • 前端设计模式
  • 以太坊客户端Geth命令参数详解
  • 这几个编码小技巧将令你 PHP 代码更加简洁
  • 仓管云——企业云erp功能有哪些?
  • 整理一些计算机基础知识!
  • # 数论-逆元
  • #DBA杂记1
  • $.ajax中的eval及dataType
  • (10)STL算法之搜索(二) 二分查找
  • (6)STL算法之转换
  • (Arcgis)Python编程批量将HDF5文件转换为TIFF格式并应用地理转换和投影信息
  • (pojstep1.1.1)poj 1298(直叙式模拟)
  • (八)光盘的挂载与解挂、挂载CentOS镜像、rpm安装软件详细学习笔记
  • (八十八)VFL语言初步 - 实现布局
  • (附源码)ssm失物招领系统 毕业设计 182317
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理第3章 信息系统治理(一)
  • (强烈推荐)移动端音视频从零到上手(下)
  • (转)shell中括号的特殊用法 linux if多条件判断
  • (转)项目管理杂谈-我所期望的新人
  • .NET 8.0 中有哪些新的变化?
  • .NET CLR Hosting 简介
  • .NET 中使用 TaskCompletionSource 作为线程同步互斥或异步操作的事件
  • @Responsebody与@RequestBody
  • [Android]Android开发入门之HelloWorld
  • [Android]常见的数据传递方式