准备补充些JavaScript知识,所以先从Qt角度简单梳理一下。Qt对JavaScript支持,从Qt3时代就开始了,但由于各种原因,一直没有好好了解过:
- QSA:Qt Script for Application。2003~2008,后被QtScript取代。
- QtScript:2007(Qt4.3) ~ 2020 (Qt6.0),后被QJSEngine取代。(QtScript有两个版本,Qt经典版本和Qt4.6起基于JavaScriptCore的版本)
- QJSEngine:2012(Qt5.0)~至今。(QJSEngine 也有两个版本,基于V8的版本和Qt5.2起Qt自己的V4版本)
QtWebEngine和早期QtWebkit中都包含javascript引擎,不在本文范围之内。
背景
Qt下javascript的故事,感觉和Qt下opengl的故事有一拼:变来变去,受外界影响很大。
Qt下引擎
早期Qt引入javascript是为了给QWidget引入脚本功能,后来改进引擎是为了支持QML。
QSA
QSA(Qt Script for Application),实现上,它是ECMAScript 3.0和4.0草案的复合。
- 2003年7月发布
- 2008年结束支持(被Qt4.3引入的功能更强的QtScript模块取代)。
QtScript
分两个版本(阶段):
- QtScript (Classic):Qt4.3 ~ Qt4.5。这个模块的问题是,没有JIT功能,速度慢,不支持EMCAScript新特性。Qt 4.6时,QtScript 使用JavaScriptCore进行了重写。原有的这个模块,以QtScriptClassic的名字被放入到了Qt Solutions 中。
- QtScript:Qt4.6 ~ Qt5.15。使用JavaScriptCore对QtScript进行重写的版本。但是JavaScriptCore作为Webkit的JavaScript引擎,并没有公开的API接口,不关注Webkit之外的应用场景,也不接受Qt提交的适配QtScript补丁。Qt需要维护自己的JavaScriptCore,在其内部挂钩子,但是上游变动很大,每次适配都需要大量工作,这从一开始也为废弃埋下伏笔。
时间线:
- 2007年5月,Qt4.3 引入了QtScript模块(ECMAScript 3.0标准)。
- 2009年12月,Qt4.6使用JavaScriptCore对QtScript进行了重写。(此时Qt在Nokia旗下,重写QtScript是为了推出QML/QtQuick,以更好支持Qt在塞班、meego等手机操作系统下的APP开发)。
- 2015年7月,Qt5.5发布,QtScript被标识为废弃。
- 2020年8月,Qt6.0发布,QtScript从Qt6中正式移除。
QJSEngine
分两个版本(阶段):
- QJSEngine(V8):Qt5.0~Qt5.1。基于Google V8的 QJSEngine。V8作为JavaScript引擎,速度没得说,但是和QML配合起来,数据需要来回转换,性能受影响。而且V8和JavaScriptCore有同样的问题,不Care Qt下的使用场景,不接受Qt提交的补丁,需要Qt自行维护一个V8,比较痛苦。
- QJSEngine:Qt5.2 ~ 至今。Qt自研的V4,兼容EMCAScript5.1(在Qt6.0中,至少支持到EMCAScript 7)。与V8相比,对QML支持更好,但是纯JavaScript的话,会稍差一些。
时间线:
- 2012年,Qt5.0引入基于Google V8的 QJSEngine。
- 2013年,Qt5.2引入自己的JavaScript引擎——V4VM,取代Google的V8。
QQmlEngine:QJSEngine的派生类,与QJSEngine都是在Qt5.0引入。注意,QtQuick1中引擎叫做QDeclarativeEngine。
JavaScript
对于JavaScript,网络上的资料就很多了。而且里面涉及多个公司的爱恨情仇,远比Qt的故事复杂...
JavaScript提交给ECMA指定标准,称之为ECMAScript,标准编号ECMA-262。本文中没有刻意区分 ECMAScript、ECMA-262和JavaScript。
- ECMAScript (/ˈɛkməskrɪpt/),JavaScript在标准中的名字
- ECMA-262,标准的名字
- ISO/IEC 16262:2011,对应的ISO标准名字
标准化之前
- 1995年,网景公司(NetScope)就在浏览器中嵌入Scheme还是Java,展开激烈讨论。最终布兰登·艾克(Brendan Eich)5月花了10天时间设计了一门语言。取名Mocha,9月改名LiveScript,1995年年底定名JavaScript(当时网景公司与昇阳电脑公司组成的开发联盟为了让这门语言搭上Java这个编程语言“热词”,将其临时改名为JavaScript,日后这成为大众对这门语言有诸多误解的原因之一)。
- 1996年8月,微软发布的 IE3.0 包含了JScript。JScript系通过对网景的解释器进行逆向而创建。(浏览器大战)
- 1996年11月,网景正式向ECMA(欧洲计算机制造商协会)提交语言标准。ECMA以JavaScript语言为基础制定了ECMAScript标准规范——ECMA-262。
注:1996年,微软在IE浏览器中也搞了自己VBScript脚本,但在IE寿终正寝之前,基本就已经没人在web中使用了,毕竟大家都不喜欢打开网页看到一句“请在IE浏览器下使用”。VBScript作为Windows系统的脚本语言,也已经于2023年10月10日进入微软的弃用名单。
标准化 1.0~3.0
- 1997年6月,ECMAScript 1.0
- 1998年6月,ECMAScript 2.0
- 1999年12月,ECMAScript 3.0
ECMAScript 3.0获取巨大成功,得到广泛支持。
标准化 4.0 3.1 5.x(ISO/IEC)
ECMAScript 4.0 并不存在,ECMAScript 3.1 也不存在,因为
- 2000年,ECMAScript 4.0开始酝酿
- 2007年10月, ECMAScript 4.0草案发布,各方对此有严重分歧
- 2008年7月,终止ECMAScript4.0开发
ECMA会议决定:将ECMAScript4.0草案中涉及现有功能改善的部分,发布为ECMAScript3.1,但会后不久又将其改为ECMAScript5.0;将草案中改动大的部分,放入JavaScript.next(即后面的ES6)或JavaScript.next.next。
- 2009年12月,ECMAScript5.0发布
- 2011年6月,ECMAScript5.1发布(并成为ISO国际标准 ISO/IEC 16262:2011)
标准化 6~ 至今(成熟阶段?)
- 2015年6月,ECMA262的第6版发布。如果从2000年4.0酝酿开始算,这一版本用时15年(比从C++98和难产的C++0x即后来的C++11跨度都大)。从这一版本开始,官方开始用年份命名:ECMAScript2015,简称ES2015(尽管很多人都在用ES6)。
从ES2015开始,ECMA-262开挂了,每年6月份一个版本:
- 2016年6月,ECMA262 第7版,ECMAScript 2016
- 2017年6月,ECMA262 第8版,ECMAScript 2017
- 2018年6月,ECMA262 第9版,ECMAScript 2018
- 2019年6月,ECMA262 第10版,ECMAScript 2019
- 2020年6月,ECMA262 第11版,ECMAScript 2020
- 2021年6月,ECMA262 第12版,ECMAScript 2021
- 2022年6月,ECMA262 第13版,ECMAScript 2022
- 2023年6月,ECMA262 第14版,ECMAScript 2023
- ...
Babel
- https://babeljs.io
Babel 是一个广泛使用的 JavaScript 编译器,它可以将最新的 ECMAScript 语言标准(如 ES2015+)编译为向后兼容的 JavaScript 版本,以便在旧版本的浏览器或环境中运行。
Babel 默认只转换新的 JavaScript 句法(syntax),而不转换新的 API,比如 Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise 等全局对象,以及一些定义在全局对象上的方法。要使用新的API,需要使用某种Polyfill才行。
Babel的插件babel-polyfill
包含 core-js
。
引擎环境
ECMAScript规范的语言本身,而其执行需要环境:
- 浏览器下:ECMAScript + WebAPIs(BOM + DOM)
- Node.js:ECMAScript + Node APIs(fs + net + ...)
同样:
- Qt下:ECMAScript + Qt APIs
QJSEngine
- https://doc.qt.io/qt-6/qtjavascript.html
- https://doc.qt.io/qt-6/qtqml-javascript-functionlist.html
- https://doc.qt.io/qt-6/qtqml-javascript-hostenvironment.html
- https://wiki.qt.io/V4
扯了这么多,终于可以回到正题了。
从QTBUG-47735在Qt5.12修复来看,QJSEngine 应该支持 ECMAScript2016 (ES7)。EMCA各个版本的特性可以如下地址查看:
- https://www.w3schools.com/js/js_versions.asp
ECMAScript2017(ES8)还不支持,见QTBUG-58620,Add async/await support to V4。可是,六七年过去了,没都有动静。这也使得我们一直不能用 async/await。
简单测试
使用ES2016中引入指数操作符(**
)试一试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
输出结果:
1 2 3 4 5 |
|
使用ES2017添加的Object.entries()
试试看:
1 2 3 |
|
另外,在 qtdeclarative/tests/auto/qml/ecmascripttests下,有ecma262的测试用例,以及Qt用于跑这些数据的 qjstest 程序。
小例子
点击按钮,调用javascript改变其属性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
使用module,实现上述功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
参考
- https://www.qt.io/blog/2013/04/15/evolution-of-the-qml-engine-part-1
- https://bugreports.qt.io/browse/QTBUG-47735
- https://bugreports.qt.io/browse/QTBUG-71329
- https://bugreports.qt.io/browse/QTBUG-58620
- https://doc.qt.io/qt-6/qtqml-javascript-hostenvironment.html
- https://doc.qt.io/qt-5/qtscript-index.html
- https://doc.qt.io/qt-5/qtqml-javascript-hostenvironment.html
- https://doc.qt.io/archives/4.3/qtscript.html#language-overview
- https://doc.qt.io/archives/qt-4.8/porting-qsa.html
- https://www.qt.io/blog/2013/09/30/qt-5-2-alpha-available
- https://wiki.qt.io/New_Features_in_Qt_5.5
- https://development.qt-project.narkive.com/hzVG9b6f/qtscript-vs-qml-qjsengine
- https://en.wikipedia.org/wiki/QtScript
- https://web.archive.org/web/20131202231949/http://blog.qt.digia.com/blog/2007/01/05/say-hello-to-qtscript/
- https://web.archive.org/web/20080119082451/http://doc.trolltech.com/qsa-1.2.1/getting-started.html
- https://tc39.es/ecma262/
- https://developer.mozilla.org/en-US/docs/Web/JavaScript
- https://ecma-international.org/publications-and-standards/standards/ecma-262/
- https://www.w3schools.com/js/js_versions.asp
- https://en.wikipedia.org/wiki/ECMAScript
- https://en.wikipedia.org/wiki/JavaScript
- https://en.wikipedia.org/wiki/ECMAScript_version_history
- ES6 标准入门
- https://es6.ruanyifeng.com/