本节是第四讲的第二十五小节,上一节课为大家介绍了客户端数据存储包括Web Strorage ,IndexDB和Cache API,本节我们把前面的基础知识做个总结,并为大家介绍一些常用的开发技巧。
重新介绍 JavaScript
为什么会有这一篇“重新介绍”呢?因为 JavaScript 堪称世界上被人误解最深的编程语言。虽然常被嘲为“玩具语言”,但在它看似简洁的外衣下,还隐藏着强大的语言特性。 JavaScript 目前广泛应用于众多知名应用中,对于网页和移动开发者来说,深入理解 JavaScript 就尤为必要。
我们有必要先从这门语言的历史谈起。在1995 年 Netscape 一位名为 Brendan Eich 的工程师创造了 JavaScript,随后在 1996 年初,JavaScript 首先被应用于 Netscape 2 浏览器上。最初的 JavaScript 名为 LiveScript,但是因为一个糟糕的营销策略而被重新命名,该策略企图利用Sun Microsystem的Java语言的流行性,将它的名字从最初的 LiveScript 更改为 JavaScript——尽管两者之间并没有什么共同点。这便是之后混淆产生的根源。
几个月后,Microsoft 随 IE 3 发布推出了一个与之基本兼容的语言 JScript。又过了几个月,Netscape 将 JavaScript 提交至 Ecma International(一个欧洲标准化组织), ECMAScript 标准第一版便在 1997 年诞生了,随后在 1999 年以 ECMAScript 第三版的形式进行了更新,从那之后这个标准没有发生过大的改动。由于委员会在语言特性的讨论上发生分歧,ECMAScript 第四版尚未推出便被废除,但随后于 2009 年 12 月发布的 ECMAScript 第五版引入了第四版草案加入的许多特性。第六版标准已经于 2015 年 6 月发布。
与大多数编程语言不同,JavaScript 没有输入或输出的概念。它是一个在宿主环境(host environment)下运行的脚本语言,任何与外界沟通的机制都是由宿主环境提供的。浏览器是最常见的宿主环境,但在非常多的其他程序中也包含 JavaScript 解释器,如 Adobe Acrobat、Adobe Photoshop、SVG 图像、Yahoo! 的 Widget 引擎,Node.js 之类的服务器端环境,NoSQL 数据库(如开源的 Apache CouchDB)、嵌入式计算机,以及包括 GNOME (注:GNU/Linux 上最流行的 GUI 之一)在内的桌面环境等等。
概览
JavaScript 是一种多范式的动态语言,它包含类型、运算符、标准内置( built-in)对象和方法。它的语法来源于 Java 和 C,所以这两种语言的许多语法特性同样适用于 JavaScript。JavaScript 通过原型链而不是类来支持面向对象编程(有关 ES6 类的内容参考这里Classes,有关对象原型参考见此继承与原型链)。JavaScript同样支持函数式编程——因为它们也是对象,函数也可以被保存在变量中,并且像其他对象一样被传递。
先从任何编程语言都不可缺少的组成部分——“类型”开始。JavaScript 程序可以修改值(value),这些值都有各自的类型。JavaScript 中的类型包括:
Number(数字)String(字符串)Boolean(布尔)Function(函数)Object(对象)Symbol(ES2015 新增)
…哦,还有看上去有些…奇怪的 undefined(未定义)类型和 null(空)类型。此外还有Array(数组)类型,以及分别用于表示日期和正则表达式的 Date(日期)和 RegExp(正则表达式),这三种类型都是特殊的对象。严格意义上说,Function(函数)也是一种特殊的对象。所以准确来说,JavaScript 中的类型应该包括这些:
Number(数字)String(字符串)Boolean(布尔)Symbol(符号)(ES2015 新增)Object(对象)Function(函数)Array(数组)Date(日期)RegExp(正则表达式)null(空)undefined(未定义)
JavaScript 还有一种内置的 Error(错误)类型。但是,如果我们继续使用上面的分类,事情便容易得多;所以,现在,我们先讨论上面这些类型。
数字
根据语言规范,JavaScript 采用“遵循 IEEE 754 标准的双精度 64 位格式”("double-precision 64-bit format IEEE 754 values")表示数字。——在JavaScript(除了BigInt)当中,并不存在整数/整型(Integer)。因此在处理如下的场景时候,您一定要小心:
console.log(3 / 2); // 1.5,not 1
console.log(Math.floor(3 / 2)); // 1
一个看上去是整数的东西,其实都是浮点数。当然,您也需要小心这种情况:
0.1 + 0.2 = 0.30000000000000004
在具体实现时,整数值通常被视为32位整型变量,在个别实现(如某些浏览器)中也以32位整型变量的形式进行存储,直到它被用于执行某些32位整型不支持的操作,这是为了便于进行位操作。
JavaScript 支持标准的算术运算符,包括加法、减法、取模(或取余)等等。还有一个之前没有提及的内置对象 Math(数学对象),用以处理更多的高级数学函数和常数:
Math.sin(3.5);
var circumference = 2 * Math.PI * r;
你可以使用内置函数 parseInt() 将字符串转换为整型。该函数的第二个可选参数表示字符串所表示数字的基(进制)
parseInt("123", 10); // 123
parseInt("010", 10); // 10
一些老版本的浏览器会将首字符为“0”的字符串当做八进制数字,2013 年以前的 JavaScript 实现会返回一个意外的结果:
parseInt("010"); // 8 parseInt("0x10"); // 16
这是因为字符串以数字 0 开头,parseInt()函数会把这样的字符串视作八进制数字;同理,0x开头的字符串则视为十六进制数字。如果想把一个二进制数字字符串转换成整数值,只要把第二个参数设置为 2 就可以了:
parseInt("11", 2); // 3
JavaScript 还有一个类似的内置函数 parseFloat(),用以解析浮点数字符串,与parseInt()不同的地方是,parseFloat() 只应用于解析十进制数字。一元运算符 + 也可以把数字字符串转换成数值:
+ "42"; // 42 + "010"; // 10 + "0x10"; // 16
如果给定的字符串不存在数值形式,函数会返回一个特殊的值 NaN(Not a Number 的缩写):
parseInt("hello", 10); // NaN
要小心NaN:如果把 NaN 作为参数进行任何数*运学**算,结果也会是 NaN:
NaN + 5; //NaN
可以使用内置函数 isNaN() 来判断一个变量是否为 NaN:isNaN(NaN); // true
JavaScript 还有两个特殊值:Infinity(正无穷)和 -Infinity(负无穷):
1 / 0; // Infinity -1 / 0; // -Infinity
可以使用内置函数 isFinite() 来判断一个变量是否是一个有穷数, 如果类型为Infinity, -Infinity 或 NaN则返回false:
isFinite(1/0); // false
isFinite(Infinity); // false
isFinite(-Infinity); // false
isFinite(NaN); // false
isFinite(0); // true
isFinite(2e64); // true
isFinite("0"); // true
// 如果是纯数值类型的检测,则返回 false:
Number.isFinite("0"); // false
备注: parseInt() 和 parseFloat() 函数会尝试逐个解析字符串中的字符,直到遇上一个无法被解析成数字的字符,然后返回该字符前所有数字字符组成的数字。但是运算符 "+"对字符串的转换方式与之不同, 只要字符串含有无法被解析成数字的字符,该字符串就将被转换成 NaN。可分别使用这两种方法解析“10.2abc”这一字符串,并比较得到的结果,来理解这两种方法的区别。
字符串
JavaScript 中的字符串是一串Unicode 字符序列。这对于那些需要和多语种网页打交道的开发者来说是个好消息。更准确地说,它们是一串UTF-16编码单元的序列,每一个编码单元由一个 16 位二进制数表示。每一个Unicode字符由一个或两个编码单元来表示。
如果想表示一个单独的字符,只需使用长度为 1 的字符串。
通过访问字符串的 length(编码单元的个数)属性,可以得到它的长度。
"hello".length; // 5
这是我们第一次碰到 JavaScript 对象。我们有没有提过你可以像 object 一样使用字符串?是的,字符串也有 methods(方法)能让你操作字符串和获取字符串的信息。
"hello".charAt(0); // "h"
"hello, world".replace("world", "mars"); // "hello, mars"
"hello".toUpperCase(); // "HELLO"
“Hello”.toLowerCase();//”hello”
“hello”.indexOf(“e”);//1
“hello”+”,”+”world”//hello,world
“hello”.slice(1,3);//el
“hello”.substr(1,3);//ell
“hello”.substring(1,3);//el
let arr=”hello,world”.split(“,”);//arr[0]=”hello” ; arr[1]=”world”;
其他类型
与其他类型不同,JavaScript 中的 null 表示一个空值(non-value),必须使用 null 关键字才能访问,undefined 是一个“undefined(未定义)”类型的对象,表示一个未初始化的值,也就是还没有被分配的值。我们之后再具体讨论变量,但有一点可以先简单说明一下,JavaScript 允许声明变量但不对其赋值,一个未被赋值的变量就是 undefined 类型。还有一点需要说明的是,undefined 实际上是一个不允许修改的常量。
JavaScript 包含布尔类型,这个类型的变量有两个可能的值,分别是 true 和 false(两者都是关键字)。根据具体需要,JavaScript 按照如下规则将变量转换成布尔类型:
false、0、空字符串("")、NaN、null 和 undefined 被转换为 false
所有其他值被转换为 true
也可以使用 Boolean() 函数进行显式转换:
Boolean(''); // false
Boolean(234); // true
不过一般没必要这么做,因为 JavaScript 会在需要一个布尔变量时隐式完成这个转换操作(比如在 if 条件语句中)。所以,有时我们可以把转换成布尔值后的变量分别称为 真值(true values)——即值为 true 和 假值(false values)——即值为 false;也可以分别称为“真的”(truthy)和“假的”(falsy)。
JavaScript 支持包括 &&(逻辑与)、|| (逻辑或)和!(逻辑非)在内的一些逻辑运算符。
变量
在 JavaScript 中声明一个新变量的方法是使用关键字 let 、const 和 var:
let 语句声明一个块级作用域的本地变量,并且可选的将其初始化为一个值。
let a;
let name = 'Simon';
const 允许声明一个不可变的常量。这个常量在定义域内总是可见的。
const Pi = 3.14; // 设置 Pi 的值
Pi = 1; // 将会抛出一个错误因为你改变了一个常量的值。
var 是最常见的声明变量的关键字。它没有其他两个关键字的种种限制。这是因为它是传统上在 JavaScript 声明变量的唯一方法。使用 var 声明的变量在它所声明的整个函数都是可见的。
var a;
var name = "simon";
如果声明了一个变量却没有对其赋值,那么这个变量的类型就是 undefined。
JavaScript 与其他语言的(如 Java)的重要区别是在 JavaScript 中语句块(blocks)是没有作用域的,只有函数有作用域。因此如果在一个复合语句中(如 if 控制结构中)使用 var 声明一个变量,那么它的作用域是整个函数(复合语句在函数中)。 但是从 ECMAScript Edition 6 开始将有所不同的, let 和 const 关键字允许你创建块作用域的变量。
运算符
JavaScript的算术操作符包括 +、-、*、/ 和 % ——求余(与模运算相同)。赋值使用 = 运算符,此外还有一些复合运算符,如 += 和 -=,它们等价于 x = x operator y。
x += 5; // 等价于 x = x + 5;
可以使用 ++ 和 -- 分别实现变量的自增和自减。两者都可以作为前缀或后缀操作符使用。
+ 操作符还可以用来连接字符串:
"hello" + " world"; // hello world
如果你用一个字符串加上一个数字(或其他值),那么操作数都会被首先转换为字符串。如下所示:
"3" + 4 + 5; // 345
3 + 4 + "5"; // 75
这里不难看出一个实用的技巧——通过与空字符串相加,可以将某个变量快速转换成字符串类型。
JavaScript 中的比较操作使用 <、>、<= 和 >=,这些运算符对于数字和字符串都通用。相等的比较稍微复杂一些。由两个“=(等号)”组成的相等运算符有类型自适应的功能,具体例子如下:
123 == "123" // true
1 == true; // true
如果在比较前不需要自动类型转换,应该使用由三个“=(等号)”组成的相等运算符:
1 === true; //false
123 === "123"; // false
JavaScript 还支持 != 和 !== 两种不等运算符,具体区别与两种相等运算符的区别类似。
控制结构
if (name == "puppies") {
} else if (name == "kittens") {
} else {
}
//三元运算符
condition==true?true:false;
name == "tom"?”he's tom”:”he isn't tom”
while (true) {
// 一个无限循环!
}
do {
//至少执行一次循环!
} while (false)
for (let i = 0; i < 5; i++) {
// 将会执行五次
}
for (let property in object) {
// for...in语句以任意顺序遍历一个对象
}
for (let value of array) {
//for...of语句在可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环
}
&& 和 || 运算符使用短路逻辑(short-circuit logic),是否会执行第二个语句(操作数)取决于第一个操作数的结果。在需要访问某个对象的属性时,使用这个特性可以事先检测该对象是否为空:
var name = o && o.getName();
或用于缓存值(当错误值无效时):
var name = cachedName || (cachedName = getName());
类似地,JavaScript 也有一个用于条件表达式的三元操作符:
var allowed = (age > 18) ? "yes" : "no";
在需要多重分支时可以使用 基于一个数字或字符串的switch 语句:
switch(action) {
case 'draw':
break;
case 'eat':
break;
default:
}
switch(a) {
case 1: // 继续向下
case 2:
break;
default:
}
以上内容部分摘自视频课程04网页游戏编程JavaScript-25重新介绍,更多示例请参见网站示例。跟着张员外讲编程,学习更轻松,不花钱还能学习真本领。
