热搜:前端 nest neovim nvim

【译】《你不知道的JS》(第一章)

lxf2023-06-19 02:23:05

节选翻译自(You-Dont-Know-JS - 2nd Edition )

很多人认为JS是很糟糕的语言,设计的不好,有严重缺陷,这是个很荒谬的说法。

和任何很棒的语言一样,JS有精妙的设计,也有一些坑。即使是JS的创造者Brendan Eich也遗憾地认为有些部分是错误的。但是他错了,那些并不是错误。正是由于JS的全部部分,才让它成了这世上最普遍,最有影响力的语言。

给前端开发者的建议: 只让代码达到想要的效果是不够的,如果能完全理解代码是怎么运行的,你就能在开发工作中更加高效。

good enough to work is not, and should not be, good enough.

你不知道的JS

问题:

  • 什么是JavaScript?
  • alert("hello")是JS 吗?
  • 编程范式有哪几种,JS属于哪一种?
  • 什么是向后/向前兼容,HTML, CSS JS 分别是哪种,如果不兼容,解决方案是什么?
  • 解释型语言和编译型语言有什么特点,JS是哪一种?JS引擎在运行代码前做了什么?
  • WebAssembly是什么(WASM)

关于JavaScript

这个语言和Java没关系,也不是用来写脚本的,为了迎合市场,吸引Java程序员才这样命名。

这个语言的官方名称是ECMAScript,由TC39委员会指定,归属于ECMA组织

ECMAScript定义了语法和行为,运行在浏览器/Node.js上的JavaScript(简称JS) 是ECMAScript规范(简称ES)的实现。

运行JS的环境有很多,除了浏览器,Node.js之外,还有机器人,电灯泡……

这么多环境里,web浏览器占着主导地位,JS在web浏览器里是如何实现的,这一点至关重要。

大多数情况下,浏览器JS引擎实现的JS 和规范里的是一致的,不过也有些差异。ECMAScript附录B中记录了只针对web浏览器的规范, 不过不建议使用,因为它们只在特定JS引擎环境下有用。开发中还是应该尽可能遵从JS规范。

alert("hello")是JS 吗?

你是怎么看这个代码的呢?JS规范中并没有定义 alert(...)函数,但是所有的web环境都有它

很多JS环境(比如浏览器JS引擎, Node.js)在全局作用域中添加了API, 为你的JS程序提供了特定环境的能力,比如alert()就是在用户浏览器弹出一个警告框

fetch(..)getCurrentLocation(..)getUserMedia(..)看上去很像JS,其实是Web API。 还有Node.js环境里, 提供了很多内置模块的API方法,比如fs.write(..)

console.log()不在JS规范里,但是他们太通用了,几乎所有JS环境都会定义他们。

所以alert(..) 和 console.log(..) 并不是JS,只是像JS。他们遵从JS语法。他们的行为由正在运行的JS引擎控制。

人们总是抱怨JS不一致,其实大多这些跨浏览器的差异并不是JS导致的,而是环境自己的行为差异。

 alert(..) 这个调用算是JS, 但是 alert 函数本身并不是JS规范。

编程范式

“范式”术语指的是组织代码的思路和方法,范式里有很多风格,包含各种库和框架。

典型的范式分类包括: 过程式面向对象函数式

  • 过程式风格在组织代码时,自上而下,线性处理,一系列操作结合成过程
  • 面向对象风格在组织代码时,把逻辑和数据放到 类 里
  • 函数式风格把代码组织成函数,(和过程不一样,纯计算), 并把这些函数映射出值

范式没有对错之分,它们指导程序员如何组织代码,维护代码,解析问题。

有些语言是偏向单一范式的,比如C是过程式,Java/C++是面向对象, Haskell是函数式。

多范式语言更加灵活,一个程序甚至也能有2个以上的范式

JS是多范式语言

向后兼容 向前兼容

JS 向后兼容,不向前兼容(不支持在旧的JS引擎上使用新的特性)

HTML和CSS相反, 是向前兼容(在2010的浏览器上,使用2019年的特性),但不是向后兼容 (以前写的html和css可能不可用了)

编程语言向前兼容是不实际的, HTML 和CSS是声明式的,很容易跳过不认识的声明,对已认识的声明影响较小。

如果特性是新的语法,程序可能会无法编译运行,抛出语法错误; 如果特性是一个新的API,比如ES6的Object.is(),程序会运行到一个地方,然后当它遇到这个未知API的引用时,抛出运行时异常

对于新的不兼容的语法,解决方案就是转译transpile, (使用工具,把程序的源码从一种形式转换成另一种形式,仍然是文本源码的形式)。通常,可以用转译器来解决由语法导致的前向兼容问题。最常见的就是使用Babel将新的JS语法转换到相对旧的语法

为什么要使用工具把新的语法版本转换成老的版本? 原因是,强烈推荐开发者使用最新版本的JS,这样他们的代码简洁高效。

开发者应该把精力集中在清晰的新语法格式,让工具处理,生成一个向前兼容的适用于部署运行在旧的JS引擎环境上的代码版本。

例子:let关键字是ES6的特性

if (something) {
    let x = 3;
    console.log(x);
}
else {
    let x = 4;
    console.log(x);
}


// 转译后:
var x$0, x$1;
if (something) {
    x$0 = 3;
    console.log(x$0);
}
else {
    x$1 = 4;
    console.log(x$1);
}

如果向前兼容的问题和新语法无关,而是缺少了新加的API,最常用的解决方法就是为缺失的API提供一个定义,就像是在旧环境已经定义过它。这种模式叫做polyfill 填充。

例子: finally属于ES2019规范

// getSomeRecords() returns us a promise for some data it will fetch
var pr = getSomeRecords();

// show the UI spinner while we get the data
startSpinner();

pr
.then(renderRecords)   // render if successful
.catch(showError)      // show an error if not
.finally(hideSpinner)  // always hide the spinner


// polyfill
if (!Promise.prototype.finally) {
    Promise.prototype.finally = function f(fn){
        return this.then(
            function t(v){
                return Promise.resolve( fn() )
                    .then(function t(){
                        return v;
                    });
            },
            function c(e){
                return Promise.resolve( fn() )
                    .then(function t(){
                        throw e;
                    });
            }
        );
    };
}

解释型 编译型

JS是解释型语言还是编译后的程序,这是个一直有争论的问题,大部分意见认为它是解释型。

相比于编译型语言,解释型/脚本语言通常被认为更差,原因有很多,大家觉得它们缺少性能优化,不是成熟的静态类型语言(脚本语言通常使用动态类型)。

被认为是编译型的语言,通常会生成程序的二进制格式用于之后执行。 很多人因为这个觉得JS不是编译型语言(没有二进制),实际上,最终程序的格式并不是很重要。

JS到底是解释型语言还是编译型语言,和它怎么处理错误有关系

脚本/解释型语言通常从上至下逐行执行,比如第五行有错误,直到1-4行执行完后,程序第五行的错误才会被发现。(图1)

【译】《你不知道的JS》(第一章)

与之相比,编译型语言在执行前,会先经理一个解析的步骤(图2)

【译】《你不知道的JS》(第一章)

图2: 解析 + 编译 + 执行

在执行前,一个非法的命令可以在解析的阶段被捕获。程序不会运行。对于静态错误来说,在执行代码前发现它们会更好。

所有编译型语言都经过了解析。在经典编译理论中,解析后就是代码生成,生成一个可执行的形式。

JS源代码在被执行前,会先被解析,解析后是抽象语法树AST(Abstract Syntax Tree)

所以JS是被解析的语言, 但它是不是编译的呢?

答案接近于 是

具体来说,JS引擎 “编译” 生成了二进制字节码(bytecode),然后交给“JS虚拟机”去执行。有些人说VM虚拟机在解释字节码。那样的话,java也算是解释型语言了。当然,这和传统的JAVA是编译型语言相违背

JS引擎可以对解析后的代码进行多次JIT(Just-In-Time)即时处理/优化

从源代码到JS执行的,JS引擎做了什么?

  1. 源程序经过Babel转译(transpile),webpack打包(或者其他构建工具),然后以各种不同的形式给到JS引擎
  2. JS引擎把代码解析AST抽象语法树(parse the code to an AST) (通过词法分析tokenize,语法分析)
  3. JS引擎把AST转换成字节码(a binary intermediate representation),然后由优化的JIT编译器来进一步提炼/转换
  4. 最后,JS VM虚拟机执行程序

【译】《你不知道的JS》(第一章)

JS是编译型语言

WebAssembly简介

WASM类似于汇编的格式,它跳过了解析编译阶段(通常由JS引擎完成),二进制打包的程序不需要很多处理就能被JS引擎执行。

刚开始它的目的是性能提升,此外,它还能让非JS的语言进入web平台。举个例子,它可以将Go语言的程序(支持多线程)转换为JS引擎能理解的形式,尽管JS引擎没有线程的特性。

还有个观点认为WASM正在成为一个跨平台的虚拟机,程序被编译后可以运行在不同的环境里

总结

JS的定义

  • JS是ECMAScript规范的实现 (ECMAScript规范由TC39委员会制定,归属于ECMA组织)

JS is an implementation of the ECMAScript standard , which is guided by the TC39 committee and hosted by ECMA. It runs in browsers and other JS environments such as Node.js.

  • JS是多范式语言: 开发者可以混合使用过程式,面向对象,函数式这些范式

JS is a multi-paradigm language, meaning the syntax and capabilities allow a developer to mix and match (and bend and reshape!) concepts from various major paradigms, such as procedural, object-oriented (OO/classes), and functional (FP).

  • JS是编译型语言,在执行JS前,工具(JS引擎)会先进行处理,并识别一些错误。

JS is a compiled language, meaning the tools (including the JS engine) process and verify a program (reporting any errors!) before it executes.

收获

  • 什么是JavaScript?

JS是ECMAScript规范的实现,很多环境都能运行JS,比如web浏览器。

  • alert("hello")是JS 吗?

alert, console.log(),这些只是看上去像JS,他们遵守了JS的语法,但是并不属于JS规范。由于console.log的通用性,每个环境都会定义它

  • 编程范式有哪几种,JS属于哪一种?

JS是多范式语言,可以混合使用 过程式,面向对象,函数式编程

  • 什么是向后/向前兼容,HTML, CSS JS 分别是哪种,如果不兼容,解决方案是什么?

JS向后兼容;HTML和CSS向前兼容

向前兼容的解决方案有两个,针对语法syntax,转译transpile; 针对新的API方法,填充polyfill

babel是最常见的转译器。

  • 解释型语言和编译型语言有什么特点,JS是哪一种?JS引擎在运行代码前做了什么?

(有争议)本文中,JS是更像编译型语言。

  • WebAssembly是什么(WASM)

WebAssembly 是一门类汇编语言, 为其他语言(Go, Rust, C++)提供了移植进web的道路

下一章《学习JS最好的方式是?》 未完成,持续更新中……

本网站是一个以CSS、JavaScript、Vue、HTML为核心的前端开发技术网站。我们致力于为广大前端开发者提供专业、全面、实用的前端开发知识和技术支持。 在本网站中,您可以学习到最新的前端开发技术,了解前端开发的最新趋势和最佳实践。我们提供丰富的教程和案例,让您可以快速掌握前端开发的核心技术和流程。 本网站还提供一系列实用的工具和插件,帮助您更加高效地进行前端开发工作。我们提供的工具和插件都经过精心设计和优化,可以帮助您节省时间和精力,提升开发效率。 除此之外,本网站还拥有一个活跃的社区,您可以在社区中与其他前端开发者交流技术、分享经验、解决问题。我们相信,社区的力量可以帮助您更好地成长和进步。 在本网站中,您可以找到您需要的一切前端开发资源,让您成为一名更加优秀的前端开发者。欢迎您加入我们的大家庭,一起探索前端开发的无限可能!