目录
1.javascript原型链(经典函数构造器 + prototype)
四、如何使用 TypeScript 构建类型安全的 DApp 项目
2. 使用 web3.js 或 ethers.js 连接以太坊并调用合约(示例用 ethers.js)
3. 使用 TypeScript 定义事件类型并监听合约事件
4. 使用类型安全的 ABI 绑定工具(如 TypeChain)
一、ES6+ 语法
“ES6+ 语法”指的是 ECMAScript 2015(即 ES6)及其之后版本的 JavaScript 语言新特性和语法。简单说,就是现代 JavaScript 的新语法和功能。
常见 ES6+ 语法包括:
let
和const
变量声明箭头函数
() => {}
模板字符串
`Hello ${name}`
解构赋值
默认参数
展开运算符
...
Promise 和 async/await 异步处理
类(class)和继承
模块导入导出
import
和export
Symbol 类型
Map 和 Set 数据结构
生成器函数
function*
可选链操作符
?.
空值合并运算符
??
1. let
和 const
变量声明
let a = 10; // 可变变量
const b = 20; // 常量,不能重新赋值
// b = 30; // 会报错
解释: let
声明的变量有块级作用域,const
声明常量,值不能变。
2. 箭头函数 () => {}
const add = (x, y) => x + y;
console.log(add(2, 3)); // 5
解释: 箭头函数写法简洁,并且不绑定自己的 this
。
3. 模板字符串
const name = 'Alice';
console.log(`Hello, ${name}!`); // Hello, Alice!
解释: 用反引号 `
包裹,可以直接嵌入变量和表达式。
4. 解构赋值
const person = {name: 'Bob', age: 25};
const {name, age} = person;
console.log(name, age); // Bob 25
const arr = [1, 2, 3];
const [first, second] = arr;
console.log(first, second); // 1 2
解释: 从对象或数组中快速提取值赋给变量。
5. 默认参数
function greet(name = 'Guest') {
console.log(`Hello, ${name}`);
}
greet(); // Hello, Guest
greet('Alice'); // Hello, Alice
解释: 函数参数可以设置默认值。
6. 展开运算符 ...
const arr1 = [1, 2];
const arr2 = [...arr1, 3, 4];
console.log(arr2); // [1, 2, 3, 4]
const obj1 = {a: 1, b: 2};
const obj2 = {...obj1, c: 3};
console.log(obj2); // {a:1, b:2, c:3}
解释: 展开数组或对象,合并或复制。
7. Promise 和 async/await 异步处理
// Promise
function fetchData() {
return new Promise(resolve => {
setTimeout(() => resolve('data'), 1000);
});
}
fetchData().then(data => console.log(data)); // data
// async/await
async function asyncFetch() {
const data = await fetchData();
console.log(data);
}
asyncFetch(); // data
解释: 用 Promise 处理异步,async/await 语法更简洁。
8. 类(class)和继承
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise.`);
}
}
class Dog extends Animal {
speak() {
console.log(`${this.name} barks.`);
}
}
const d = new Dog('Rex');
d.speak(); // Rex barks.
解释: ES6 引入类语法,更接近传统面向对象。
9. 模块导入导出 import
和 export
// utils.js
export function sum(x, y) {
return x + y;
}
// main.js
import {sum} from './utils.js';
console.log(sum(2, 3)); // 5
解释: 支持模块化开发,导入导出代码片段。
10. Symbol 类型
const sym = Symbol('desc');
const obj = {};
obj[sym] = 'value';
console.log(obj[sym]); // value
解释: Symbol 是一种独一无二的标识符,常用作对象属性键,避免命名冲突。
11. Map 和 Set 数据结构
const map = new Map();
map.set('a', 1);
console.log(map.get('a')); // 1
const set = new Set([1, 2, 2, 3]);
console.log(set); // Set {1, 2, 3}
解释: Map 是键值对集合,Set 是无重复值的集合。
12. 生成器函数 function*
function* gen() {
yield 1;
yield 2;
yield 3;
}
const g = gen();
console.log(g.next().value); // 1
console.log(g.next().value); // 2
解释: 生成器可暂停执行,逐步产出值。
13. 可选链操作符 ?.
const obj = {a: {b: 10}};
console.log(obj.a?.b); // 10
console.log(obj.x?.b); // undefined 不报错
解释: 访问嵌套属性时安全,不会因中间值为 null 或 undefined 报错。
14. 空值合并运算符 ??
const foo = null ?? 'default';
console.log(foo); // default
const bar = 0 ?? 42;
console.log(bar); // 0
解释: 当左侧是 null 或 undefined 时,返回右侧值。
二、Javascript闭包
闭包(Closure)示例
function outer() {
let count = 0;
return function inner() {
count++;
console.log(count);
}
}
const counter = outer();
counter(); // 1
counter(); // 2
counter(); // 3
解释:
函数
outer
返回了一个内部函数inner
。inner
函数可以访问outer
的变量count
,即使outer
已经执行完毕。这种函数和其访问的变量环境形成的组合,就叫闭包。
闭包常用来实现私有变量和数据封装。
三、原型链
1.javascript原型链(经典函数构造器 + prototype)
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log(`Hello, my name is ${this.name}`);
};
const alice = new Person('Alice');
alice.sayHello(); // Hello, my name is Alice
console.log(alice.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__); // null
解释:
每个对象都有一个内部属性
[[Prototype]]
(常用__proto__
访问),指向它的原型对象。当访问对象的属性或方法时,如果自身没有,会沿着
[[Prototype]]
一层层往上找,这个查找链就是原型链。上面例子中,
alice
访问sayHello
方法时没在自身属性里找到,就去它的原型对象Person.prototype
查找。Person.prototype
的原型是Object.prototype
,这构成了原型链的多层关系。Object.prototype
的原型是null
,链条终点。
2.TypeScript 版本(class 语法)
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
sayHello() {
console.log(`Hello, my name is ${this.name}`);
}
}
const alice = new Person('Alice');
alice.sayHello(); // Hello, my name is Alice
console.log(Object.getPrototypeOf(alice) === Person.prototype); // true
console.log(Object.getPrototypeOf(Person.prototype) === Object.prototype); // true
console.log(Object.getPrototypeOf(Object.prototype)); // null
解释:
TypeScript 使用
class
关键字声明类,语法更现代,代码更清晰。sayHello
是类的方法,实际挂载在Person.prototype
上,实例通过原型链访问。Object.getPrototypeOf()
用来获取对象的原型,效果和__proto__
类似,但更标准安全。原型链关系和JavaScript版本完全一样:实例原型 → 类的
prototype
→Object.prototype
→null
。
3.总结
JavaScript版本用构造函数和显式原型,适合传统理解原型链机制。
TypeScript版本用类语法,更符合现代代码风格,本质上还是基于JavaScript原型链实现。
三、作用域链
作用域链简介
作用域链是当访问一个变量时,JavaScript 引擎从当前作用域开始,逐级向上查找变量的过程。
这保证了内层函数可以访问外层函数的变量。
作用域链和原型链不同,作用域链是关于变量查找的执行环境机制。
1. JavaScript 版本示例
function outer() {
const a = 10;
function inner() {
const b = 20;
console.log(a + b); // 30
}
inner();
}
outer();
解释:
inner
函数内部访问变量a
,它不在自身作用域中。于是它查找外层作用域
outer
,找到了变量a
,然后计算并输出结果。这就是作用域链:
inner
→outer
→ 全局。
2. TypeScript 版本示例
TypeScript 的作用域链机制和 JavaScript 一样:
function outer() {
const a: number = 10;
function inner() {
const b: number = 20;
console.log(a + b); // 30
}
inner();
}
outer();
解释:
语法和JavaScript几乎一致,只是变量类型显式声明了。
作用域链机制完全相同,内层函数能访问外层函数的变量。
总结
作用域链保证了变量从内层到外层逐级查找。
它是运行时上下文环境的一部分,和闭包密切相关。
JavaScript 和 TypeScript 的作用域链规则一致,TS 只是加了类型。
四、如何使用 TypeScript 构建类型安全的 DApp 项目
下面给你关于“使用 TypeScript 构建类型安全的 DApp(去中心化应用)项目”的几个核心知识点,附带简短代码示例和解释。
1. 使用 TypeScript 定义智能合约接口类型
// 定义智能合约函数的接口
interface MyContract {
methods: {
balanceOf(address: string): { call(): Promise<string> };
transfer(to: string, amount: string): { send(): Promise<void> };
};
}
解释:
通过接口定义智能合约方法,确保调用时参数类型和返回类型明确,减少错误。
2. 使用 web3.js 或 ethers.js 连接以太坊并调用合约(示例用 ethers.js)
import { ethers } from 'ethers';
async function getBalance(contract: MyContract, address: string): Promise<string> {
const balance = await contract.methods.balanceOf(address).call();
return balance;
}
解释:
函数参数用类型接口约束 contract
,保证传入合约实例符合预期方法,address
是字符串。
3. 使用 TypeScript 定义事件类型并监听合约事件
interface TransferEvent {
from: string;
to: string;
value: string;
}
contract.on('Transfer', (from: string, to: string, value: string) => {
const event: TransferEvent = { from, to, value };
console.log('Transfer event:', event);
});
解释:
定义事件结构接口,事件监听回调参数用类型标注,方便后续类型检查和自动补全。
4. 使用类型安全的 ABI 绑定工具(如 TypeChain)
// 生成的合约类型(伪代码)
import { MyContract } from './types';
const contract: MyContract = getContractInstance();
const result = await contract.balanceOf('0x123...');
解释:
TypeChain 等工具根据合约 ABI 自动生成 TypeScript 类型,调用合约更安全,减少运行时错误。
5. 总结
用 TypeScript 接口和类型定义合约方法和事件,保证调用和监听的类型安全。
使用 ethers.js 或 web3.js 结合类型定义调用智能合约。
通过 TypeChain 等工具生成合约类型代码,减少手写错误。
五、泛型
概念
泛型可以让函数、类或接口在使用时指定类型,而不是在定义时就固定死,提高了代码复用性和类型安全。
JavaScript 本身没有泛型,泛型是 TypeScript(以及其他静态类型语言)提供的类型系统特性,用来增强代码的类型安全和复用性。
简单来说:
JavaScript 是动态类型语言,变量和函数的参数类型在运行时确定,没有静态类型检查。
TypeScript 在 JavaScript 基础上加了类型系统,其中就包括泛型,可以在编译阶段帮你检查类型,避免运行时错误。
所以,泛型是 TypeScript 的特色,JavaScript 没有对应的语法和概念。
1. 泛型函数示例
function identity<T>(arg: T): T {
return arg;
}
const str = identity<string>('hello'); // str 类型是 string
const num = identity<number>(123); // num 类型是 number
解释:
identity
是一个泛型函数,<T>
是类型参数,代表调用时传入的具体类型。传入参数和返回值类型都与
T
一致,调用时指定类型,保证类型安全且复用性强。
2. 泛型接口示例
interface Box<T> {
value: T;
}
const box1: Box<string> = { value: 'hello' };
const box2: Box<number> = { value: 100 };
解释:
Box
是一个泛型接口,成员value
的类型由外部指定。方便用同一个接口定义不同类型的对象。
3. 泛型类示例
class Stack<T> {
private items: T[] = [];
push(item: T) {
this.items.push(item);
}
pop(): T | undefined {
return this.items.pop();
}
}
const stack = new Stack<number>();
stack.push(10);
console.log(stack.pop()); // 10
解释:
泛型类
Stack
支持存放任何类型的元素,且保证类型一致性。实例化时指定具体类型,保证操作时类型安全。
4. 泛型约束示例
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
loggingIdentity('hello'); // 输出 5
loggingIdentity([1, 2, 3]); // 输出 3
// loggingIdentity(123); // 报错,number 没有 length 属性
解释:
泛型约束保证传入的类型必须有
length
属性。防止传入不符合约束的类型。
六、接口
概念
接口用于定义对象的结构,让代码更规范,可扩展性更好。
1. TypeScript 接口示例
interface User {
id: number;
name: string;
email?: string; // 可选属性
}
function greet(user: User) {
console.log(`Hello, ${user.name}`);
}
const user1 = { id: 1, name: 'Alice' };
greet(user1);
解释:
interface User
定义了一个结构类型,规定对象必须有id
和name
,email
可选。函数
greet
形参要求是User
类型,确保传入对象符合接口。
2. JavaScript 中模拟接口(无类型检查)
JavaScript 没有接口,但你可以用约定或运行时检查实现类似效果:
function greet(user) {
if (typeof user.id !== 'number' || typeof user.name !== 'string') {
throw new Error('Invalid user object');
}
console.log(`Hello, ${user.name}`);
}
const user1 = { id: 1, name: 'Alice' };
greet(user1);
解释:
通过函数内部手动检查对象属性类型,保证参数符合预期结构。
但没有编译时类型检查,容易出错且不够方便。
总结:
TypeScript接口:静态类型检查,定义对象结构,是编译时用来提升代码健壮性的工具。
JavaScript:无接口概念,只能靠编码习惯和运行时检测保证对象结构。
应用场景
API 数据模型定义
前后端数据类型统一
高度协作项目中约定字段结构
七、装饰器
下面是 TypeScript 中的 装饰器(Decorator) 的讲解、使用条件、代码示例和解释。
📌 一、装饰器是什么?
装饰器是对类、方法、属性或参数的增强,是元编程的一种形式。需要在 tsconfig.json
中开启 experimentalDecorators
。
装饰器是 一种特殊的语法,用于 修改类、类方法、属性或参数的行为。它本质上是一个函数。
装饰器是 TypeScript 的高级功能之一,需要在 tsconfig.json
中启用:
{
"compilerOptions": {
"experimentalDecorators": true
}
}
🧪 二、类装饰器示例
function Logger(constructor: Function) {
console.log('Class decorated:', constructor.name);
}
@Logger
class User {
constructor(public name: string) {}
}
✅ 解释:
@Logger
是一个类装饰器。它接收构造函数作为参数,在类定义时执行。
装饰器不会改变类本身行为,但可以扩展、增强或者记录日志。
📦 三、方法装饰器示例
function LogMethod(
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
const original = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Method ${propertyKey} called with`, args);
return original.apply(this, args);
};
}
class MathTool {
@LogMethod
add(a: number, b: number) {
return a + b;
}
}
const tool = new MathTool();
tool.add(2, 3); // 控制台打印日志
✅ 解释:
@LogMethod
修改add
方法,使其在执行前打印参数。可以用于日志记录、性能分析、权限验证等。
🏷️ 四、属性装饰器示例
function ReadOnly(target: any, propertyKey: string) {
Object.defineProperty(target, propertyKey, {
writable: false,
});
}
class Person {
@ReadOnly
name = 'Alice';
}
const p = new Person();
// p.name = 'Bob'; // ❌ 会失败(只读)
✅ 解释:
@ReadOnly
将属性设置为只读。
📌 五、装饰器的应用场景
日志打印(如方法调用参数)
权限控制
数据校验
自动绑定(如 Vue、NestJS 中常见用法)
AOP(面向切面编程)
NestJS 控制器、服务模块增强
Web3 签名校验封装