我看JavaScript(二)——程序形式与形态

接着讲JS动态性,JS第一大核心特性。语言动态性是指什么,其实社区并没有一个比较一致的看法,有的说数据类型动态,有的函数定义动态,我们看看维基的定义:

Dynamic programming language, is a class of high-level programming languages which, at runtime, execute many common programming behaviors that static programming languages perform during compilation. These behaviors could include extension of the program, by adding new code, by extending objects and definitions, or by modifying the type system.

将静态语言在「编译时的常见任务」,推迟到运行时,通过动态增加新代码、扩展对象及其定义,或修改类型系统来扩展程序本身。

再看MDN:

A dynamic programming language is a programming language in which operations otherwise done at compile-time can be done at run-time. For example, in JavaScript it is possible to change the type of a variable or add new properties or methods to an object while the program is running.

动态编程语言是一种能将「在编译阶段执行的操作」,转移到运行阶段时执行的语言。比如,在JavaScript中, 我们可以在程序运行中,改变变量的类型或者为一个对象增加一个新属性或者方法。

两者的意思大致相同,都提到了将「编译任务」推迟到运行时,维基表述的更为清楚一些,对这个语言设计的意图和内容实质——增加代码扩展程序——都说出来了,但是有几个有待深入的问题,第一,编译时的什么任务推迟到了运行时?第二,扩展程序是什么意思,有什么意义呢?具体又是怎么做到的呢?这是一堆大问题。

注:关于语言动态性,基本认同MDN和维基这个种半官方的的解释——主要是语言提供了API给程序员,在运行时扩展程序,但是有一点,我认为修改类型系统是比较边缘的,虽然弱类型的行为看起来具有动态性。弱类型的类型系统应该不属于语言动态特性。

程序理论的缺乏

事实上,我们目前不能解释清楚语言动态性确切涵义、意义和具体操作,因为解释的前提是我们对「程序形式或形态」有一种确定的认识,这需要一种完全成熟的「程序理论」,而这种理论是前所未有的。

当然,任何事情都由不成熟到成熟的过程,不成熟也有不成熟的一种解释与发展,所以不成熟并不妨碍我们对语言动态性的探讨,而大胆的探讨对推进程序理论的成熟性是有建设性的。

程序形式或形态

「程序的形式定义」是解释语言动态性的基础,因为语言要动态改变「程序形态」前必须知道改变的目标。变量的值,或变量的值类型都不能全面代表「程序形态」。

程序是一种精神构造

程序形式简单说,就是“程序是什么”,一般常见定义是:

A computer program is a collection of instructions that performs a specific task when executed by a computer. A computer requires programs to function.

程序是完成特定任务的指令序列。

这个定义很直观易懂,但远远没有全面刻画程序的存在形态。

任何事物都有一种存在形态(外在形式和内在结构)和存在意义,而计算机程序的存在形态很特别,它有别于实物的物质构造,是一种精神构造。程序的实质是一种认知计算,是对人逻辑认识过程的模拟,而执行程序的机器是一种非智能的“大脑”,智力有限。程序的存在意义是完成计算,得到一个计算结果。程序执行的过程是“弱智”机器遵循程序的指令序列不断改变程序状态,状态的最后是期望的计算结果。程序极像人演算的代数方程。

其实程序与机器是高度依赖的,程序的存在依赖的机器的执行。如果硬要将程序独立分析,那么程序的「属」应该是指令(语言上的语句),指令序列还是指令。任何有用的程序都是复合多条指令或语句的,程序的常规观念并不影响单条指令语句也是程序这个事实。

注:裸机指令还不能完全等同高级语言的语句,因为操作符可能产生中间值,可能只是表达式,并不改变机器状态,像加法指令。

程序的复合方式

「程序」到底指单条,还是多条语句,取决解释上下文。常规的「程序」概念是指由多条语句组成,完成一项状态多变的计算任务。多条语句会增加「程序」的结构涵义。对此种程序存在形态主要在对「程序的复合方式」上。我们有过编程经验的大概明白如下的概念图:

program

上图的核心可以定位为语句,向内分解为表达式,表达式再分解为操作数和操作符;向外组合成块(子句block)、函数(特殊分支子句)、类对象和模块。以下分别就这些重要程序结构概念进行分析。

语句

什么算语句?完整的语句应该是能改变机器状态,产生一个计算结果(或是多步的中间结果)的程序指令,否则只有值的只是表达式。特别注意的是,声明语句严格上讲与计算语句是有区别的,因为声明是写给编译器读的,指导编译器进行优化,计算语句才是写给程序机器的。

程序语句按改变「机器的不同类型状态」可以分为计算语句和分支语句。机器有两个主要的状态,第一是存储状态,第二是指令位置(下一条执行指令位置,也就所谓程序计数器)。计算语句主要是赋值语句,当然创建新值的语句也是常见改变程序状态的计算;分支语句则包括了条件分支(if else)、循环分支(for while)和函数调用(函数是特殊的分支)

表达式

机器的状态改变需要数据,这些数据可能需多个中间处理。表达式(及其嵌套)表征这种处理。

语句块

语句块或子句是多条语句简单的线性结集。块一般用作分支的子句。

函数

一般对函数的认识是,函数是带参数并且可以返回值的子程序(过程),可反复调用。

Functions are "self contained" modules of code that accomplish a specific task. Functions usually "take in" data, process it, and "return" a result. Once a function is written, it can be used over and over and over again. Functions can be "called" from the inside of other functions.

函数实质其实是一种特殊的分支子句——带数据跳转某处并可以返回原处。普通分支子句只是线性分割程序作用,而函数将子句的数据进行了抽象,并且返回计算结果,函数的这种对分支子句功能的丰富和计算操作的抽象,让函数对「构造复合的程序」有了非凡的意义。

我们的目标是了解程序存在形态,寻求一种通用的程序理论,而这种理论很大一部分内容是关于如何在语句的基础上,构造出更复杂的程序。要构造复杂的程序,各程序分支子句必须是独立了,并且必须是非线性分割。普通分支子句(例如if)具有一定的独立性——左右分支是无涉的,但是是一种线性分割。函数则可这做到这一点,函数是复杂程序的构件模板。

类对象

函数对一段通用程序的进行封装,提高了程序复合性,增加了计算词汇。类或对象则在函数的基础之上,增进了程序复合程度而已。

面向对象的概念风靡全球,但其实类或对象只是对一个以上的函数的封装。很多人解释不清楚JS闭包是什么,其实就是个类对象。为什么闭包一定是个内嵌函数?一定会返回一个函数或者对象呢?关于类对象对构造复合程序有相当多内容,此处从略。

模块

模块就是对多相关类对象的封装。

参考

裸男
Nakeman.cn 2023 Build by Gatsby and Tailwind, Deploy on Netlify.