ES6对于Class类的基本语法详解(2024-04-10)

news/2024/7/11 1:31:56 标签: javascript, es6, 开发语言

目录

1、传统ES5写法

 2、ES6 的class语法

3、ES5与ES6行为对比

4、类的constructor() 方法 

5、类的实例 new

6、类的对象属性(新写法)

7、类的取值函数(getter)和存值函数(setter)

8、Class类的表达式

9、Class的static静态方法与静态属性


 ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。

1、传统ES5写法

JavaScript 语言中,生成实例对象的传统方法是通过构造函数。

javascript">function Point(x, y) {
  this.x = x;
  this.y = y;
}

Point.prototype.toString = function () {
  return '(' + this.x + ', ' + this.y + ')';
};

var p = new Point(1, 2);

基本上,ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。

 2、ES6 的class语法

javascript">class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }
}


-----------------------------------------------


// ES6的类完全可以看作构造函数的另外一种写法

class Point {
  // ...
}

typeof Point // "function"
Point === Point.prototype.constructor // true

// 上面代码表明,类的数据类型就是函数,类本身就指向构造函数。
// 使用的时候,也是直接对类使用new命令,跟构造函数的用法完全一致。

上面代码定义了一个“类”,可以看到里面有一个constructor()方法,这就是构造方法,而this关键字则代表实例对象。这种新的 Class 写法,本质上与本章开头的 ES5 的构造函数Point是一致的。

Point类除了构造方法,还定义了一个toString()方法。

注意,定义toString()方法的时候,前面不需要加上function这个关键字,直接把函数定义放进去了就可以了。

另外,方法与方法之间不需要逗号分隔,加了会报错。

上面代码表明,类的数据类型就是函数,类本身就指向构造函数。

使用的时候,也是直接对类使用new命令,跟构造函数的用法完全一致。

javascript">class Bar {
  doStuff() {
    console.log('stuff');
  }
}

const b = new Bar();
b.doStuff() // "stuff"

构造函数的prototype属性,在 ES6 的“类”上面继续存在。

事实上,类的所有方法都定义在类的prototype属性上面。

javascript">class Point {
  constructor() {
    // ...
  }

  toString() {
    // ...
  }

  toValue() {
    // ...
  }
}

// 等同于

Point.prototype = {
  constructor() {},
  toString() {},
  toValue() {},
};


------------------------------

class B {}
const b = new B();

b.constructor === B.prototype.constructor // true 等同

// b是B类的实例,它的constructor()方法就是B类原型的constructor()方法

上面代码中,constructor()toString()toValue()这三个方法,其实都是定义在Point.prototype上面。

因此,在类的实例上面调用方法,其实就是调用原型上的方法。

由于类的方法都定义在prototype对象上面,所以类的新方法可以添加在prototype对象上面。Object.assign()方法可以很方便地一次向类添加多个方法。

javascript">class Point {
  constructor(){
    // ...
  }
}

Object.assign(Point.prototype, {
  toString(){},
  toValue(){}
});

3、ES5与ES6行为对比

javascript">1、prototype对象的constructor属性,直接指向“类”的本身,这与 ES5 的行为是一致的。

var Point = function (x, y) {
  // ...
};

class Point {
  constructor(){
    // ...
  }
}

Point.prototype.constructor === Point // true

------------------------------------------------------------------------------

2、类的内部所有定义的方法,都是不可枚举的(non-enumerable)

// toString()方法是Point类内部定义的方法,它是不可枚举的。这一点与 ES5 的行为不一致
class Point {
  constructor(x, y) {
    // ...
  }

  toString() {
    // ...
  }
}

Object.keys(Point.prototype)
// []  不可枚举
Object.getOwnPropertyNames(Point.prototype)
// ["constructor","toString"]


// 上面代码采用 ES5 的写法,toString()方法就是可枚举的。
var Point = function (x, y) {
  // ...
};

Point.prototype.toString = function () {
  // ...
};

Object.keys(Point.prototype)
// ["toString"]  可枚举
Object.getOwnPropertyNames(Point.prototype)
// ["constructor","toString"]

4、类的constructor() 方法 

constructor()方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。

一个类必须有constructor()方法,如果没有显式定义,一个空的constructor()方法会被默认添加。

javascript">class Point {
}

// 等同于
class Point {
  constructor() {}
}

5、类的实例 new

生成类的实例的写法,与 ES5 完全一样,也是使用new命令。前面说过,如果忘记加上new,像函数那样调用Class(),将会报错。

javascript">class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }
}

// 实例报错
var point = Point(2, 3);

// 实例正确
var point = new Point(2, 3);


----------------------------------------------------------------------

// 类的属性和方法,除非显式定义在其本身(即定义在this对象上),否则都是定义在原型上(即定义在class上)。

// x和y都是实例对象point自身的属性(因为定义在this对象上),所以hasOwnProperty()方法返回true,而toString()是原型对象的属性(因为定义在Point类上),所以hasOwnProperty()方法返回false。这些都与 ES5 的行为保持一致

point.toString() // (2, 3)
point.hasOwnProperty('x') // true
point.hasOwnProperty('y') // true
point.hasOwnProperty('toString') // false
point.__proto__.hasOwnProperty('toString') // true


------------------------------------------------------------------------
// 类的所有实例共享一个原型对象,它们的原型都是Point.prototype,所以__proto__属性是相等的
var p1 = new Point(2,3);
var p2 = new Point(3,2);
p1.__proto__ === p2.__proto__  //true 相等

// 此时可以通过实例__proto__属性为“类”添加方法,并共享方法(一般不推荐此种方法,会改变原始定义类方法,污染全局!!!)
p1.__proto__.printName = function () { return 'Oops' };
p1.printName() // "Oops"
p2.printName() // "Oops" p2共享p1方法

6、类的对象属性(新写法)

ES6为类规定新的写法,定义在类的最顶层;

改变实例属性定义在constructor()方法里面的this上的写法。

javascript">// 原来的写法
class IncreasingCounter {
  constructor() {
    this._count = 0;
  }
  increment() {
    this._count++;
  }
}

// 新写法
// 实例属性_count与increment()方法,处于同一个层级(不需要在实例属性前面加上this)
// 新写法定义的属性是实例对象自身的属性,而不是定义在实例对象的原型上面)
class IncreasingCounter {
  _count = 0;
  increment() {
    this._count++;
  }
}

7、类的取值函数(getter)和存值函数(setter)

与 ES5 一样,在“类”的内部可以使用getset关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。

javascript">class MyClass {
  name = '张三'
  get prop() {
    console.log('getter: '+this.name);
    return this.name;
  }
  set prop(value) {
    this.name = value
    console.log('setter: '+this.name);
  }
}

let inst = new MyClass();

inst.prop = '刘五';
// setter: '刘五'

inst.prop
// getter:'张三'


// 存值函数和取值函数是设置在属性的 Descriptor 对象上的
var descriptor = Object.getOwnPropertyDescriptor(
  MyClass.prototype, "prop"
);

"get" in descriptor  // true
"set" in descriptor  // true

8、Class类的表达式

与函数一样,类也可以使用表达式的形式定义。

javascript">const MyClass1 = class isMe {
  name = 'sun'
  getClassName() {
    console.log(this)
    console.log(isMe)
    console.log(isMe.name)
    return isMe.name;
  }
};

// 类的名字是isMe,但是isMe只在 Class 的内部可用,指代当前类。
// 在 Class 外部,这个类只能用MyClass引用

let inst = new MyClass1();
inst.getClassName() // isMe
isMe.name // ReferenceError: isMe is not defined

上面代码表示,isMe只在 Class 内部有定义。

如果类的内部没用到的话,可以省略isMe,也就是可以写成下面的形式。

javascript">// 简写表达式,类的名字在内部没有使用到
const MyClass1 = class { /* ... */ };

-------------------------------------------------
// 或采用 Class 表达式,可以写出立即执行的 Class。
let person = new class {
  constructor(name) {
    this.name = name;
  }

  sayName() {
    console.log(this.name);
  }
}('张三');

person.sayName(); // "张三"  person是一个立即执行的类的实例

9、Class的static静态方法与静态属性

类相当于实例的原型,所有在类中定义的方法,都会被实例继承。

如果在一个类的方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”

javascript">class Foo {
  //静态属性(Class 本身的属性,即Foo.prop,而不是定义在实例对象(this)上的属性)
  static prop = 123;

  //静态方法(Class 本身的方法)
  static classMethod() {
    console.log(this)
    console.log(Foo.prop); // 123
    this.baz(); // 等同于类静态方法调用Foo.baz()
  }

  static baz() {
    console.log('hello');
  }

  baz() {
    console.log('world');
  }
}

Foo.classMethod() // 'hello'  直接使用类来调用
// 如果静态方法包含this关键字,这个this指的是类,而不是实例
// 静态方法classMethod 调用了this.baz,这里的this指的是Foo类,而不是Foo的实例,等同于调用Foo.baz()。另外,从这个例子还可以看出,静态方法可以与非静态方法重名
------------------------------------------------------------------

class Foo2 {
  classMethod() {
    return 'hello';
  }
}

Foo2.classMethod() 
// TypeError: Foo2.classMethod is not a function
// 只有静态方法才能直接使用类来调用,不然就报错

------------------------------------------------------------------
// 实例化类
let isfoo = new Foo();
isfoo.classMethod();
// TypeError: isfoo.classMethod is not a function
//(静态方法不能继承到实例对象中,无法调用)

子类能继承父类的静态方法

javascript">class Foo {
  static classMethod() {
    return 'hello';
  }
}

class Bar extends Foo {
}

Bar.classMethod() // 'hello' 子类Bar直接类调用(已经继承了Foo父类的静态方法classMethod)


http://www.niftyadmin.cn/n/5480688.html

相关文章

CentOS 7实战:轻松实现MySQL 5.7.25的tar包离线安装

单机版: 1、Centos7 系统自带MariaDB安装MySQL前需要卸载 # 查看系统自带的MariaDB [rootmysql_master ~]# rpm -qa |grep mariadbmariadb-libs-5.5.56-2.el7.x86_64# 卸载系统自带的MariaDB [rootmysql_master ~]# rpm -e --nodeps mariadb-libs-5.5.56-2.el7.x8…

头歌-机器学习 第5次实验 线性回归方法

第1关:数据载入与分析 任务描述 本关任务:编写一个能够载入线性回归相关数据的小程序。 编程要求 该实战内容中数据为一元数据,利用 pandas 读入数据文件,并为相应的数据附上名字标签,分别为Population 和 Profit。…

使用JAXB实现JavaBean与XML互相转换

JAXB 注解列表 注解使用范围描述XmlAccessorOrder包、类控制类中字段和属性的顺序。XmlAccessorType包、类此注释提供对类中属性和字段的默认序列化的控制。XmlRootElement类、枚举将类或枚举类型映射到XML元素。此注释可与以下注释一起使用:XmlType、XmlEnum、Xml…

探索进程控制第一弹(进程终止、进程等待)

文章目录 进程创建初识fork函数fork函数返回值fork常规用法fork调用失败的原因 写时拷贝进程终止进程终止是在做什么?进程终止的情况代码跑完,结果正确/不正确代码异常终止 如何终止 进程等待概述进程等待方法wait方法waitpid 进程创建 初识fork函数 在…

网络安全(黑客)—2024自学文档

一、什么是网络安全 网络安全可以基于攻击和防御视角来分类,我们经常听到的 “红队”、“渗透测试”等就是研究攻击技术,而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 无论网络、Web、移动、桌面、云等哪个领域,都有攻与防两面性…

【C/C++】Jemalloc + Jeprof内存泄漏分析

一, 前提 1 jemalloc lib 需要开启 profiling 功能 既编译 jemalloc的时候添加--enable-prof 选项 (同时会生成一个jeprof 工具,用来生成报告) $ wget https://github.com/jemalloc/jemalloc/archive/5.2.1.tar.gz $ tar xvf 5.2.1.tar.gz $ cd jemalloc-5.2.1/ $ mkdir build…

基于OptiTrack跟踪系统和Turtlebot机器人的视觉SLAM定位评估

本文旨在介绍使用OptiTrack光学跟踪系统和Turtlebot机器人进行视觉SLAM定位实验的详细流程,包括实验平台搭建过程、数据处理过程以及SLAM估计评估方法。由于涉及知识较多,部分内容只给出了相关参考博文链接。 1 实验平台搭建 实验平台包括OptiTrack光学…

SPLD论文笔记

SLPD论文笔记 题目:SLPD: Slide-Level Prototypical Distillation for WSIs 摘要 提高特征表示能力是许多全玻片病理图像 (WSI) 任务的基础。最近的工作在病理特异性自我监督学习(SSL)方面取得了巨大成功。然而&…