# 入门与进阶
概览,讲述编程所需的学习与实践。
# 代码
一个程序,经常被称为源代码
或者只是代码
,是一组告诉计算机要执行什么任务的特殊指令。
合法的格式与指令的组合规则被称为一种计算机语言
,有时被称作为它的语法
。
# 执行一个程序
程序需要被执行
,也称为运行这个程序
。
开发者编写的代码,并不能直接被计算机理解。因此需要通过特殊工具(解释器或编译器)将代码翻译为计算机可以理解的命令。
对于某些计算机语言,这种命令的翻译是每次程序运行时从上向下,一行接一行完成的,称之为解释
。
对于另一些语言,这种翻译是提前完成的,称之为编译
,当程序之后运行时,就可以直接运行已经翻译好,随时可以运行的计算机指令了。
JavaScript通常被断言为解释型
的语言,因为JavaScript源代码在它每次运行时才处理。但这并不是完全准确的,JavaScript引擎实际上在运行前一刻编译
它,然后立即运行编译好的代码。
# 内建类型的方法
const a = "hello world";
const b = 3.14159;
a.length; // 11
a.toUpperCase(); // "HELLO WORLD"
b.toFixed(4); // "3.1416"
a
和b
为"原生类型",却可以调用方法是怎么回事?
简而言之,是因为对象包装器的作用。当我们想把基本类型值
作为一个object
来使用时,JS自动将这个值『封箱』为它对应的对象保证器(开发者不可见)。相关的属性和方法在包装器上,因此这些原生类型就可以调用方法了。
string
->String
包装器number
->Number
包装器boolean
->Boolean
包装器
# 旧的与新的
新的JS特性,不一定能在老版本的浏览器中使用。将新特性带到老版本中,可以通过两种技术实现:『填补』和『转译』。
# 填补
填补(Polyfilling)是一个人为发明的词。指拿新特性的定义并制造一段行为等价的代码,但是这段代码可以运行在老版本的JS环境中。
例如:ES6定义了一个Number.isNaN
工具,我们通过相同的代码来实现此功能,使它能在低于ES6浏览器中使用。
// 如果当前浏览器不支持此方法,才使用填补功能
if (!Number.isNaN) {
Number.isNaN = function isNaN(x) {
return x !== x;
};
}
并不是所有的新特性都可以完全填补,一个特性的行为也不一定能够完全填补,可能会存在细微的偏差。因此在实现填补时应当非常小心,来确保尽可能严格地遵循语言规范。
或者使用更靠谱的填补,ES5-Shim (opens new window)、ES6-Shim (opens new window)等。
# 转译
功能可以填补,但是语言中新增的语法是没有任何办法填补的。在老版本的JS引擎中,使用新的语法就会因为不可识别/不合法
而抛出错误。
所以更好的选择是使用一个工具将新版本的代码转换为等价的老版本的代码。这个处理通常被称为『转译(transpiling)』,表示转换
+ 编译
。
实际上,你的源代码是使用新的语法形式编写的,但是你向浏览器部署的是转译过的旧语法形式,一般转译器会插入到构建过程中。
为什么使用新语法后,却又将它转译为老版本代码,为什么不直接编写老版本代码呢?原因如下:
新语法是为了使代码更具可读性和维护性而设计的,老版本的等价物经常会绕很多圈子,因此应该首选更新更干净的语法,不仅为自己,也为了开发团队的其他成员。
举一个转译的简单例子,ES6新增了一个『默认参数值』的特性,看起来如下:
function foo(a = 2) {
console.log(a);
}
foo(); // 2
foo(42); // 42
很简单,也很有用吧?但这种新语法在ES6之前的引擎中是不合法的。那么转译器将会对这段代码做什么才能让它在老版本环境中运行呢?
function foo() {
var a = argument[0] !== (void 0) ? arguments[0] : 2;
console.log(a);
}
对比下来,新语法更具可读性和维护性,目前比较通用的转译器Babel (opens new window)、Traceur (opens new window)。
# 非JavaScript
现在大多数JS程序都是在浏览器这样的环境运行的,你所编写的很大一部分代码,严格来说,不是直接由JavaScript控制的。
例如最常见的DOM API:
var el = document.getElementById("foo");
document
并不是JS引擎提供的,也不受JavaScript语言规范所控制。它采用了某种与普通JS对象极其相似的形式,但不是真正的对象,通常称为宿主对象
。
另外,document
的getElementById
方法看起来像一个普通的JS函数,但它只是一个微微暴露出来的接口,指向由浏览器DOM提供的内建方法。在一些(现代)浏览器中,这一层可能也是由JS实现的,但是传统的DOM及其行为是由C/C++
这样的语言实现的。