JS++ 类型系统

JS++ 类型系统

RGBtoHex:从JS++中使用JavaScript

我们将创建两个文件,一个是JavaScript文件,一个是JS++文件,来介绍它们是如何相互作用的。我们将使用经典的’rgbToHex’例子来理解Js++的类型保证。

首先,创建一个名为rgbtohex.js的文件(JavaScript),并输入以下代码。

(不要担心试图理解这个JavaScript代码。我们只是把它作为一个例子来使用)。

function rgbToHex(red, green, blue) {
    if (typeof red !==  "number")   
      throw new TypeError("Expected numeric 'red' value");
    if (typeof green !==  "number") 
      throw new TypeError("Expected numeric 'green' value");
    if (typeof blue !==  "number")  
      throw new TypeError("Expected numeric 'blue' value");

    if (red < 0   || red > 255)     
      throw new RangeError("Expected 'red' value between 0 and 255 (inclusive)");
    if (green < 0 || green > 255)   
      throw new RangeError("Expected 'green' value between 0 and 255 (inclusive)");
    if (blue < 0  || blue > 255)    
      throw new RangeError("Expected 'blue' value between 0 and 255 (inclusive)");

    var r = red.toString(16);
    var g = green.toString(16);
    var b = blue.toString(16);

    while (r.length < 2) r = "0" + r;
    while (g.length < 2) g = "0" + g;
    while (b.length < 2) b = "0" + b;

    return "#" + r + g + b;
}

将上述JavaScript代码保存为rgbtohex.js,并创建一个新的main.jspp文件。

external alert;
external rgbToHex;

alert(rgbToHex(255, 255, 255));

我们首先将’alert’声明为’外部’。alert “实际上是网页浏览器的一个原生函数(通过 “DOM API”),它允许我们显示消息框。接下来,我们将我们刚刚写的自定义JavaScript函数(’rgbToHex’)声明为’外部’。这允许我们使用JS++中的函数。

编译main.jspp。 现在创建一个index.html文件。

<!DOCTYPE html>
<head>
    <title>RGB to Hex Conversion</title>
</head>
<body>
<script src="rgbtohex.js"></script>
<script src="main.jspp.js"></script>
</body>
</html>

确保JavaScript依赖性(rgbtohex.js)的

结果是 "#ffffff",这是相当于我们在main.jspp中输入的RGB值(255,255,255)的十六进制颜色代码。

我们刚刚成功地使用了我们从JS++中编写的一个自定义JavaScript函数。

类型保证

"类型保证 "是Js++的一个独特功能,它意味着你的类型在编译时分析和运行时代码执行时保证是正确的,即使你用未类型化的JavaScript代码污染了你的Js++代码。这是你能找到的最接近于更安全的语言中的类型安全的东西,这些语言不需要处理大量的非类型化代码。

当你使用像 "外部"、"var "或 "函数 "这样的关键词时,你会分别得到作为 "外部类型 "的导入、变量和函数,并且是未定型和未检查的。换句话说,你只是在JS++里面写JavaScript。然而,当你用JS++的类型声明变量和函数时,如 "int"、"string"、"bool"、"short "或 "unsigned int",这些被称为 "内部类型",并保证是正确的。

当 "外部类型 "进入 "内部类型 "的领域时,会产生一个运行时转换。

var x = 123;
int y = x; // 'x' is converted to 'int', 'y' is therefore guaranteed to be 'int'

由于进行了转换,我们可以保证始终正确处理类型。我们将在本章和下一章继续探讨这一概念在实践中的作用,但首先,让我们看看这如何适用于我们的RGB到Hex转换器。

关键概念

在JS++中,需要理解的关键概念是JS++将数据类型分为 "内部 "类型和 "外部 "类型。内部数据类型是JS++的数据类型:int, string, bool, unsigned int, array types, dictionary, and user-defined types (我们将在第11章介绍)。外部类型是指JavaScript的数据类型。

JS++的类型系统本质上是内部数据类型、外部数据类型以及它们之间的转换。正如我们在JavaScript章节中所探讨的,类型转换实际上是在JavaScript中处理类型的最安全和最容错的方式,而JS++正是建立在这个基础上。

RGB值

RGB颜色模型定义了三个颜色值:红色、蓝色和绿色。这些颜色值是数字,必须符合0-255的范围。JS++实际上有一种数据类型正好符合数字的规范,并保证数字在0-255范围内:"字节 "数据类型。

由于类型保证,当我们在JS++中声明数据类型时,它可以改变我们程序的运行行为--与它们在C、C++、Java、C#和许多其他静态类型的编程语言中的方式一样。因此,如果我们将RGB定义为期望从0到255的字节值,那么在编译时分析和运行时应用程序执行过程中,我们保证这些值是数字的,范围在0到255之间。

例如,在编译时分析和错误检查过程中,如果你不小心试图在预期的 "字节 "中传递一个 "int",编译器会给你一个错误,因为 "int "允许0到255范围以外的数字。

同样地,在你的应用程序运行时,你声明为 "字节 "的变量保证不会是256。这意味着我们永远无法得到无效的RGB值。(对于来自C或C++背景的开发者来说,你也可以保证在运行时 "无符号int "永远不会是-1,因为无符号-和有符号-溢出会导致整数包装)。

检查无类型的JavaScript

RGB值必须在0到255的范围内。由于JavaScript缺乏数据类型,我们必须手动执行这些检查。让我们检查一下我们如何在'rgbToHex'的JavaScript函数中处理这个问题。

我们的'rgbToHex'JavaScript函数的前三行是检查数字的。

    if (typeof red !==  "number")   
       throw new TypeError("Expected numeric 'red' value");
    if (typeof green !==  "number") 
       throw new TypeError("Expected numeric 'green' value");
    if (typeof blue !==  "number")  
       throw new TypeError("Expected numeric 'blue' value");

如果我们没有提供数字作为参数,我们会用'throw'语句产生一个运行时错误。

接下来的三行检查这些数字是否符合0到255的范围。

    if (red < 0   || red > 255)     
      throw new RangeError("Expected 'red' value between 0 and 255 (inclusive)");
    if (green < 0 || green > 255)   
      throw new RangeError("Expected 'green' value between 0 and 255 (inclusive)");
    if (blue < 0  || blue > 255)    
      throw new RangeError("Expected 'blue' value between 0 and 255 (inclusive)");

再一次,如果数字不在范围内,我们使用'throw'语句抛出一个运行时错误。

然而,为什么要在应用程序执行期间解决运行时错误呢?在JS++中,这些错误可以在不运行程序的情况下进行检查--在编译时。

更安全地使用JavaScript

你可以在JS++中比在JavaScript本身中更安全地使用JavaScript。

让我们从修改main.jspp代码开始,明确地使用 "字节 "数据类型。

external alert;
external rgbToHex;

byte red = 255;
byte green = 255;
byte blue = 255;
alert(rgbToHex(red, green, blue));

编译main.jspp并打开index.html。结果应该是完全一样的:将弹出一个包含 "#ffffff "的信息框。然而,这次有一个区别:你保证只能发送从0到255的整数值。

还记得JavaScript函数中的那些检查,以确保我们收到可接受的RGB输入值吗?如果提供了不正确的值,脚本将通过抛出异常来停止应用程序的执行。所有这些潜在的错误都已经通过使用JS++和声明类型被抹去了。尽管我们没有修改任何原始的JavaScript代码,这些错误还是被抹去了。运行时错误仍然存在于JavaScript代码中,但它们永远不会执行,因为我们提供的输入值总是正确的。

试着把 "红"、"绿 "或 "蓝 "变量中的一个改为0到255范围以外的数字。

external alert;
external rgbToHex;

byte red = -1;
byte green = 255;
byte blue = 255;
alert(rgbToHex(red, green, blue));

现在试着编译修改后的main.jspp文件。你会得到一个错误。

[  ERROR  ] JSPPE5013: Computed value `-1' is out of range for type `byte' at line 4 char 11 at main.jspp

我们可以在此基础上用JS++而不是JavaScript重写整个'rgbToHex'函数,以提高类型安全,同时消除所有的运行时检查和错误。

将rgbToHex移到Js++中

首先,让我们停止导入JavaScript的'rgbToHex'函数,删除导入它的'外部'语句。我们的main.jspp文件现在应该只是像这样。

external alert;

alert(rgbToHex(255, 255, 255));

不要试图现在就编译。一旦我们移动了所有的JavaScript代码,我们就可以编译了。

rgbToHex "的JavaScript函数依赖于 "TypeError"(JavaScript原生)和 "RangeError"(也是JavaScript原生)。让我们导入这些。

external alert;
external TypeError, RangeError;

alert(rgbToHex(255, 255, 255));

最后,我们只需将rgbToHex.js(JavaScript文件)的代码复制并粘贴到main.jspp(Js++文件)中。

external alert;
external TypeError, RangeError;

function rgbToHex(red, green, blue) {
    if (typeof red !==  "number")   
        throw new TypeError("Expected numeric 'red' value");
    if (typeof green !==  "number") 
        throw new TypeError("Expected numeric 'green' value");
    if (typeof blue !==  "number")  
        throw new TypeError("Expected numeric 'blue' value");

    if (red < 0   || red > 255)     
        throw new RangeError("Expected 'red' value between 0 and 255 (inclusive)");
    if (green < 0 || green > 255)   
        throw new RangeError("Expected 'green' value between 0 and 255 (inclusive)");
    if (blue < 0  || blue > 255)    
        throw new RangeError("Expected 'blue' value between 0 and 255 (inclusive)");

    var r = red.toString(16);
    var g = green.toString(16);
    var b = blue.toString(16);

    while (r.length < 2) r = "0" + r;
    while (g.length < 2) g = "0" + g;
    while (b.length < 2) b = "0" + b;

    return "#" + r + g + b;
}

alert(rgbToHex(255, 255, 255));

现在我们可以编译代码了。它应该编译成功。在你的网络浏览器中打开index.html,你应该注意到结果仍然是一个显示 "#ffffff "的信息框。

我们刚刚把一大块JavaScript代码直接复制到Js++中,就成功了。这是因为Js++是JavaScript的超集;换句话说,它只是具有更多功能的JavaScript。

然而,我们仍然需要将我们复制和粘贴的这些 "无类型 "的JavaScript代码转换成Js++,以利用Js++的类型保证。否则,我们就只是在Js++里写普通的JavaScript。

将 "未类型 "的JavaScript转换为 "类型 "的Js++

我们将开始把我们的JavaScript 'rgbToHex' 函数转换为Js++,删除所有的运行时检查和错误。你将不再需要它们了。

你的main.jspp文件现在应该看起来像这样。

external alert;

function rgbToHex(red, green, blue) {
    var r = red.toString(16);
    var g = green.toString(16);
    var b = blue.toString(16);

    while (r.length < 2) r = "0" + r;
    while (g.length < 2) g = "0" + g;
    while (b.length < 2) b = "0" + b;

    return "#" + r + g + b;
}

alert(rgbToHex(255, 255, 255));

通过删除运行时检查和错误,我们实际上可以提高潜在的性能。一个显而易见的现象是,总体上要处理的指令较少。然而,通过删除所有的if语句,我们在硬件(CPU)层面改善了指令级的并行性,减少了分支错误预测的机会。(由于浏览器脚本中所有额外的抽象层,这些优化可能适用,也可能不适用。现在,只要知道我们减少了操作的总体数量就足够了)。

由于我们删除了运行时的检查和错误,我们的代码现在已经完全没有检查了试着用一个无效的值如256调用'rgbToHex'。你会发现它是允许的。让我们来解决这个问题。

在每个参数前面添加'byte'来限制它们的数据类型。

external alert;

function rgbToHex(byte red, byte green, byte blue) {
    var r = red.toString(16);
    var g = green.toString(16);
    var b = blue.toString(16);

    while (r.length < 2) r = "0" + r;
    while (g.length < 2) g = "0" + g;
    while (b.length < 2) b = "0" + b;

    return "#" + r + g + b;
}

alert(rgbToHex(255, 255, 255));

编译main.jspp并打开index.html。像往常一样,你会看到一个显示 "#ffffff "的信息框。

现在再试着用一个无效的值如-1或256调用'rgbToHex'函数。你将不再能够编译代码,因为这些错误将在编译时被检测出来。

添加更多的类型

我们的'rgbToHex'函数仍然用关键字'function'来声明。这是JavaScript声明函数的方式,而且它仍然使我们的函数不安全。

在JS++中,最好的做法是尽可能地声明数据类型。由于我们的'rgbToHex'函数总是返回一个字符串值,我们应该将返回类型限制为'字符串'。

external alert;

string rgbToHex(byte red, byte green, byte blue) {
    var r = red.toString(16);
    var g = green.toString(16);
    var b = blue.toString(16);

    while (r.length < 2) r = "0" + r;
    while (g.length < 2) g = "0" + g;
    while (b.length < 2) b = "0" + b;

    return "#" + r + g + b;
}

alert(rgbToHex(255, 255, 255));

现在,函数内部的所有'返回'语句必须返回一个评估为字符串数据的表达式。

最后但同样重要的是,看看这些语句。

var r = red.toString(16);
var g = green.toString(16);
var b = blue.toString(16);

什么方法被调用了?

请记住,在JavaScript中,很多类型的编程都是基于直觉的。(Js++类型系统建立在这种直觉之上,所以用Js++编程对JavaScript开发者来说应该是一种自然的进步。)在上面的每一条语句中,"toString(16) "都被调用。换句话说,我们知道我们应该期待字符串数据。那么让我们把我们的'var'改为'string'吧。

external alert;

string rgbToHex(byte red, byte green, byte blue) {
    string r = red.toString(16);
    string g = green.toString(16);
    string b = blue.toString(16);

    while (r.length < 2) r = "0" + r;
    while (g.length < 2) g = "0" + g;
    while (b.length < 2) b = "0" + b;

    return "#" + r + g + b;
}

alert(rgbToHex(255, 255, 255));

编译main.jspp。 删除rgbtohex.js的JavaScript文件。编辑index.html,完全删除对JavaScript rgbtohex.js文件的引用。你的index.html代码现在应该看起来像这样。

<!DOCTYPE html>
<head>
    <title>RGB to Hex Conversion</title>
</head>
<body>
<script src="main.jspp.js"></script>
</body>
</html>

在你的网络浏览器中打开index.html。你应该看到一个带有 "#ffffff "的信息框。祝贺你!你刚刚把一个JavaScript函数改成了JS2。你刚刚把一个JavaScript函数改成了Js++。

其他考虑因素

正如我们在前一章所学到的关于JavaScript的知识,我们凭直觉对数据类型有一种预期的感觉。

然而,并不是所有的JavaScript值都有一个兼容的Js++类型。这可以用jQuery来说明。例如,让我们重新看一下第五章中关于循环的例子。

external ;

for (int i = 0; i<2; i++) {("#content").append("i is now ", i, "; ");
}

这是一个有趣的例子,因为它涵盖了很多。首先,如果我们想打破jQuery方法的调用,使#content(一个ID为 "content "的页面元素)的选择被保存到一个变量中,会怎么样?这里没有一个兼容的JS++类型,所以我们可以直接使用'var'。

external ;

for (int i = 0; i<2; i++) {
    var content =("#content");
    content.append("i is now ", i, "; ");
}

从理论上讲,最好是没有不安全的代码。然而,在实践中,为了与JavaScript兼容,"外部"、"var "和 "函数 "是必要的。换句话说,外部类型是需要的。理想情况下,我们永远不会在 "完美的JS++代码 "中使用外部类型,但现实世界的编程几乎没有理想。

精明的观察者可能还注意到一个相反方向的转换:从内部到外部。我们将'for'循环的计数器变量'i'声明为'int',一个内部的JS++类型。然而,当我们把它作为一个参数传递给jQuery时,它被转换为 "外部"。

content.append("i is now ", i, "; ");

默认情况下,"原始类型"(如'int'、'string'、'bool'、'unsigned int'等)有隐含的默认转换,定义为从'外部'转换。这是因为JavaScript对于内部的Js++原始类型有自然兼容的数据类型。因此,转换也是自然的。有了用户定义的类型和用户定义的转换,情况会变得更加复杂,但我们会在后面的章节中介绍这些。

理解前向保证和后向保证之间的区别很重要。在前向保证中,我们不担心过去,而专注于未来。例如,考虑为浏览器的消息框函数('alert')做一个简单的封装。

external alert;

void messageBox(string message) {
    alert(message);
}

在上面的代码中,如果你用不安全的代码调用'messageBox',这并不重要,因为由于类型保证,'message'变量在'messageBox'函数中的所有逻辑都被保证为'string'。

最后说明

为什么JS++的类型保证是必要的?因为,使用不安全的JavaScript,即使是像GoDaddy这样拥有资源的大型网站,也会因为一个TypeError而导致结账失败,造成销售损失。

在上面的例子中,"继续 "按钮从来没有工作过,所以购物车的结账无法完成。

奇怪的是,结账失败来自于我们在第9章中探讨的同一个 "toLowerCase "方法。由于JS2++是唯一健全的、逐渐类型化的JavaScript编程语言,类型保证的含义不仅仅是在JavaScript中加入 "类型"。它意味着当你声明这些类型时,这些类型被保证是正确的,而且不存在可能导致运行时失败的边缘情况(不像微软和Facebook试图添加数据类型的做法)。

不要因为有人告诉你用JavaScript写代码,而成为破坏关键代码的人。你需要的不仅仅是类型和类型检查,你需要 类型保证。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程

JavaScript 教程