1+1=10

记记笔记,放松一下...

JavaScript引擎与运行时入门小记

接前面Qt中的JavaScript引擎小记,继续梳理javascript基础知识。

比如,写了一个javascript程序,该如何执行它,需要什么东西?

1
2
3
4
5
6
7
let myadd = (a, b) => a + b;
function test() {
    let a = myadd(1, 2);
    let b = Math.sqrt(16);
    let c = a ** b;
    console.log(c);
}

本文目标:在Firefox、Node.js、Qt、Python下,分别执行上面的脚本。

注意:在这个例子中,console 并不是ECMAScript规范的一部分,而是由运行时提供的(好在各个运行时基本都提供了该功能)。

基本概念

  • JavaScript引擎(Engine):一种将 JavaScript 代码转换为可执行代码的程序组件,它负责解释和执行 JavaScript 代码。早期的引擎只是解释器,但现代的引擎都使用JIT(just-in-time compilation)来改善性能。知名的引擎有:V8、JavaScriptCore、SpiderMonkey、 ...
  • JavaScript运行时(Runtime):JavaScript的运行环境,包括JavaScript引擎、标准库、宿主环境等。不同的运行时为JavaScript执行提供不同的资源,比如网络浏览器和Node.js都是运行时。浏览器提供文档对象模型(DOM)和浏览器对象模型(BOM)等API;Node.js提供文件系统操作、网络操作等API。

在 Qt上:QJSEngine和QScriptEngine 都是 JavaScript引擎,而使用这两个类的Qt程序则是 JavaScript 运行时。不同的Qt程序为javascript提供不同的资源(暴露不同的API给javascript)。

是否可以这样类比C++:把引擎理解成不带任何库函数的编译器,把运行时理解成编译器+库函数?

引擎 与 运行时

JavaScript的主战场是浏览器,引擎基本都是浏览器厂商开发的。

浏览器

运行时 引擎 备注
Chrome V8
Firefox SpiderMonkey
Edge V8 早期(2015年之前?)曾使用自己的 Chakra
Safari /səˈfɑːri/ JSC (JavaScriptCore) 2008年Apple重写JSC后,内部代号 Nitro
Opera V8 早期(2013年Opera15之前)使用自己的 Carakan、Futhark、Linear B
IE Charkra 早期(IE9之前)使用自己的 JScript

javascript-engine-web-runtime

JavaScript 只是一个单线程的编程语言,这意味着:它只有一个调用栈, 一次只能做一件事情

服务端

额,不清楚为什么很多人叫服务端,这不就是脱离浏览器的JavaScript运行环境吗?为了和浏览器(客户端)相对应??

运行时 引擎 备注
Node.js V8
Deno V8
Bun JSC (JavaScriptCore)

Qt

在Qt中,出现过两个JavaScript引擎,共四种实现:

  • QScriptEngine: Qt4.3时是自己实现的,Qt4.6时内部切换到JavaScriptCore,Qt5.5时废弃,Qt6.0时移除。
  • QJSEngine: Qt5.0时内部使用V8引擎,Qt5.2时切换到Qt自己的引擎V4。

其他

wikipedia给出了数十种引擎。用于嵌入式的、用于java的、用于rust的 ...

如何运行javascript

回归初衷,如果要测试一个javascript,该如何运行?

浏览器

浏览器是javascript最主要的运行环境,如何用

放入 html 页面中

而后在浏览器中通过界面元素进行触发(botton点击、body加载 等):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html>
<head>
    <title>1+1=10's JavaScript Test</title>
    <script>
        let myadd = (a, b) => a + b;
        function test() {
            let a = myadd(1, 2);
            let b = Math.sqrt(16);
            let c = a ** b;
            console.log(c);
        }
    </script>
</head>
<body>
    <button onclick="test()">Click Me!</button>
</body>
</html>

注意,<script>可以放在页面不同位置上:

  • <head>中:可以确保脚本在页面加载前执行,但可能导致阻塞页面渲染。
  • <body>底部:提升加载性能
  • <head>中使用defer属性:推迟执行,直到页面加载完毕。(避免阻塞页面加载)

为了保持通用,此处刻意没有用alter()等弹窗函数(它是浏览器这种运行时特有的)。

不过,执行是执行了,可是console把内容输出到什么地方去了??

在console中直接执行

JavaScript中console的输出,需要在console中才能看到。在Windows下,快捷方式如下:

浏览器 快捷键(Windows) 备注
Chrome Ctrl+Shift+J
Firefox Ctrl+shift+K
Edge Ctrl+Shift+J

既然打开console了,其实,在这里面可以执行javascript代码的。以Firefox为例

  • 支持单行js、多行js、js文件
  • Ctrl+O 打开js文件
  • Ctrl+Enter 执行当前脚本

firefox-console

服务端

以node.js为例。将如下代码保存为 t.js

1
2
3
4
5
6
7
8
9
let myadd = (a, b) => a + b;
function test() {
    let a = myadd(1, 2);
    let b = Math.sqrt(16);
    let c = a ** b;
    console.log(c);
}

test()

执行它:

1
node t.js

Qt

编写程序,调用 QJSEngine,编译运行。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <QCoreApplication>
#include <QJSEngine>

const char *js = R"js(
    let myadd = (a, b) => a + b;
    function test() {
        let a = myadd(1, 2);
        let b = Math.sqrt(16);
        let c = a ** b;
        console.log(c);
    }
    test()
)js";

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QJSEngine engine1;
    engine1.installExtensions(QJSEngine::ConsoleExtension);
    engine1.evaluate(js);

    return 0;
}
  • 因为console并不是ECMAScript规范一部分,QJSEngine提供该功能,需要通过扩展方式开启。

Python

Python下轮子多,值得一试。

  1. Python中一个的js2py包,但是只支持ECMA5.1。所以下面的脚本不能跑通:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import js2py

js = '''
    let myadd = (a, b) => a + b;
    function test() {
        let a = myadd(1, 2);
        let b = Math.sqrt(16);
        let c = a ** b;
        console.log(c);
    }
    test()
'''

js2py.eval_js(js)
  1. 使用pythonmonkey运行,没有问题:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import pythonmonkey as pm

js = '''
    let myadd = (a, b) => a + b;
    function test() {
        let a = myadd(1, 2);
        let b = Math.sqrt(16);
        let c = a ** b;
        console.log(c);
    }
    test()
'''

pm.eval(js)

参考

  • https://en.wikipedia.org/wiki/JavaScript_engine
  • https://en.wikipedia.org/wiki/List_of_ECMAScript_engines
  • https://sayansarkar333.medium.com/advance-javascript-working-of-javascript-engine-3c36c1a46f50
  • https://dev.to/sanderdebr/a-brief-explanation-of-the-javascript-engine-and-runtime-2idg
  • https://www.codecademy.com/article/running-javascript-in-the-browser-console
  • https://firefox-source-docs.mozilla.org/devtools-user/web_console/the_command_line_interpreter/index.html
  • https://doc.qt.io/qt-6/qjsengine.html