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

2024-04-15 04:12

本文主要是介绍《每天十分钟》-红宝书第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

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

这篇关于《每天十分钟》-红宝书第4版-对象、类与面向对象编程(七)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/904824

相关文章

JSON字符串转成java的Map对象详细步骤

《JSON字符串转成java的Map对象详细步骤》:本文主要介绍如何将JSON字符串转换为Java对象的步骤,包括定义Element类、使用Jackson库解析JSON和添加依赖,文中通过代码介绍... 目录步骤 1: 定义 Element 类步骤 2: 使用 Jackson 库解析 jsON步骤 3: 添

Spring常见错误之Web嵌套对象校验失效解决办法

《Spring常见错误之Web嵌套对象校验失效解决办法》:本文主要介绍Spring常见错误之Web嵌套对象校验失效解决的相关资料,通过在Phone对象上添加@Valid注解,问题得以解决,需要的朋... 目录问题复现案例解析问题修正总结  问题复现当开发一个学籍管理系统时,我们会提供了一个 API 接口去

Java如何通过反射机制获取数据类对象的属性及方法

《Java如何通过反射机制获取数据类对象的属性及方法》文章介绍了如何使用Java反射机制获取类对象的所有属性及其对应的get、set方法,以及如何通过反射机制实现类对象的实例化,感兴趣的朋友跟随小编一... 目录一、通过反射机制获取类对象的所有属性以及相应的get、set方法1.遍历类对象的所有属性2.获取

java中VO PO DTO POJO BO DO对象的应用场景及使用方式

《java中VOPODTOPOJOBODO对象的应用场景及使用方式》文章介绍了Java开发中常用的几种对象类型及其应用场景,包括VO、PO、DTO、POJO、BO和DO等,并通过示例说明了它... 目录Java中VO PO DTO POJO BO DO对象的应用VO (View Object) - 视图对象

vue如何监听对象或者数组某个属性的变化详解

《vue如何监听对象或者数组某个属性的变化详解》这篇文章主要给大家介绍了关于vue如何监听对象或者数组某个属性的变化,在Vue.js中可以通过watch监听属性变化并动态修改其他属性的值,watch通... 目录前言用watch监听深度监听使用计算属性watch和计算属性的区别在vue 3中使用watchE

Java将时间戳转换为Date对象的方法小结

《Java将时间戳转换为Date对象的方法小结》在Java编程中,处理日期和时间是一个常见需求,特别是在处理网络通信或者数据库操作时,本文主要为大家整理了Java中将时间戳转换为Date对象的方法... 目录1. 理解时间戳2. Date 类的构造函数3. 转换示例4. 处理可能的异常5. 考虑时区问题6.

每天认识几个maven依赖(ActiveMQ+activemq-jaxb+activesoap+activespace+adarwin)

八、ActiveMQ 1、是什么? ActiveMQ 是一个开源的消息中间件(Message Broker),由 Apache 软件基金会开发和维护。它实现了 Java 消息服务(Java Message Service, JMS)规范,并支持多种消息传递协议,包括 AMQP、MQTT 和 OpenWire 等。 2、有什么用? 可靠性:ActiveMQ 提供了消息持久性和事务支持,确保消

Java第二阶段---09类和对象---第三节 构造方法

第三节 构造方法 1.概念 构造方法是一种特殊的方法,主要用于创建对象以及完成对象的属性初始化操作。构造方法不能被对象调用。 2.语法 //[]中内容可有可无 访问修饰符 类名([参数列表]){ } 3.示例 public class Car {     //车特征(属性)     public String name;//车名   可以直接拿来用 说明它有初始值     pu

如何掌握面向对象编程的四大特性、Lambda 表达式及 I/O 流:全面指南

这里写目录标题 OOP语言的四大特性lambda输入/输出流(I/O流) OOP语言的四大特性 面向对象编程(OOP)是一种编程范式,它通过使用“对象”来组织代码。OOP 的四大特性是封装、继承、多态和抽象。这些特性帮助程序员更好地管理复杂的代码,使程序更易于理解和维护。 类-》实体的抽象类型 实体(属性,行为) -》 ADT(abstract data type) 属性-》成

Java基础回顾系列-第二天-面向对象编程

面向对象编程 Java类核心开发结构面向对象封装继承多态 抽象类abstract接口interface抽象类与接口的区别深入分析类与对象内存分析 继承extends重写(Override)与重载(Overload)重写(Override)重载(Overload)重写与重载之间的区别总结 this关键字static关键字static变量static方法static代码块 代码块String类特