ECMAScript 初探 - 基础篇

坚强是说给别人听的谎言 提交于 2019-12-02 11:38:54

ECMAScript 语言的标准是由 Netscape、Sun、微软、Borland 等公司基于 JavaScript 和 JScript 锤炼、定义出来的。

ECMAScript 仅仅是一个描述,定义了脚本语言的所有属性、方法和对象。其他语言可以实现 ECMAScript 来作为功能的基础,正如 JavaScript 那样。这个实现又可以被扩展,包含特定于宿主环境的新特性,比如 QML 就引入了 Qt 对象系统中的信号与槽,还增加了动态属性绑定等非常有特色的新功能。

作为一种全新的编程语言,QML 有三个核心:

  • ECMAScript
  • Qt 对象系统
  • Qt Quick 标准库


一、语法

下面一个个来看 ECMAScript 的基础概念。


1. 区分大小写

与 C++ —样,变量、函数名、运算符以及其他一切东西都是区分大小写的,也就是说, 变量 area 和 Area 是不同的。


2. 弱类型

与 C++ 不同,ECMAScript 中的变量没有特定的类型,定义变量时只用 var 运算符,可以将它初始化为任意的值,你可以随时改变变量所存储的数据类型(实际上应当尽量避免这样做)。例如:

var i = 0;
console.log(i);
i = "hello"
console.log(i);

尽管在语法上这么做没有问题,但好的编码习惯是一个变量始终存放相同类型的值。


3. 语句后的分号可有可无

C、C++、Java 等语言都要求每条语句以分号(;)结束。ECMAScript 则允许开发者自行决定是否以分号结束一行代码。如果没有分号,ECMAScript 就把这行代码的结尾看作该语句的结束(与 Lua、Python、Visual Basic 相似)。在每条语句后使用分号是好的编码习惯,因为 QML 难免要与 C++ 代码混合使用,这样做有助于你保持一致的编码习惯,避免思维混乱。 下面两行代码的语法都是正确的:

var background = "white";
var i = 0


4. 注释

ECMAScript 借用了 C、Java 等语言的注释语法,支持两种类型的注释—单行注释和多行注释。

单行注释:
// single-line comment

多行注释:
/*
 * the root element of QML
 */


5. 代码块

代码块表示一系列应该顺序执行的语句,这些语句被封装在左花括号({)和右花括号(})之间。例如:

if(focus == true){ 
    border.width =2;
    border.color = "blue";
}


二、变量

在 ECMAScript 中使用 var 运算符声明变量,与 C++ 类似,变量名需要遵循一定的规则。


1. 变量声明

变量用 var 运算符加变量名来定义。例如:

var i = 0;

在这个例子中,声明了变量 i 并把它初始化为 0。你也可以不初始化,在用到时再初始化。

一个var语句可以定义多个变量。例如:

var i = 0 , name = "j";

这个例子定义了变量 i,初始化为数字;还定义了变量 name,初始化为字符串。你看到了,这和 C++ 或 Java 不同,一个 var 语句定义的多个变量可以有不同的类型。


2. 变量命名规则

变量命名需要遵守两条简单的规则:

  • 第一个字符必须是字母、下画线(_)或美元符号($)。
  • 余下的字符可以是下画线、美元符号或者任何字母或数字字符。

下面这些变量名都是合法的:

var test;
var objectName;
var —phone;
var $1;

为了代码的可读性,在命名变量时还应该遵循一定的命名风格。因为 Qt 是基于 C++ 的应用框架,QML 又是 Qt 框架的一部分,这里建议和 Qt C++ 代码采取同样的命名风格—驼峰命名法。

对于变量(包括函数名),以小写字母开始,单词之间采用驼峰命名法。对于类名,以大写字母开始,单词之间采用驼峰命名法。


三、原始类型

ECMAScript 有 5 种原始类型,即 Undefined、Null、Boolean、Number 和 String。每种原始类型定义了它包含的值的范围及其字面量表示形式。

ECMAScript 提供了 typeof 运算符来判断一个值的类型,如果这个值是原始类型,typeof 还会返回它具体的类型名字;而如果这个值是引用值,那么 typeof 统一返回 ”object” 作为类型名字。示例如下:

import QtQuick 2.2

Rectangle {
    Component.onCompleted:{
        var name = "Zhang San Feng";
        console.log(typeof name); // 输出:qml:string
        console.log(typeof 60); // 输出:qml:number
    }
}

变量 name 的类型是 string,字面量 60 的类型是 number。其中 “qml:” 是使用 console.log 输出信息时携带的前缀。


1. Undefined 类型

Undefined 类型只有一个值,即 undefined。当声明的变量未初始化时,该变量的默认值就是 undefined。例如:

var temp;

上面的代码声明了变量 temp 但并未显式地讲行初始化,它的值将被设置为 undefined, 这和 C++ 不同。ECMAScript 的这一特性—未初始化的变量也有固定的初始值,我们可以将一个变量和 undefined 比较来实现一些业务逻辑。比如:

var runOnce;
...
if(runOnce == undefined){
    runOnce = true;
}else{
    ...
}

当函数没有明确的返回值时,返回的值也是 undefined,如下所示:

function blankFunc(){}
console.log(blankFunc() == undefined); // 输出:true


2. Null 类型

Null 类型也只有一个值,即 null。

你可以显式地将一个变量初始化为 null,然后据此实现一些逻辑。


3. Boolean 类型

Boolean 是 ECMAScript 中最常用的类型之一,它有 true 和 false 两个值。


4. Number 类型

Number 类型是最特殊的,它既可以表示 32 位的整数,也可以表示 64 位的浮点数。你在 QML 代码中直接输入的任何数字都被看作是 Number 类型的字面量。

下面的代码声明了存放整数值的变量:

var integer = 10;

下面的代码使用十六进制的字面量来初始化存放整数值的变量:

var hexNumber = Oxlf;
var hexNumber2 = OxEA;

数字类型的最大值是 Number.MAX_VALUE,最小值是 Number.MlN_VALUE,它们定义了 Number 值的外边界,所有的 ECMAScript 数都必须在这两个值之间。

不过,由表达式计算生成的数值可以不落在这两个数之间。当计算生成的数值大于 Number.MAX_VALUE 时,它将被赋值为 Number.POSITIVEJNFINITY,即正无穷大;当生成的数值小于 Number.MIN_VALUE 时将被赋值为Number.NEGATIVEJNFINITY,即负无穷大。


5. String 类型

ECMAScript 中的 String 类型是作为原始类型存在的,它存储 Unicode 字符,对应的 Qt C++ 类型为 QString。当你混合 C++ 和 QML 编程时,所有的 QString 类型的变量都会被映射为 ECMAScript 中的 String。

字符串字面量可以用双引号(")或单引号(')来声明。而在 Qt 中,只能用双引号, 单引号表示字符。为了一致性,建议你尽可能不要使用单引号表示字符串。在 ECMAScript 中没有字符类型,这也是为什么你可以使用单引号来表示字符串的原因。下面的两行代码都是有效的:

var name = 'Lv Bu';
var name = "Guan Yu";


四、类型转换

如果一种编程语言不支持类型转换,那真是无法想象。在 ECMAScript 中,类型转换非常简单。


1. 转换成字符串

Boolean、Number、String 三种原始类型,都有 toString() 方法,可以把它们的值转换为字符串。比如下面的代码在 Qt 中可以正常运行:

var name = "Zhang San Feng"; console.log(name.toString());
Console.log(true.toString());
var visible = false;
console.log(visible.toString());
var integer = 3.14159;
console.log(integer.toString());

或有疑问:String 还有 toString() 方法,不多余吗?确实有点儿多此一举,不i寸,ECMAScript 规定所有对象都有 toString() 方法,因此这个必须有。


Number 类型的 toString() 方法还可以按基转换,比如:

var integer = 13;
console.log (integer.toString(16)); // 输出: D

如果你不指定数基,那不管原来是用什么形式声明的 Number 类型,toString() 都按十进制输出。


2. 转换成数字

parselnt() 和 parseFloat() 可以把非数字的原始值转换成数字,前者把值转换为整数,后者把值转换成浮点数。这两个方法只能用于 String 类型,如果你对其他类型调用它们, 返回值将是奇葩的 NaN。

parselnt() 和 parseFloat() 会扫描字符串,直到遇到第一个非数字字符时停止,将转换的结果返回。比如parselnt(”2014年")将会返回 2014。对于 parseFloat(),会将第一个小数点作为有效字符,而 parselnt() 则不会。

下面是一些示例:

var numl = parselnt ("2014 年"); // 输出:2014
var num2 = parselnt ("OxC"); // 输出:12
var num3 = parselnt ("3.14"); // 输出:3
var num4 = parselnt ("green"); // 输出:NaN_
var num5 = parseFloat ("3.14159"); // 输出:3.14159
var num6 = parseFloat ("1.3.3"); // 输出:1.3
var num7 = parseFloat ("Am I Float"); // 输出:NaN


parselnt() 还支持基模式,下面是一些示例:

var numl = parselnt ("AK47", 16); // 输出:10
var num2 = parselnt ("AK47", 10); // 输出:NaN
var num3 = parselnt ("010", 8); // 输出:8
var nun4 = parselnt ("010", 10); // 输出:10

需要注意的是,代表浮点数的字符串必须以十进制形式表示,比如parseFloat(“OxFE”),返回 NaN。


3. 强制类型转换

如果你是 C/C++ 程序员,对强制类型转换一定又爱又恨。ECMAScript 也支持强制类型 转换,有三种转换:

  • Boolean(value),把 value 转换成 Boolean 类型。
  • Number(value),把value转换为数字(整数或浮点数)。
  • String(value),把value转换成字符串。


(1)Boolean()

当要转换的值是一个非空字符串、非0数字或对象时,Boolean() 函数将返回 true。如果该值为空字符串、数字0、undefined 或 null,它将返回 false。一些示例:

var bl = Boolean; // 返回 false
var b2 = Boolean("Qt Quick"); // 返回 true
var b3 = Boolean(456); // 返回 true
var b4 = Boolean(0); // 返回 false
var b5 = Boolean (null); // 返回 false
var b6 = Boolean (new ArrayO); // 返回 true


(2)Number()

Number() 的强制类型转换与 parselnt() 和 parseFloat() 方法的不同之处在于:Number() 转换的是整个值!parselnt() 和 parseFloat() 只转换第一个无效字符之前的字符串。比如parseFloat ("1.3.3")返回 1.3,而 Number ("1.3.3")则返回 NaN。

对可转换为数字的字符串,Number() 会自己决定调用 parseInt() 还是 parseFloat()。比如Number ("250")返回 250, Number ("1.3")返回 1.3。


(3)String

String() 可以把任何值转换为字符串,它与调用 toString() 方法的唯一不同在于:对 null 或 undefined 值强制类型转换可以生成字符串而不引发错误:

var null2String = String(null); // "null"
var oNull = null;
var s2 = oNull.toString (); // error


五、函数

ECMAScript 中的函数,就是具名的、可重复使用的代码块。另外,ECMAScript 不支持函数重载。


1. 函数语法

函数语法如下:

function functionName(arg1, arg2, ..., argN){
    // 要执行的代码
}

function 是定义函数时必须使用的关键字。functionName可以任意取,符合变量命名规则即可。 arg1 到 argN 是函数的参数,当然也可以没有参数。花括号内是要执行的代码块。


无参函数示例:

function quitApp(){
    Qt .quit ();
}

带参函数示例:

function showError(msg){
    console.log("error - ", msg);
}

function travel(country, city){
    console.log("Welcome to ", city, " , ", country);
}

当我们使用函数参数的时候,参数就像不带 var 运算符的变量声明一样。这与 C++ 中必须给函数参数指明类型大相径庭。


2. 函数的返回值

ECMAScript 中的函数,默认都是有返回值的,即便你没有显式使用 return 语句,它也会返回 undefined。如果你想把函数运算的结果返回给调用它的地方,可以使用 return 语句。下面是个简单的示例:

function add(numberl, number2){
    var result = number1 + number2;
    console.log(number1, "+" ,number2, result);
    return result;
}

你可以这样调用 add() 函数:var ret = add(100, 34);


六、运算符

ECMAScript 的运算符和 C++、Java 等语言的差不多,具体内容不再赘述。这里只重点介绍一下关键字运算符。void、typeof、instanceof、new、delete 这些都是关键字运算符。

  • void 运算符比较特殊,它放在一个表达式前,舍弃表达式的值,返回 undefined。

  • typeof 前面讲过了,对于原始值,返回原始类型;对于引用值,返回 object。这导致你无法准确判断一个对象的引用类型,所以 ECMAScript 引入了 instanceof 运算符。

  • instanceof 用来测试一个对象的实际类型,你需要显式指定要测试的类型。例如:

    var str = new String ("hello world");
    console.log (str instanceof String); // 输出:true
  • new 运算符用来创建一个对象,前面用了很多次了,不再赘述。 delete 运算符比较特别,在 QML 中,一般它只能删除一个对象内由你定义的属性,而框架定义的那些核心属性,多数是你不能删除的。我们在 ECMAScript 中调用 delete,多数时候是解除对对象的引用,以免老有人引用某个对象而导致它逍遥法外。


七、使用 console

console 提供了输出日志信息、断言、计时器、计数器、性能分析等功能,这里只介绍前三个我们经常用到的功能。


1. 输出日志信息

console对象提供了多个打印调试信息的方法:

  • console.log();
  • console.debug();
  • console.info();console.warn();
  • console.error();


2. 断言

console.assert() 提供断言功能,它接受一个表达式,当表达式的值为 false 时会输出调试信息,打印 QML 所在行。例如:console.assert (false)

如果你传递了额外的参数给 console.assert(),它会在控制台输出这些信息。示例:

var years = 0;

for (; years < 18; years++){
    console.log("I\'m minor"); 
    continue;
    console.log ("You shoult not see me"};
}

console.assert(years < 18, years);

上面的断言语句,将会输出下列信息:

18
onCompleted (file:///F:/projects/qtquick/qmls/show_type.qml:187)

需要注意的是,在 QML 中,使用 console.assert(),断言失败,程序并不会终止运行。


3. 计时器

console 提供了计时器功能,方便我们测量某些代码的耗时情况。

console.time(tag) 启动定时器,字符串类型的 tag 是必需的。console.timeEnd(tag) 停止计时器,在控制台输出某个标签对应的耗时信息。tag 是必需的。 下面是简单的示例:

console.time("regexp");

var str = "We are dogs;\nYour dogs;\nWe want meat.\nPlease.";
var lines = str.match(/^We.*/mg);
console.log(lines.length);
console.log(lines);
console.timeEnd("regexp");

控制台输出如下:

regexp: 7ms


参考:

《Qt Quick 核心编程》第5章 ECMAScript初探


易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!