深入探究Prototype与原型链

Prototype_proto_的区别

  • Prototype属性(原型对象)。是函数(Function)独有的属性,非函数类对象没有这个属性。作用是,作为一个“公共仓库”,用来放置希望所有实例共享的属性和方法。
  • _proto_属性(隐式原型指针)。JavaScript 中的每一个对象(包含函数、数组、实例等)都有。作用是,指向创建该对象的构造函数的 prototype。它就是对象用来“向上查找”的连接线。

一个函数对象拆解后的实际样子

要彻底搞懂一个函数对象在内存和浏览器控制台里的完整拆解结构,我们可以将它划分为 4 大核心板块

在 Chrome 控制台中运行 console.dir(Foo) 并展开后,你能看到它的全部内部组成:

// 1. 打印函数对象(查看它的 prototype 和 [[Prototype]])
function Foo() {}
Foo.prototype.sayHi = function() {};

console.dir(Foo);

函数对象的 4 大结构板块

第一块:函数自身的基础元数据(自身属性)

  • length: 0
    • 含义: 函数的形参个数。因为 Foo() 没有写入参,所以是 0。
  • name: "Foo"
    • 含义: 函数的名字字符串。

第二块:prototype(给 future 实例准备的共享仓库)

展开 ▼ prototype: 这一段,就是将来使用 new Foo() 生产出的实例对象能共享的所有内容:

  • sayHi: ƒ ()
    • 含义: 挂载在 Foo.prototype 上的方法。
  • constructor: ƒ Foo()
    • 含义: 原型对象上的“反向指针”,表明这个原型仓库是属于 Foo 函数的。
  • ▼ [[Prototype]]: Object
    • 含义: Foo.prototype 本身也是一个普通的 JavaScript 对象,所以它的原型指向了最高顶层的 Object.prototype

第三块:已废弃的旧语法属性与引擎标记

  • arguments: null / caller: null
    • 含义: 传统 JS 中用来获取“传入参数”和“调用当前函数的父级函数”的旧属性。在现代 ES6 严格模式下已被禁用,所以显示为 null
  • [[FunctionLocation]]: prototype.html:10
    • 含义: 浏览器引擎的标记,提示这个函数是在代码文件 prototype.html 的第 10 行被定义的。

第四块:▼ [[Prototype]]: ƒ ()(Foo 函数自己继承的能力)

这是最关键的隐式原型指针(即 Foo.__proto__。因为函数本身也是一个对象,所以它也需要继承“作为函数”的能力,这里指向的是 Function.prototype

  • apply: ƒ apply() / bind: ƒ bind() / call: ƒ call()
    • 含义: 函数独有的能力。你平时能写 Foo.call()Foo.apply(),就是因为在这个原型层级里找到了这些内置方法。
  • constructor: ƒ Function()
    • 含义: 说明函数 Foo 是由 JavaScript 内置的 Function 构造函数创建出来的。
  • toString: ƒ toString()
    • 含义: 将函数源码转为字符串的方法(如 Foo.toString())。
  • ▼ [[Prototype]]: Object
    • 含义: Function.prototype 本身也是个对象,它的原型同样指向顶级 Object.prototype
  • ▼ [[Scopes]]: Scopes[0]
    • 含义: Function.prototype 的作用域链。

第五块:▼ [[Scopes]]: Scopes[1](Foo 函数的作用域链)

  • 0: Global {window: ...}
    • 含义: 引擎记录的当前函数生效的上下文环境。这里显示它能访问全局的 window 对象。如果是闭包函数,这里还会看到 Closure 作用域。

在控制台里看函数,记住两行关键展开项即可:

  1. 寻找 prototype, 看实例能调用的属性/方法(包含 sayHi)。
  2. 寻找 [[Prototype]], 看函数自己能调用的属性/方法(包含 call/apply/bind)。

prototype、[[Prototype]]与_proto_的关系

用最简单的话描述它们关系:[[Prototype]] 是真正的内存槽,__proto__ 是访问它的后门通道,而 prototype 只是函数手里拿着的预留仓库。

1、 [[Prototype]] 的正式定义

[[Prototype]] 的正式名称是“对象的内部槽”(Internal Slot),更具体的称呼是“内部原型”。在 ECMAScript 官方规范中:

  • 特征: 双方括号 [[...]] 表示这是 规范内部的私有属性,在传统的 JS 代码中是无法直接像 obj.[[Prototype]] 这样书写和访问的。
  • 正式名称: [[Prototype]] 内部槽(Internal Slot)。
  • 含义: 它是存在于每个 JavaScript 对象内部、由引擎维护的一个私有槽位,用来保存该对象的原型引用(即指向上一级原型对象或 null)。

2、 __proto__[[Prototype]] 的关系

简而言之:__proto__ 只是访问和修改内部槽 [[Prototype]] 的一种“历史遗留通道/访问器”。

映射关系:

  • [[Prototype]]真正的原型存储槽(存在于内存深处)。
  • __proto__ 是挂在 Object.prototype 上的一个 getter / setter 访问器属性(属性拦截器)。当读取 obj.__proto__ 时,底层实际上是在执行 Object.getPrototypeOf(obj),去获取真正的 [[Prototype]]

历史与规范地位:

  • 历史: __proto__ 最初只是浏览器厂商(主要是 Firefox/Chrome)私自添加的非标准扩展功能。
  • 现实: 到了 ES6,为了兼容网络上大量已经使用了 __proto__ 的老旧代码,官方规范不得不把它收录进标准,但明确将它放在了 附录 B(Annex B – 仅为了 Web 兼容性保留的特性) 中。

由于 __proto__ 性能较差(修改它会破坏引擎优化)且属于废弃边缘的附录规范,现代 JS 严格禁止在生产环境中使用 __proto__

规范推荐使用以下 官方标准 API 来操作 [[Prototype]]

操作需求不推荐(历史遗留)官方标准 API(推荐 )
获取 [[Prototype]]obj.__proto__Object.getPrototypeOf(obj)
修改 [[Prototype]]obj.__proto__ = newProtoObject.setPrototypeOf(obj, newProto)
创建时指定 [[Prototype]]Object.create(proto)

3.三者核心属性速查表

假设我们定义一个构造函数,并用它创建一个实例:

function Person() {}
const p = new Person();

它们三者在内存中的关系可以通过这个联动公式串联起来:

实例 p 的内部槽           实例 p 的访问后门           构造函数的预留仓库
p.[[Prototype]]   ===   p.__proto__   ===   Person.prototype

完整的逻辑接力过程:

  1. Person.prototype 预备资源: 函数 Person 诞生时,引擎会自动给它配一个 prototype 对象。你可以在上面挂载方法(如 Person.prototype.sayHi = ...)。
  2. new 操作做连接: 当你执行 new Person() 时,引擎创建了实例 p,并把 p 内部的 [[Prototype]] 槽位直接指向了 Person.prototype
  3. __proto__ 负责读取: 由于 [[Prototype]] 是私有槽位,在代码里写 p.[[Prototype]] 会报错,所以你可以通过 p.__proto__ 这个后门去读取或修改底层指向的 Person.prototype
属性名称谁拥有它?它是什么?它的真实作用
[[Prototype]]所有对象(普通对象、函数、数组等)内部私有槽位(存储在引擎深处)真正的原型指针。原型链查找时,引擎就是沿着它一路向上找的。
__proto__所有对象(通过继承 Object.prototype 获得)访问器属性(getter/setter 后门)读取或修改 [[Prototype]] 的历史通道。只是一个指向 [[Prototype]] 的快捷接口。
prototype仅函数对象(普通对象没有)普通属性(挂在函数上的对象)给未来实例准备的模板仓库。当你 new Foo() 时,新对象的 [[Prototype]] 就会连到这里。

函数的双重身份与两条原型链

二、 两种查找路径的具体对比

为了彻底理清,我们看看这两条链在查找时到底有什么区别:

1. 当函数扮演“普通对象/方法本身”时(寻找 Foo.xxx

此时,Foo 和一个 {} 没有任何本质区别,引擎直接从 Foo 自身的 [[Prototype]] 开始向上找:

  • 查找路线: Foo $\rightarrow$ Foo.[[Prototype]](指向 BarFunction.prototype) $\rightarrow$ Object.prototype $\rightarrow$ null
  • 触发场景: Foo.call()Foo.bind()Foo.staticMethod()

2. 当函数扮演“构造工厂”时(给实例 f1f1.xxx

此时,Foo 只是一个“借用身份的工具人”,真正参与原型链查找的是它手里拿着的那个 Foo.prototype(一个纯普通对象)

  • 查找路线: f1 $\rightarrow$ Foo.prototype $\rightarrow$ Foo.prototype.[[Prototype]](指向 Bar.prototype) $\rightarrow$ Object.prototype $\rightarrow$ null
  • 触发场景: const f1 = new Foo(); f1.sayHi()

三、 总结:底层原理相同,只是“发起点”不同

所以,说“函数的原型链查找方式和普通对象根本不同”:

  • 底层机制上: 不同(完全相同)。 无论是函数还是普通对象,引擎在找属性时,永远只认“当前对象”的 [[Prototype]] 这一根线,没有任何特权或特殊的查找算法。
  • 应用逻辑上: 对(确实要区分)。 普通对象只有一条单向链;而函数因为既有 [[Prototype]] 又有 prototype 属性,它同时身处两条互相平行的原型链中。

彻底理解JavaScript中的对象

JS对象迷惑性:理解本质

对于C++、Java、C#面向对象的语言而言,对象这个词都是很明确的:“对象”必须以“类(Class)”为模板实例化而来。

而JS中的对象概念,则宽泛得多。例如,以下代码中的staff 、p都是对象,甚至函数Person也是对象:

//普通对象
const staff = {
    name: "Tom",
    age: 18
};

//Promise对象
const p = new Promise(...);

//函数也是一个对象
function Person(name) {
  this.name = name;
}

这不禁让人困惑,JavaScript的对象到底指的是什么?

因为中文把 Object 翻译成“对象”,听起来特别抽象,实际上:

JS 里的“对象”并不是指某一种长相,而是指一种“可以被当成一个整体来管理,并且可以通过属性名找到内部东西”的值。

“对象”本质上只是一个包含键值对(Key-Value)的动态哈希表。

很粗浅的理解,对象就是一个可以装很多“属性”的东西。

认识了这个本质,我们很快就能理解了,staff,p,Person 长相完全不同,但它们在 JavaScript 的底层分类上都属于 Object。以下都是JS中的对象,只是创建它们的“模板”不同而已:

JavaScript Object

├── 普通对象
├── Promise对象
├── Array对象
├── Date对象
├── RegExp对象
├── Map对象
└── …

创建对象有很多种方式

例如,普通对象:

 const a = {};

其它类型对象:

 //Promise 对象
 const b = new Promise(...);
 //Array 对象。
 const c = [];
 //Date 对象
 const d = new Date();
 //Map 对象
 const e = new Map();
 //RegExp 对象
 const f = /abc/;
  //甚至得到的是函数对象
 const g = function() {};

不要再把对象单一理解成:

 { name: “Tom”, age: 18 }

而应该理解成:

对象 = 一个可以拥有属性和方法、可以被独立操作的实体。

于是:

                  Object
                    │
        ┌───────────┼────────────┐
        ↓           ↓           ↓
      普通对象     Array       Promise
        │           │           │
    name/age     push/length   then/catch

它们虽然功能完全不同,但都属于“对象”这个大类别。

HTTP方法的深度探源与RESTful API的设计思想

HTTP认知溯源

HTTP方法本质上就是一个普通的 ASCII 编码字符串,我们可以自定义HTTP方法。从这一点看每个http方法没有什么区别。但是,在应用层,考虑到缓存与网络效能、容错与重试机制、安全隔离与攻击防御等,浏览器、路由器、防火墙、CDN节点、NGINX等应用对各方法做了区别对待。

HTTP方法是在安全和幂等的理念下设计的,语义化的命名有助于互联网生态的协作,在缓存、爬虫、搜索、CDN分发、前后端数据交互等方面能更低成本协作。而REST则是一种设计API的理念,即使用 URI 来标识资源,使用标准的 HTTP 方法 来表示操作。RESTful则是符合这种理念的API。

即使写程序很多年的老鸟,能真正说清楚道明白HTTP方法的也不多,多数都只会使用而不知道所以然。每个人都知道使用GET、POST方法,使用也很熟练,但是底层设计思想并不一定了解。这里,来认真梳理一下HTTP方法背后设计的逻辑。

一. HTTP方法常见10种

HTTP 标准里常见的10 个:

方法主要作用
GET获取资源
POST提交数据 / 创建资源
PUT完整替换资源
PATCH部分修改资源
DELETE删除资源
HEAD获取响应头,不返回正文
OPTIONS查询服务器支持哪些方法/能力
CONNECT建立隧道
TRACE请求回显,用于诊断
QUERY用于查询搜索(2026年RFC10008新增)

(实际上HTTP方法数量多达几十种,这里只是列举常见的方法,详见文末)

二.HTTP方法两个核心设计理念:安全(Safe) 与 幂等(Idempotent)


注意:以上的标准是2014年颁布的RFC 7231,QUETY标准出自2026颁布的RFC 10008

FastAPI + HarfBuzz + Redis实现动态字体子集化实战

字形编译引擎(Text Shaping Engine)当今的绝对王者HarfBuzz,拥有极强性能,Android、Chromium、Firefox等主流应用底层都在用它。HarfBuzz有一个hb-subset 子集化模块,是专业的字体子集化引擎:

当你给它一段文本(比如 "动态字体子集化")和原字体时,HarfBuzz 会:
自动分析这几个汉字对应的 Glyph ID。
保留这几个字所需的矢量描边数据。
剔除其余上万个未用到的字形数据
重构并修复 .ttf 文件的 OpenType 内部索引表,确保输出的小字体符合国际标准,能在浏览器中被完美加载。

直观了解:HarfBuzz是做什么的

官方是这样介绍的:


harfbuzz-world.cc is a single-file build of HarfBuzz that drops into your own C or C++ project without any build-system glue, plus a live playground for it right here in your browser:

harfbuzz-world.cc 提供了一个单文件版的 HarfBuzz(HarfBuzz 整个库整合成一个可以直接编译的 .cc 文件。),可以直接放进你自己的 C/C++ 项目中,无需额外处理构建系统;同时,它还在浏览器中提供了一个可以实时操作的 HarfBuzz 实验场。


shape — the shaped glyph stream as a table, alongside an SVG preview.(字形整形 / 字形塑形)
subset — produce a font subset for the current text and download it.(子集化)
raster — pixel-perfect BGRA rendering at any size, blitted to a canvas.(光栅化/位图化)
vector — the same shaped text rendered to SVG and downloadable PDF.
gpu — slug-based GPU rendering via WebGL2.(GPU 渲染)

官网地址:https://harfbuzz-world.cc/



OpenType(包括排版规则、连字、变形、字形定位表等)也是一套极其复杂的标准规范,而 HarfBuzz 就是这套标准在全世界最权威、最完美的执行引擎(“编译器/解析引擎”)。 很多人以为:计算机显示文本 = 查字典(给一个字符编码,找到对应的字体形状画出来)。 如果全世界只有英文字母(A-Z),这确实很简单。但在现实世界中,文字的显示极其复杂:

—— 复杂连字(Ligatures)与形态变化:阿拉伯文同一个字母,在词首、词中、词尾、单独存在时,形状完全不同,而且必须连笔。印地文/泰文: 辅音和元音组合时,元音符号可能会跑到辅音的上方、下方甚至左边。英文在优雅的排版中,f 和 i 连着写时,f 的横线和 i 的点会融合成一个整体(fi 连字)。

—— 字形(Glyph)与字符(Character)不是一回事。字符(Unicode):是逻辑上的编码(如 U+0061 代表 a)。字形(Glyph):是字体文件(.ttf)中实际绘制出来的矢量图形。一个字符可能对应多个字形,多个字符也可能合成一个字形。

HarfBuzz 的核心工作,这串文本在当前字体下,应该使用字体库里的哪几个字形(Glyph ID)?每个字形应该放在什么坐标位置?字形之间的间距(Kerning)是多少?

说得更直白一些,HarfBuzz就是把字体规则给完美呈现出来,就像浏览器,要把庞大复杂的HTML/CSS/Javascript这些规则给视觉实现,HarfBuzz就是字体界的浏览器。

由于项目需要,我们需要做动态字体子集化,HarfBuzz是不二之选。我们采用的是Python FastAPI + HarfBuzz + Redis+Nginx+Vue3+pdf-lib前后端技术栈来实现。以下的记录是其中一些操作要点。

繁荣的根基:制度与人文的力量——读《Leaders》

繁荣的背后是自由的制度和对知识专业的尊重

中华民国在台湾的经济奇迹,不是偶然,尼克森原版回忆录,记述了两种制度在海峡两岸造成的巨大差距背后原因。经济繁荣背后是永恒的自由、对知识和市场的尊重,无它。

读尼克松(Richard Nixon)的原版回忆录《Leaders》,看到很多历史的细节,完全打破原来的认知。其中最震撼的一点就是关于台湾经济起飞原因的描述:

Chiang was an example of that rarest of political animals: the conservative revolutionary. The American Revolution succeeded in founding an orderly and free society because its leaders were essentially conservatives. They fought for freedoms that they had once possessed, but which had been taken away. The French Revolution foundered as it did partly because its leaders sought to achieve a purely intellectual and abstract vision that had no foundations in their national history.

蒋中正是一个极少见的、天生的政治领袖,他是一个保守主义革命志士。美国革命之所以能够成功建立一个有序且自由的社会,是因为其领袖本质上都是保守主义者,他们夺回了曾经有过但被剥夺了的自由。而法国革命之所以失败,部分原因在于其领导者试图实现一种纯粹理性的、抽象的幻想,而这种革命幻想在法国历史中毫无根基。

Chiang’s intenions resembled those of the Americans more than those of the French. He wanted to revivify Chinese tradition. He rejected its corruption by the old order. He fought against pervasive opium addiction and the still-common practice of foot binding. But he was not a democrat, even though he did introduce constitutional government. The problem, as he saw it,was not too little freedom but too much. China needed discipline, for as Sun Yat-sen had stated, “We have become a heap of sand.” The discipline Chiang sought, however, would release the creative and productive abilities of the Chinese people.

蒋中正的革命理念走的是美国路线,而非法国。他希望能复兴中国传统文化。他摒弃旧社会的腐败,取缔当时普遍存在的吸食鸦片和缠足陋习。他带领中国进入宪政,尽管他不是传统意义上的民主人士。在他看来,问题的核心不在于自由太少,而在于自由过度。中国需要秩序,正如孙中山所言:“中国人是一盘散沙” ,蒋中正所寻求的纪律,是为了释放中国人的创造力与生产力。

When implemented on Taiwan, his ideas produced an economic miracle.Though he received American economic aid through 1965, the amounts were so small that they cannot account for the country’s explosive economic growth. Economic statistics can never capture the tragedy that the Communist victory was for the Chinese people, but they do make some important points.The Communists collectivized agricultural production, and today the mainland produces less rice per capita than it did before the revolution. Chiang paid the landlords for their land and distributed it to the peasantry. The former landlords invested much of their money in industry while the government encouraged foreign investment. Today, Taiwan has a per capita income five times as great as that of the mainland. And the eighteen million Chinese on Formosa export about fifty percent more than the one billion on the mainland.

蒋中正的理念在台湾实施后,创造了经济奇迹。在1965年前,尽管他接受了美国的援助,但对这些援助金额微乎其微,不足以解释台湾经济的爆炸式增长。经济统计数据永远无法完全展现共产党胜利给中国人民带来的悲剧,但它们确实揭示了一些重要信息。共产党推行农业集体化,如今大陆的人均大米产量低于革命前。蒋中正向地主购买土地,并将土地分配给农民。原地主将大部分资金投资工业,且政府则鼓励外商投资。如今,台湾的人均收入是大陆的五倍(实际远不止五倍)。台湾的1800万人出口额比大陆的10亿人还高出约50%。


大量的“经济学家”在分析台湾经济起飞时,总是把美援描述为经济成长的核心因素;很多相关的书籍,包括现今台湾本土的出版物,都在夸大美援的作用;简中书籍文章更是故意误导读者,甚至把国府迁台时带走的黄金描述成关键因素。而处于美援中的核心人物尼克松,却认为美援微不足道,台湾早期的繁荣绝不能被简单归功于“美国给钱”。

尼克松1953至1961年担任美国副总统,正是这段时间,是美国援助中华民国的黄金期,是台湾经济从贫弱到起飞的关键时期。很多美国援助法案,都有其本人积极参与,他掌握了很多第一手数据。此后,他在1968-1974年出任美国总统,成了第一把手,对台政策他本人就是决策人。最关键的是,他在写《Leaders》这本回忆录时,已经是1980年代,因水门事件已离开政治多年,早已不担任任何公职,这种回头看的方式,使得他更加冷静与客观,更带有全局性的视野。

显然,尼克松认为,促成台湾经济繁荣最根本的因素,绝不是美援,而是蒋中正一而贯之的保守主义理念在台湾得到了实施。事实也证明,1965年6月30日美援正式停止后,台湾的增长反而更加迅猛。这有力地证明了增长的动力源源不断来自于内部制度,而非外部输血。如果仅仅靠援助就能产生经济奇迹,那么二战后接受美援更多的菲律宾、南越甚至许多拉美国家,却没有一个能复刻台湾的成功。很显然尼克森认为:援助是“外因”,而制度驱动的增长才是“内因”。

字体子集化优化在线动态渲染PDF

最近做了一个在线生成PDF,适时预览并可在线打印的系统。使用了pdf-lib.js(https://pdf-lib.js.org),作为前端创建PDF的框架,虽然很方便,但遇到一个棘手的问题:如何解决中文显示。

PDF文件不同于web文件(HTML+CSS),它的字体是嵌入文件的,而由于安全性和PDF文件本身机制的问题,并不能读取本地字体文件。而国际PDF阅读器规范中的 “Standard 14 Fonts”(14 种标准字体)中并没有中文字体,这就导致了在不嵌入中文字体的情况下,出现中文就会无法渲染。

Standard 14 Fonts

内置的 14 种标准字体 这 14 种字体全部属于 拉丁字母(英文) 系列,分为四大类:
1.无衬线 (Sans Serif)字体: Helvetica, Helvetica-Bold, Helvetica-Oblique, Helvetica-BoldOblique
2.衬线 (Serif/Roman) 字体:Times-Roman, Times-Bold, Times-Italic, Times-BoldItalic
3.等宽 (Monospace)字体: Courier, Courier-Bold, Courier-Oblique, Courier-BoldOblique
4.符号 (Symbol) :Symbol, ZapfDingbats

PDF 的设计初衷是 “所见即所得” (WYSIWYG)。它的目标是:不管你把这个文件发给谁,在手机上、打印机上还是 20 年后的电脑上,看到的排版必须一模一样。

封闭性:PDF 不信任阅读者的系统。它认为:“万一对方电脑里没有微软雅黑,我的排版不就乱了吗?”

机制:为了保证绝对一致,pdf-lib 必须执行 “字体嵌入 (Embedding)”。它需要把字体文件的每一个二进制数据“缝进” PDF 文件里。

面临的难点

这个系统需要兼容正体中文(繁体)和简体中文,而这种字体体量大得惊人。兼容性最好的Adobe全字符体——Super OTC 体积高达100M+,师出同门的Google Noto也差不多这个体量,如放到Web项目,简直是灾难,浏览器瞬间会被卡死。

于是只能放弃使用这种大字符集的字体,选择性地支持正体或简体。

但是单独的简体字体,体积也很大,思源Source Han Serif,单独一种字重的字体,也高达20M+,Noto也要10M-20M。如果要加载这么大的字体,配置一般的电脑尤其是内存小的电脑,会频繁出现假死的情况。

于是,只能想办法减少字体的体积。

通过万能的Gemini,终于有了解决办法:字体子集化。

说白了,就是从原字体中提取一部分字符,只要能覆盖自己常用的字符就行了。

方案:

1. 用集成工具做子集化。这是最常用的方式,工具例如,Fontmin(https://ecomfe.github.io/fontmin/)。

这种方式就是要准备“字体白名单”,也就是你要把那些你需要的字符一一列出来,让它把你需要的字符从庞大字库中“抠”出来。

可以到GitHub上下载常用中文字符3500字等整理好的字符集,导入Fontmin中。也可以自己整理用到的文字再加进去。

2.用 Python 做字体子集化。

#安装fonttools库
pip install fonttools brotli

#命令执行子集化
pyftsubset original.ttf --text-file=chars.txt --output-file=sub.woff2 --flavor=woff2
'''
其中
original.ttf:原始字体,如思源宋体
--text-file:字符白名单文件
--output-file输出字体
'''

#不指定字符白名单,只按照某个标准来子集化(如变成GB2312标准字符集)
pyftsubset NotoSerifSC-Medium.ttf --unicodes="U+0020-007E,U+00A0-00FF,U+2000-206F,U+3000-303F,U+4E00-9FA5" --output-file=label_gb2312.ttf --layout-features='*'

3.在线动态字体子集化

① Node.js 生态方案:Fontmin,基于 fonteditor-core,支持 TTF、EOT、WOFF、WOFF2 之间的转换。它支持插件机制,可以方便地动态剪裁。示例代码:

const Fontmin = require('fontmin');

const fontmin = new Fontmin()
    .src('fonts/SourceHanSansCN-Regular.ttf') // 原始 15MB 字体
    .use(Fontmin.glyph({
        text: '张三的PDF发票金额: ¥100.00' // 前端传过来的动态文本
    }))
    .use(Fontmin.ttf2woff2()) // 转换为更小的 woff2 格式
    .dest('dist/fonts');

fontmin.run((err, files) => {
    // 这里的 files[0].contents 就是剪裁后只有几 KB 的字体二进制流
});

西安事变,一场分裂的联合阴谋,中国历史倒退的悲剧

掩盖罪恶的谎言

彻底改变中国命运走向的西安事变,经过诸多学者的挖掘,真相早已大白。中国大陆,共党所编造的“教科书”对这段历史颠倒黑白的描述,不过是洗脑和掩盖罪恶的手段。西安事变自始至终都与“爱国”“抗日”毫无关系,不过是张与共党的各怀鬼胎的苟合,企图背靠苏俄,在西北占山为王,成立分裂中国的割据政权。

今天是西安事变89年。1936年12月12日,张杨发动震惊中外的西安事变,彻底改变了中国历史的走向。当时已被宣判死刑的共党得以苟延,从此在全国浴血抗战的大背景下,却一心躲在深山和敌后扩张坐大,并与日军秘密勾结背刺血战抗日的国军,为日后全面赤化中国大陆积蓄力量。中国人的命运,陡然在13年后进入近代史最黑暗、残酷和恐怖的时期,延续至今七十多年。

大陆的历史“教科书”里,讲述的这个“伟大”的“爱国”政变,不过是彻头彻尾的谎言。张杨这两个妄人,也被塑造成了“爱国将领”。颠倒黑白的伪史,不过是洗脑的目的。

一. 张学良发动西安事变,本意是效仿盛世才,割据西北,根本就不是为了所谓的“抗日”

2013年3月20日,一批和西安事變有關的秘密文件在紐約邦瀚斯(Bonhams)拍卖行拍賣,这批文件的是一个叫做海岚·里昂(Hyland L. Lyon)的美国人所有,他曾擔任張學良飛機駕駛員、機械師,他於1941年離開中國後,帶著個人行李、照片,以及受張家所托儲存私密物件的保險箱回到美國。据斯坦福大学胡佛研究所研究员郭岱君说,胡佛研究所能出的最高价为十万美元,竞不过出二十多倍高價(二百多万美元)的中共陕西西安文管所,最后被后者买走。

 

邦瀚斯(Bonhams)官网展示的部分文件

链接地址:https://www.bonhams.com/auction/21070/lot/3/chinese-communist-party-mao-zedong-zhou-enlai-et-al-holograph-letter-in-an-unknown-hand-with-secretarial-signatures-of-mao-zedong-zhou-enlai-bo-gu-and-zhang-wentian-6-pp-8vo-np-august-9-1936-an-18-point-letter-addressed-to-zhang-xueliang-outlining-a-strategy-to-convince-chiang-kai-shek-to-cooperate-with-communist-forces/

BBC的报道原文:https://www.bbc.com/zhongwen/trad/china/2013/03/130321_xian_incident_auction

共党官网的报道原文:http://dangshi.people.com.cn/BIG5/n/2013/0321/c85037-20868868.html