《每天十分钟》-红宝书第4版-对象、类与面向对象编程(七)

之前的几个继承模式,只使用 ECMAScript 5 的特性来模拟类似于类(class-like)的行为。不难看出,各种策略都有自己的问题,也有相应的妥协,而且代码又丑又长。
为了解决这些问题,ECMAScript 6 新引入的 class 关键字具有正式定义类的能力。类(class)是ECMAScript 中新的基础性语法糖结构,因此刚开始接触时可能会不太习惯。虽然 ECMAScript 6 类表面上看起来可以支持正式的面向对象编程,但实际上它背后使用的仍然是原型和构造函数的概念。

类的定义
与函数类型相似,定义类也有两种主要方式:类声明和类表达式。这两种方式都使用 class 关键字加大括号:

// 类声明
class Person {} 
// 类表达式
const Animal = class {};

注意:类不会像变量一样提升
类的构成
包含构造函数方法、实例方法、获取函数、设置函数和静态类方法,但这些都不是必需的。空的类定义照样有效。默认情况下,类定义中的代码都在严格模式下执行。

// 空类定义,有效 
class Foo {} 
// 有构造函数的类,有效
class Bar { constructor() {} 
} 
// 有获取函数的类,有效
class Baz { get myBaz() {} 
} 
// 有静态方法的类,有效
class Qux { static myQux() {} 
}

类构造函数
constructor 关键字用于在类定义块内部创建类的构造函数。方法名 constructor 会告诉解释器在使用 new 操作符创建类的新实例时,应该调用这个函数。构造函数的定义不是必需的,不定义构造函数相当于将构造函数定义为空函数。
类构造函数与构造函数的主要区别是,调用类构造函数必须使用 new 操作符。而普通构造函数如果不使用 new 调用,那么就会以全局的 this(通常是 window)作为内部对象。调用类构造函数时如果忘了使用 new 则会抛出错误:

function Person() {} 
class Animal {} 
// 把 window 作为 this 来构建实例
let p = Person(); 
let a = Animal(); 
// TypeError: class constructor Animal cannot be invoked without 'new'

把类当成特殊函数
ECMAScript 中没有正式的类这个类型。从各方面来看,ECMAScript 类就是一种特殊函数。声明一个类之后,通过 typeof 操作符检测类标识符,表明它是一个函数:

class Person {} 
console.log(Person); // class Person {} 
console.log(typeof Person); // function

实例、原型和类成员

  1. 实例成员
    每个实例都对应一个唯一的成员对象,这意味着所有成员都不会在原型上共享
class Person { constructor() { // 这个例子先使用对象包装类型定义一个字符串// 为的是在下面测试两个对象的相等性this.name = new String('Jack'); this.sayName = () => console.log(this.name); this.nicknames = ['Jake', 'J-Dog'] } 
} 
let p1 = new Person(), p2 = new Person(); 
p1.sayName(); // Jack 
p2.sayName(); // Jack 
console.log(p1.name === p2.name); // false 
console.log(p1.sayName === p2.sayName); // false 
console.log(p1.nicknames === p2.nicknames); // false 
p1.name = p1.nicknames[0]; 
p2.name = p2.nicknames[1]; 
p1.sayName(); // Jake 
p2.sayName(); // J-Dog
  1. 原型方法与访问器
    为了在实例间共享方法,类定义语法把在类块中定义的方法作为原型方法。
class Person { constructor() { // 添加到 this 的所有内容都会存在于不同的实例上this.locate = () => console.log('instance'); }// 在类块中定义的所有内容都会定义在类的原型上locate() { console.log('prototype'); } 
} 
let p = new Person(); 
p.locate(); // instance 
Person.prototype.locate(); // prototype

可以把方法定义在类构造函数中或者类块中,但不能在类块中给原型添加原始值或对象作为成员数据:

class Person { name: 'Jake' 
} 
// Uncaught SyntaxError: Unexpected token

类方法等同于对象属性,因此可以使用字符串、符号或计算的值作为键:

const symbolKey = Symbol('symbolKey'); 
class Person { stringKey() { console.log('invoked stringKey'); } [symbolKey]() { console.log('invoked symbolKey'); } ['computed' + 'Key']() { console.log('invoked computedKey'); } 
} 
let p = new Person(); 
p.stringKey(); // invoked stringKey 
p[symbolKey](); // invoked symbolKey 
p.computedKey(); // invoked computedKey

类定义也支持获取和设置访问器。语法与行为跟普通对象一样:

class Person { set name(newName) { this.name_ = newName; } get name() { return this.name_; } 
} 
let p = new Person(); 
p.name = 'Jake'; 
console.log(p.name); // Jake
  1. 静态类方法
    可以在类上定义静态方法。这些方法通常用于执行不特定于实例的操作,也不要求存在类的实例。与原型成员类似,静态成员每个类上只能有一个。
    静态类成员在类定义中使用 static 关键字作为前缀。在静态成员中,this 引用类自身。其他所有约定跟原型成员一样:
class Person { constructor() { // 添加到 this 的所有内容都会存在于不同的实例上this.locate = () => console.log('instance', this); } // 定义在类的原型对象上locate() { console.log('prototype', this); } // 定义在类本身上static locate() { console.log('class', this); } 
} 
let p = new Person(); 
p.locate(); // instance, Person {} 
Person.prototype.locate(); // prototype, {constructor: ... } 
Person.locate(); // class, class Person {}

静态类方法非常适合作为实例工厂:

class Person { constructor(age) { this.age_ = age; } sayAge() { console.log(this.age_); } static create() { // 使用随机年龄创建并返回一个 Person 实例return new Person(Math.floor(Math.random()*100)); } 
} 
console.log(Person.create()); // Person { age_: ... }
  1. 非函数原型和类成员
    虽然类定义并不显式支持在原型或类上添加成员数据,但在类定义外部,可以手动添加:
class Person { sayName() { console.log(`${Person.greeting} ${this.name}`); } 
} 
// 在类上定义数据成员
Person.greeting = 'My name is';
// 在原型上定义数据成员
Person.prototype.name = 'Jake'; 
let p = new Person(); 
p.sayName(); // My name is Jake

注意 类定义中之所以没有显式支持添加数据成员,是因为在共享目标(原型和类)上添加可变(可修改)数据成员是一种反模式。一般来说,对象实例应该独自拥有通过 this引用的数据。

  1. 迭代器与生成器方法
    类定义语法支持在原型和类本身上定义生成器方法:
class Person { // 在原型上定义生成器方法*createNicknameIterator() { yield 'Jack'; yield 'Jake'; yield 'J-Dog'; } // 在类上定义生成器方法static *createJobIterator() { yield 'Butcher'; yield 'Baker'; yield 'Candlestick maker'; } 
} 
let jobIter = Person.createJobIterator(); 
console.log(jobIter.next().value); // Butcher 
console.log(jobIter.next().value); // Baker 
console.log(jobIter.next().value); // Candlestick maker 
let p = new Person(); 
let nicknameIter = p.createNicknameIterator(); 
console.log(nicknameIter.next().value); // Jack 
console.log(nicknameIter.next().value); // Jake 
console.log(nicknameIter.next().value); // J-Dog

因为支持生成器方法,所以可以通过添加一个默认的迭代器,把类实例变成可迭代对象:

class Person { constructor() { this.nicknames = ['Jack', 'Jake', 'J-Dog']; } *[Symbol.iterator]() { yield *this.nicknames.entries(); } 
} 
let p = new Person(); 
for (let [idx, nickname] of p) { console.log(nickname); 
}
// Jack 
// Jake 
// J-Dog

也可以只返回迭代器实例:

class Person { constructor() { this.nicknames = ['Jack', 'Jake', 'J-Dog']; } [Symbol.iterator]() { return this.nicknames.entries(); } 
} 
let p = new Person(); 
for (let [idx, nickname] of p) { console.log(nickname); 
} 
// Jack 
// Jake 
// J-Dog

最近又有点偷懒,罪过罪过,下接抄继承
最近带娃徒步了几座古道,古道上很少商业化,这可能也是徒步古道的重要原因,背上点露营餐拿上登山杖,一路攀登,眼睛看到的是山的原貌,耳朵听到的是各种鸟的叫声,美~,欣赏一首
醉眠 唐庚·宋
山静似太古,日长如小年。
馀花犹可醉,好鸟不妨眠。
世味门常掩,时光簟已变。
梦中频得句,捻笔又忘筌。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/815967.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JAVA集合(学习)

1.Java集合框架体系图 集合框架Collection单列集合List 有序,可重复Vector 数组结构,线程安全ArrayList 数组结构,非线程安全LinkedList 链表结构,非线程安全Set 无序,唯一HashSet 哈希表结构LinkedHashSet 哈希表…

Spring Bean作用域

Sping中bean的scope的值可以是singleton、prototype、request、session、global session。默认情况下是singleton。只有在web容器中才能使用request、session、global session。 singleton:单例模式,spring容器中有且仅有一个对象,init方法在创建容器时仅执行一次。关闭容器…

简单几步启用Ubuntu root用户密码登录

Ubuntu默认登录用户为ubuntu,且禁用了root用户使用密码方式登录。 若要开起root用户登录,只需要以下几步: 1.使用ubuntu用户登录Linux服务器。 2.使用sudo修改root用户密码。 sudo passwd root 输入新设置的root用户密码,此时…

Mac下Homebrew更新国内源brew update卡死(完美解决)

先更新下brew 有时brew版本太旧也会有问题 /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 再更新国内源 #更新Homebrew cd "$(brew --repo)" git remote set-url origin https://mirrors.ustc…

jvisualVM分析jvm内存使用快照dump

服务发生内存溢出,就需要查看服务器上Java服务的jvm堆内存使用情况,可以使用dump命令生成dump文件,然后下载到本地,然后使用jvisualVM工具打开,即可实现可视化分析。 生成dump文件常用的两种方式: 第一种…

6-138 大整数相加

本题要求实现一个函数,实现大整数相加运算。大整数按每4位保存在整数数组中,如果大整数位数不是4的倍数,则将保证低位都按4位一存。如“123456789”保存为1,2345,6789。 函数接口定义: int convert(char a[],int x[]); int add(int x[],int len1,int y[],int len2,int …

[react] useState的一些小细节

1.无限循环 因为setState修改是异步的,加上会触发函数重新渲染, 如果代码长这样 一秒再修改,然后重新触发setTImeout, 然后再触发,重复触发循环 如果这样呢 还是会,因为你执行又会重新渲染 2.异步修改数据 为什么修改多次还是跟不上呢? 函数传参解决 因为是异步修改 ,所以…

代码随想录算法训练营第三十六天| 435. 无重叠区间、 763.划分字母区间、56. 合并区间

435 题目: 给定一个区间的集合 intervals ,其中 intervals[i] [starti, endi] 。返回 需要移除区间的最小数量,使剩余区间互不重叠 。 题目链接:435. 无重叠区间 - 力扣(LeetCode) 思路: …

Nacos 入门篇---服务端如何处理客户端的服务注册请求?(三)

一、引言 ok呀,上个章节我们讲了Nacos客户端的服务自动注册,今天我们来看看服务端接收到了客户端的服务注册请求,服务端都做了哪些事情~ 二、目录 目录 一、引言 二、目录 三、回顾上节内容: 四、Nacos 服务代码入…

工作流引擎项目解析(二)

流程变量设置于获取 Activiti--流程变量_runtimeservice.setvariable-CSDN博客 Process Variables | docs.camunda.org 设置 启动时设置任务完成时设置runtimeService.setvars…()taskservice.setvars…() ProcessEngine processEngine ProcessEngines.getDefaultProcessEn…

python项目练习——24、线旅游预订系统

用户管理功能: 用户注册:用户可以填写用户名和密码进行注册。用户登录:注册过的用户可以使用用户名和密码登录系统。用户资料管理:用户可以查看和编辑自己的个人资料。旅游产品管理功能: 产品列表展示:展示可预订的旅游产品列表。产品搜索:用户可以根据关键字、目的地、…

Nginx 基础应用实战 04 在公网配置配置HTTPS

Nginx 基础应用实战 04 在公网配置配置HTTPS Nginx配置 server {listen 443 ssl;server_name aa.abc.com;ssl_certificate /data/cert/server.crt;ssl_certificate_key /data/cert/server.key;}免费签名 https://freessl.cn 阿里云 腾讯云 Nginx配置 serve…

Springboot整合nacos报错无法连接nacos

报错信息:Nacos com.alibaba.nacos.api.exception.NacosException: Client not connected, current status:STARTING 关于这个报错的原因有很多:如Nacos未启动、网络问题、配置问题、版本不兼容问题等,我的报错原因主要是版本不兼容。 下面…

5.9 mybatis之callSettersOnNulls作用

文章目录 1. 当callSettersOnNullstrue时2. 当callSettersOnNullsfalse时 在mybatis的settings配置参数中有个callSettersOnNulls参数,官方解释为:指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法…

PostgreSQL入门到实战-第三十弹

PostgreSQL入门到实战 PostgreSQL教程网站官网地址PostgreSQL概述更新计划 PostgreSQL教程网站 https://www.postgresqltutorial.com/ 官网地址 声明: 由于操作系统, 版本更新等原因, 文章所列内容不一定100%复现, 还要以官方信息为准 https://www.postgresql.org/PostgreS…

【系统分析师】计算机网络

文章目录 1、TCP/IP协议族1.1 DHCP协议1.2 DNS协议1.3网络故障诊断 2、网路规划与设计2.1逻辑网络设计2.2物理网络设计2.3 分层设计 3、网络接入3.1 接入方式3.2 IPv6地址 4、综合布线技术5、物联网5.1物联网概念与分层5.2 物联网关键技术 6、云计算7、网络存储技术&#xff08…

neo4j使用详解(结尾、neo4j的java driver使用模板及工具类——<可用于生产>)

Neo4j系列导航: neo4j安装及简单实践 cypher语法基础 cypher插入语法 cypher插入语法 cypher查询语法 cypher通用语法 cypher函数语法 neo4j索引及调优 neo4j java Driver等更多 1. 简介 本文主要是java使用neo4j driver操作neo4j的模板项目及非常有用的工具类,主要包括: 图…

Vector - CAPL - XCP介绍_02

前面我们介绍了关于使用vector XCP License后,通过CAPL对XCP协议进行连接、断开和获取当前XCP连接状态的函数,本篇文章不做过多的其他赘述,我们继续介绍CAPL控制XCP相关的其他函数。 目录 xcpActivate 代码示例 xcpDeactivate xcpActiva…

数据库-Redis(9)

目录 41.AOF重写日志时,有新数据写入怎么办呢? 42.主线程Fork出子进程是如何复制内存数据的?

LeetCode617:合并二叉树

题目描述 给你两棵二叉树: root1 和 root2 。 想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。合并的规则是:如果两个节点重…