2021年9月

下面这几种方法添加事件监听有什么区别?

  1. el.addEventListener('click', handleClick);
  2. el.setAttribute('onclick', 'handleClick()');
  3. <div onclick="handleClick()"></div>
https://wangdoc.com/javascript/events/model.html

请问 document.body.childNodesdocument.querySelectorAll() 得到的 NodeList,有哪些区别?

https://wangdoc.com/javascript/dom/nodelist.html

请问如何让数字可扩展?比如:const a = [...5] 就可以创建一个 [1, 2, 3, 4, 5] 这样的数组?

Number.prototype[Symbol.iterator] = function*() {
 let i = 0;
 let num = this.valueOf();
 while (i < num) {
   yield i++;
 }
} 

给出下面这样的一个类:

class AnObject {
  constructor(number) {
    this.number = number;
  }
}

如何让它的实例相加时,得到的值为 number 相加的值?

class AnObject {
  constructor(number) {
    this.number = number;
  }

  // 改变下面这样的就可以了
   valueOf() {
    return this.number;
  }
}

假设我们现在有一个 HTML 元素:

<div class="field">
    <label>用户名</label>
    <input />
</div>

通过 CSS 样式可以为 label 添加

.field label:after {
  content: ':';
}

但是这样并不能实现我真正的需求,我的需求是,当系统使用英文时,使用 : ,使用中文时,使用 ,那么,请问:我们该如何动态的改变 label:after 中的 content 的值?假设不允许通过 JavaScript 动态更新 CSS 样式,也不允许动态改变元素的类与 ID,那又该如何实现?

在 C++ 之前,有一个叫 C 的家伙

C 语言是由贝尔实验室的 Dennis Ritchie 于 1972 年发明的一种操作系统编程语言(专门用于开始操作系统的语言),Ritchie 的主要目的,是开发出一种易于编译、能高效访问内容、生成高效代码且不依赖于其它程序的简约语言,作为一门高阶语言,它给开发者提供了很大的控制权,同时又保留了硬件与操作系统的独立性,不必为每个平台重写代码。

由于 C 语言是如此的高效和灵活,所以,在 1973 年,Ritchie 与 Ken Thompson 使用 C 重写了大部门 Unix 操作系统的代码,许多以前的操作系统都是采用汇编语言编写的,只能运行于特定的硬件平台之上,而 C 具有出色的可移植性,允许 Unix 在许多不同类型的计算机上轻松的重新编译,此时,C 语言与 Unix 命运就已经联系在了一起。

1978 年,Brian Kernighan 与 Dennis Ritchie 出版了一本名为《C 程序设计语言》的书,这本书通常被称为 K&R,为该语言提供了非正式的规范,并成为实事上的标准,当需要最大的可移植性时,程序员会坚持 K&B 中的建议,因为当时大多数的编译器都是按照 K&R 的标准实现的。

1983 年,美国国家标准协会(ANSI)成立了一个委员会来建立 C 语言的标准,1989 年,他们完成并发布了 C89 标准,通常称为 ANSI C,1990 年国际标准化组织 ISO 采用了 ANSI C ,这个版本称为 C90,编译器最终符合 ANSI C/C90,并且需要最大可移植性的程序被编码为该标准。

1999 年,ANSI 委员会发布了一个新版本的 C,称为 C99,C99 采用了许多已经作为扩展进入编译器的特性,或者已经在 C++ 中实现的特性。

好吧,在我接触计算机的近 28 年后,我终于鼓起勇气要正式开始学习 C++ 了,虽然以前也计划过很多次,但是这次看上去是认真的了,秉着学习记笔记的良好习惯,这个博客估计又会有几个月的频繁更新了。

为什么要学?

这个问题其实很简单,事情是这样的,最近一直在开发公司的一个名为 XXXX 的项目,里面涉及到大量的地图业务,最开始我们使用了开源的 OpenLayer 库作为底层地图支持,现在功能都开发得差不多了,自己感觉二维的平面地图太弱鸡了,我想整成一个建筑的三维地图,然后就开始接触 WebGL,以前都是使用的 2D 渲染,上周花了一个星期系统性的学习了 WebGL 相关的知识,写了 Hello, 3D 应该是没啥大问题了,但是总想再进一步,就更深入的了解 WebGL,啊,原来就是个 OpenGL 的绑定啊,那我就计算更深入地学习下 OpenGL,然后发现,OpenGL 的知识基本上都是拿 C++ 语言当示例,好吧,那等我先把 C++ 学会了再回来学 OpenGL,事儿就是这么个事儿,你看,我是不是有点忘记初心了?

如何让一个类的属性全部可选?

比如我有下面这样一个类型:

type User = {
  username: string;
  gender: 'male' | 'female';
  age: number;
  bio: string;
  password: string;
}

User 类型要求所有属性都必须有值,所以:

const user: User = {
  username: 'awesomeuser',
};

是不可行的,会提示:

类型“{ username: string; }”缺少类型“User”中的以下属性: gender, age, bio

如何让它可行?使用 Partial 即可:

const user: Partial<User> = {
  username: 'awesomeuser'
}

Partial 内置内型的作用就是让一个已定义了的类型的所有属性变得可选,具体实现如下:

/**
 * Make all properties in T optional
 */
type Partial<T> = {
    [P in keyof T]?: T[P];
};

如何让一个类型的属性全部必填?

假设我们有下面这样一个类型定义:

type User = {
  username: string;
  age?: number;
  gender?: 'male' | 'female'
  bio?: string;
  password?: string;
}

然后从服务器后端拿到了一系列用户数据的结果:

const users: User[] = await api.users.list();

此时我们尝试去访问用户的 age 进行比较,想要取出 age > 18 的用户:

const filteredUsers = users.filter(user => user.age > 18);

此时你的 IDE 是不是提示你:对象可能为“未定义”。?为什么?因为我们定义了 age 本身就是可选的属性,未定义该属性时,它的值就是 undefined,而在 typescript 中, undefined 是不允许与 number 类型进行比较的,但是此时其实我们是能很确定 api.users.list 接口返回给我的,要么是抛出错误,要么给到我的就一定是带了 age 值的 User 类型数据,所以,我是完全可以去比较的,但是由于 TypeScript 并不知道你加载的数据是 User 还是所有属性都已经有值的特殊的 User,那怎么办?什么 Required 即可。

const users: Required<User>[] = [];

或者让 api.users.list() 的返回类型就是 Required<User>[] 即可。

如何自己实现 Required

/**
 * Make all properties in T required
 */
type Required<T> = {
    [P in keyof T]-?: T[P];
};

如何让一个类型的所有属性变成只读?

假设我们现在在写一个系统,当用户登录之后,会共享一个 User 对象给所有组件,但是只允许他人读取其值,不允许他人修改,但是我们定义的 User 是整个系统共用的,有编辑功能的页面也在使用这个类型定义,它们是需要能修改用户数据的,那怎么办?

使用 Readonly 即可:

const user = {
  username: "awesomeuser",
  gender: "male",
  age: 19,
  bio: "Aha, insight~",
};

const sharedUser = user as Readonly<User>;

sharedUser.username = "new Name";

此时,IDE 就会告诉你无法分配到 "username" ,因为它是只读属性。

注意:TypeScript 只作静态类型校验,但是并不表示这些值真的不能被修改了,如果真的要创建一个真正从程序上都不能被修改的对象,请问怎么解决?

我想有一个类,只具有另一个类的部分属性定义

还是那个 User,我想定义一个 Login 类型,它专门用于登录时的类型,但是我又不想重新去写一遍 usernamepassword 的类型定义(虽然在本例中,重新写一遍也很简单,但保不齐哪天就会遇到一个十分复杂的类型定义呢?),怎么办?使用 Pick 即可。

type User = {
  username: string;
  gender?: "male" | "female";
  age?: number;
  bio?: string;
  password?: string;
};

type Login = Pick<User, "username" | "password">;

const login: Login = {
  username: "pantao",
};

你们也看到了,Login 类型还是只有 username 是必填的,password 是可选的,这与我们的要求不符,怎么办?加上 Required 啊。

type User = {
  username: string;
  gender?: "male" | "female";
  age?: number;
  bio?: string;
  password?: string;
};

type Login = Required<Pick<User, "username" | "password">>;

const login: Login = {
  username: "pantao",
};

此时就会要求你必须输入 password 了。

如何自己实现 Pick

/**
 * From T, pick a set of properties whose keys are in the union K
 */
type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};

如果快速定义一个类型中,具有相同数据类型的属性?

假设我们现在在开发一个商城系统,每一个 Store 都有一个店长,一个仓库管理员,一个收银员,他们都关联到了 User 上面,此时你可以这样定义 Store 这个类型:

type User = {
  username: string;
  gender?: "male" | "female";
  age?: number;
  bio?: string;
  password?: string;
};

type Store = {
  name: string;
  location: string;
  leader: User;
  warehouseKeeper: User;
  cashier: User;
};

当然,你还可以这样定义:

type Store = Record<'leader' | 'warehouseKeeper' | 'cashier', User> & {
  name: string;
  location: string;
}

两种方式,哪种更好,当然各有优劣,但是假设我们遇到下面这样的一个情况:

type User = {
  username: string;
  gender?: "male" | "female";
  age?: number;
  bio?: string;
  password?: string;
};

const users: User = [
  {
    username: "awesomeuser",
  },
  {
    username: "baduser",
  },
  {
    username: "gooduser",
  },
];

为了访问方便,我想将 users 变量转换成一个属性名称为 users 数组索引,值为 users 数组元素(即 User 类型)的对象,怎么定义这样的一个类型?

你可以这样:

type UserSet = { [key: number]: User };

const userSet: UserSet = users.reduce(
  (set, user, index) => ({ ...set, [index]: user }),
  {} as UserSet
);

你也可以这样:

type UserSet = Record<number, User>;

如何自己实现 Record

/**
 * Construct a type with a set of properties K of type T
 */
type Record<K extends keyof any, T> = {
    [P in K]: T;
};

博客,从时记,到日记,再到周记,然后到月记,现在都成年记了……