首页 » 前端 » JavaScript » 正文

(一)JavaScript 基础概念

1. 简述

任何语言的核心都必然描述这门语言最基本的工作原理。而描述的内容通常都要涉及这门语言的语法、操作符、数据类型、内置功能等用于构建复杂解决方法的基本概念。ECMA-262 通过 ECMAScript 为我们描述了 JavaScript 的所有这些基本概念。

2. 语法

ECMAScript 的语法大量借鉴了 C 及 其他类语言如 Java、Perl 的语法,因此熟悉这些语言的开发人员在接受 ECMAScript 更加宽松语法时,一定会有一种轻松自在的感觉。

2.1. 区分大小写

ECMAScript 中的一切(变量、函数和操作符)都区分大小写。如:变量名 test 和 Test 分别是两个不同的变量。而函数名不能为 typeof ,因为他是一个关键字,但 typeOf 则是一个有效的函数名。

2.2. 标识符

标识符就是指变量、函数、属性的名字,或者函数的参数。标识符由以下格式规则组成:

  • 第一个字符必须是(字母、下划线、美元符号)
  • 其他字符可以是(字母、下划线、美元符号、数字)
  • 标识符不能是(关键字、保留字)
  • 标识符中的字母也可以包含扩展的 ASCII 或 Unicode 字母字符,但不推荐这样做。

按照惯例:ECMAScript 标识符采用驼峰大小写格式,也就是第一个单词小写,剩下的每个单词都首字母大写。虽然没有强制要求必须采用这种格式,但为了与 ECMAScript 内置的函数和对象命名格式保持一致,可以将其当做一种最佳实践。

2.3. 注释

ECMAScript 使用 C 风格的注释,包括(单行注释、块级注释)

// 单行注释

/*
    块级注释,中间的 星号 可以省略,为了提高可读性也可加上。
*/

2.4. 严格模式

ECMAScript 5 引入严格模式(strict mode) 概念,严格模式做的事:

  • 使用一种不同的解析与执行模型
  • ECMA3 中的一些不确定的行为将得到处理
  • 对某些不安全的操作也会抛出错误

要使用严格模式,可以在顶部添加如下代码:

"use strict"

这行代码看起来就是字符串,但其实它是一个编译指示(pragma),用于告诉 JavaScrip t引擎切换严格模式。

在函数内部的上部也可包含这条编译指示,也可指定函数在严格模式下执行:

function doSomething(){
    "use strict"
    // 函数体
}

严格模式下,JavaScript 的执行结果会有很大不同,后续随时指出严格模式下的区别。

  • 严格模式下 eval 和 arguments 这两个名字,不能做标识符或属性名,会抛出错误。
  • 严格模式下 给为经声明的变量赋值会抛出 ReferenceError 错误
  • 严格模式下 八进制字面量是无效的,会导致支持该模式的 JavaScript 引擎抛出错误。

2.5. 语句

  1. ECMAScript中的语句以一个分号结尾;如省略分号,则由解析器确定语句的结尾。
var sum = a + b    // 即使没有分号也是有效的语句 -- 不推荐
var diff = a - b;  // 有效的语句 -- 推荐
  • 分号结尾不是必须,建议不要省略它,因为可以避免很多错误(例如输入不完整)。
  • 加上分号在某些情况下增进代码的性能,因为解析器不必再花费时间推测应该在哪插入分号了。
  1. 条件控制语句(如 if语句)代码块
if (test) 
    alert(test); // 有效但容易出错,不要使用

if (test) {
    alert(test); // 推荐使用
}
  • 多条语句情况下才要求使用 代码块 ,单挑语句不做要求。
  • 推荐在控制语句时使用代码块,让编码更清晰,降低修改代码时的出错几率。

3. 关键字和保留字

  1. ECMA-262 描述了一组特定用途的关键字,这些关键字可以用于表示控制语句的开始或结束,或者用于执行特定操作等。

带星号表示第 5 版新增的关键字。

break 终止 do 循环 instanceof 实例..对比 typeof 类型查询
case 实例 else 否则 new 新的 var 变量
catch 捕获 finally 最终 return 返回 void 空
continue 继续 for 循环 switch 条件 while 循环
Debugger* 调试 function 函数 this 这 with 和..在一起
default 默认 if 如果 throw 抛出
delete 删除 in 在..之内 Try 尝试
  1. ECMA-262 还描述另外一组不能用作标识符的保留字。尽管保留字在这门语言中还没有描述任何特定的用途,但他们有可能在将来被用作关键字。ECMA-262 第3版定义的全部保留字
abstract 抽象 enum 枚举 int 整数 short 短整型
boolean 布尔 export 导出 interface 接口 static 静态
byte 比特 extends 继承 long 长整型 super 父级
char 字符 final 最终 native 原生 synchronized 同步
class 类 float 单精度 package 包 throws 抛出
const 常量 goto 转到 private 私有 transient 短暂的
debugger 调试器 implements 工具 protected 保护 volatile 不稳定
double 双精度 import 导入 public 公共

第 5 版把在非严格模式下运行的保留字缩减为下列这些:

class enum extends super
const export import

在严格模式下,第 5 版还对一下保留字施加了限制:

implements package public
interfac private static
let* protected yield*

第五版对使用关键字和保留字的规则进行了少许修改,关键字和保留字仍然不能作为标识符使用,但现在可以做对象的属性名,但最好不好使用关键字和保留字作为标识符和属性名,以便与将来的ECMAScript版本兼容。

4. 变量

ECMAScript 的变量是松散类型的,所谓松散类型就是可以用来保存任何类型的数据。

// 1.未经初始化的变量会保存一个特殊值 undefined
var init;
alert(init); // undefined


// 2.在函数中使用 var 操作符定义一个变量,这个变量在函数退出后就会销毁 。
function test() {
    var message = "hi"; // 局部变量
}
test();
alert(message); // 错误


// 3.省略 var 操作符,就变成一个全局变量
function test() {
  message = "hi"; // 全局变量
}
test();
alert(message); // "hi"

// 4.可以使用一条语句定义多个变量
var message = "hi",
    found = false,
    age = 29;
  • 未经初始化的变量会保存一个特殊值 undefined
  • 在函数中使用 var 操作符定义一个变量,这个变量在函数退出后就会销毁 。
  • 省略 var 操作符,就变成一个全局变量,可在函数外部任何地方访问到,但不推荐这样做,英文局部作用域中定义全局变量很难维护。
  • 可以使用一条语句定义多个变量。

5. 数据类型

ECMA中有共有 6 中数据类型

  • 基本数据类型:Undefined、Null、Boolean、Number、String
  • 复杂数据类型:Object

ECMA不支持创建任何自定义类型的机制,而所有值最终都将是上述 6 中数据类型之一。

因为 ECMAScript 数据类型具有动态性,也没必要再定义其他数据类型的必要。

5.1. typeof操作符

鉴于 ECMAScript 是松散类型,因此需要有一种手段来检测变量的数据类型【typeof】就是负责提供这方面信息的操作符。

  • Undefined 未定义
  • boolean 布尔值
  • string 字符串
  • number 数值
  • object 对象/null
  • function 函数
// 使用 typeof 操作符的例子:
var message; 
alert(typeof message); // undefined
alert(typeof true); // boolean
alert(typeof "hi"); // string
alert(typeof 95); // number
alert(typeof null); // object
alert(typeof Function); // function

5.2. undefined类型

Undefined 类型只有一个值,就是 undefined 。使用 var 声明变量但未对其初始化,这个变量值就是 undefined

// 例如
var message;
alert(message == undefined); // true

包含 undefined 值的变量和尚未定义的变量还是不一样。

// 例如
var message; // 未初始化,默认值 undefined
// var age; 变量并没有声明

alert(message); // undefined
alert(age); // 产生错误

alert(typeof message) // undefined
alert(typeof age) // undefined 变量未声明,但也不会报错。

5.3. Null类型

  1. Null 类型只有一个值的数据类型,就是 null。从逻辑角度来看 null 值表示一个空对象指针,所以使用 typeof 检测 null 值会返回 object 的原因。
// 例如
var car = null;
alert(typeof car); // object
  1. 定义的变量将来用于保存对象,最好将该变量初始化为 null 而不是其他值
// 这样只要检查 null 值就可以知道相应的变量是否已经保存了一个对象引用。
var car = null;
if (car != null) {
  // 对 car 对象执行某些操作
}
  1. 实际上 undefined 值派生自 null 值的,因为 ECMA-262规定对它们的值相等性测试要返回 true,尽管它们有这样的关系,但它们的用途完全不同。
alert(null == undefined); // true
  1. 只要定义的变量将来用于保存对象,就应该明确让该变量保存 null 值。这样不仅体现 null 作为空对象指针的惯例,而且也有助于进一步区分 null 和 undefined

5.4. Boolean类型

Boolean 类型有两个字面值:true、false 这两个值与数字不是一回事,因此true不一定等于1,false不一定等于0.

虽然 Boolean 类型的字面值只有两个,但 ECMAScript 中所有类型的值都有与这两个 Boolean 值等价的值。

下面列出各种数据类型及其对应的转换规则:

数据类型 转为 true 值 False
Boolean true false
String 任何非空 “”(空字符串)
Number 任何非零(包括无穷大) 0和NaN
Undefined n/a undefined
Object 任何对象 null

这些规则对理解流程控制语句(如 if 语句)自动执行相应的 Boolean 转换非常重要。

// 例如:
var message = "Hellow world!";
if (message) {
    alert("Value is true")
}

运行这个实例,会弹出警告框,因为字符串 message 被自动转换成了对应的 Boolean 值(true)。由于存在这种自动执行的 Boolean 转换,因此确切地知道在流程控制中使用什么变量至关重要。错误的使用一个对象而不是 Boolean 值,就有可能彻底改变应用程序的流程。

5.5. Number类型

Number 类型使用 IEEE754 格式表示整数和浮点数值,为了支持各种数值类型,ECMA-262 定义了不同的数值字面量格式。

  1. 十进制整数 数字序列(0-9)

  2. 八进制 第一位必须为0,然后八进制数字序列(0~7)

  3. 十六进制 前两位必须为 0x,后跟任何十六进制数字字母(0~9 及 A~F) 其中字母大小写都可。

  4. 浮点数 就是该数值中必须包含一个小数点,并且小数点后面至少有一位数字,但小数点前面可以没有整数。

  5. 数值范围 ECMAScript 能够表示的最小值 Number.MIN_VALUE ,最大值 Number.MAX_VALUE,超出JavaScript数值范围的值,那么会转换成 Infinity(正无穷) 、 -Infinity(负无穷)

  • 如果某次计算返回正或负的 Infintiy 值,那么该值将无法继续参与下一次的计算。因为 Infinity 不能参与计算的数值。

  • 访问 Number.NEGATIVE_INFINITY 和 Number.POSITIVE_INFINITY 也可以得到负和正 Infinity 的值

  • 如果确定一个数是否是 有穷的,可以使用 isFinite() 函数,意思是参数位于最小值与最大值之间返回 true
  1. NaN (Not a Number)是一个特殊的数值,这个数值用于表示一个本来返回数值的操作数未返回数值的情况(这样就不会抛出错误了)

    NaN有两个特点:

  • 涉及 NaN 的操作(例如 NaN/10)都会返回NaN,这个特点在多步计算中可能导致问题。
  • NaN 与任何值都不相等,包括 NaN 本身。

    针对这两个特点,ECMAScript 定义了 isNaN() 函数。这个函数帮我们确定是否“不是数值”

    // 例如,不能被装换为数值的会导致函数返回 true
    alert(isNaN(NaN));    // true
    alert(isNaN(10));     // false (10是一个数值)
    alert(isNaN("10"));   // false (可以被转换成数值)
    alert(isNaN("blue")); // true  (不能转换成数值)
    alert(isNaN(true));   // false (可以被转换成数值 1)
    

    尽管如此,isNaN() 也适用于对象,在基于对象调用 isNaN()函数时,会首先调用对象的 valueOf() 方法,然后确定该返回值是否可以转换为数值。如果不能,则基于这个返回值再调用 toString() 方法,再测试返回值。

  1. 数值转换

    有三个函数可以把非数值转换为数值:Number()、parseInt()、parseFloat()

    Number() 可以用于任何数据类型,而另外两个函数则专门用于把字符串转换成数值。

    Number() 转换规则:

  • Boolean => true 和 false 分别为 1 和 0
  • 数值 => 传入和返回
  • Null => 0
  • undefined => NaN
  • 字符串规则
    • 只包含数字,则转换为十进制,前导零会被忽略
    • 包含浮点格式,则转换为对应的浮点数值
    • 包含十六进制格式,则将其转换为相同大小的十进制
    • 空,则转换为0
    • 包含上述之外的字符,则转换为 NaN
  • 对象 => 调用对象 valueOf() 方法,让后依照前面的规则转换返回的值。如果转换结果为 NaN ,则调用对象的 toString() 方法,然后再次依照前面的规则转换返回的字符串值。

    var num1 = Number("Hello world!"); //NaN
    var num2 = Number(""); // 0
    var num3 = Number("000011"); // 11
    var num4 = Number(true); // 1
    

    // Number 函数在转换字符串时比较复杂而且不够合理,因此处理整数更常使用 parseInt()

    parseInt() 转换规则

  • 会忽略字符前面的空格,直到找到一个非空格字符

  • 第一个字符不是数字或负号,返回 NaN。也就是 parseInt转换空返回NaN,则Number转换空返回0
  • 非数字字符 “1234blue” 会被转换为 1234,因为 “blue” 会被完全忽略。因为第一个字符是数字,就会继续解析第二个字符
  • 也能识别各种整数格式(十进制、八进制、十六进制)
  • ECMA5 已经不支持转换 八进制

    var num1 = parseInt("1234blue"); //1234
    var num2 = parseInt("");         // NaN
    var num3 = parseInt("0xA");      // 10 (十六进制)
    var num4 = parseInt("22.5");     // 22 
    var num5 = parseInt("070");      // 56 (八进制) ECMA5 不支持
    var num6 = parseInt("70");       // 70 (十进制)
    var num7 = parseInt("0xf");      // 15 (十六进制)
    

    在ECMA3 支持 070 八进制 **转换为 **十进制 56,而ECMA5 070 八进制 **转换 **为十进制 70,在严格模式也一样。

    为了消除在使用 parseInt() 函数时可能导致的上述困惑,该函数有第二个参数,转换时使用的基数(即多少进制)

    var num parseInt("0xAF", 16); // 175, 如果使用第二个参数,可以省略 0x 
    // 指定基数会影响转换的输出结果
    var num1 = parseInt("10", 2);  // 2  二进制
    var num1 = parseInt("10", 8);  // 8  八进制
    var num1 = parseInt("10", 10); // 10 十进制
    var num1 = parseInt("10", 16); // 16 十六进制
    

    parseFloat() 转换规则:

  • 从第一个字符开始解析每个字符,一直解析到字符串结尾,或解析到一个无效的浮点数字符为止。

  • 字符串中第一个小数点有效,而第二个小数点就无效了,因为后面的字符串将被忽略。
  • 十六进制字符串则被转换为0,只解析十进制,因此没有第二个参数指定基数用法。

    // 例如:
    var num1 = parseFloat("1234blue");  // 1234 整数
    var num2 = parseFloat("0xA");       // 0
    var num3 = parseFloat("22.5");      // 22.5
    var num4 = parseFloat("22.34.5");   // 22.34
    var num5 = parseFloat("0908.5");    // 908.5
    var num6 = parseFloat("3.125e7");   // 31250000
    

5.6. String类型

String 由 0 或多个 16 位 Unicode 字符组成的字符序列,即为字符串。字符串可以由(双引号””、单引号’ ‘)表示。

  1. 字符字面量

    String 数据类型包含了一些特殊的字符字面量,也叫转义序列,用于表示非打印字符,或其他用途的字符。

字面量 含义
\n 换行
\t 制表符
\b 退格
\r 回车
\f 进纸
\\ 斜杠
\’ 单引号(‘),在用单引号表示的字符串中使用
\” 双引号(”),在用双引号表示的字符串中使用
\xnn 以十六进制代码nn表示的一个字符(其中n为0~F)
\unnnn 以十六进制代码nnnn表示的一个Unicode字符(其中n为0-F)

这些字符字面量可以出现在字符串中的任何位置,而且也将被作为一个字符来解析,如下列例子:

  1. 字符串的特点

    ECMAScript 中的字符串时不可变的,字符串一旦创建,它们的值就不能改变。要改变某个变量保存的字符串,首先要销毁原来的字符串,然后在用另外一个包含新值的字符串填充该变量,这个过程是在后台发生的。这也是为什么老版本浏览器 拼接字符串时速度很慢的原因所在。

    var lang = "Java";
    lang = lang + "Script";
    
  2. 转换为字符串

    把值转换为字符串有两种:toString()、String()

  • 几乎所有值都有 toString()方法,但 Null 和 undefined 值没有这个方法。
  • toString(基数),默认不指定基数情况返回十进制,可以输出 二进制、八进制、十六进制,乃至任意有效进制表示的字符串值

    var num = 10;
    alert(num.toString());    // "10"
    alert(num.toString(2));   // "1010"
    alert(num.toString(8));   // "12"
    alert(num.toString(10));  // "10"
    alert(num.toString(16));  // "a"
    
  • 在不知道要转换的值是不是 null 或 undefined 的情况下,可使用转型函数 String(),该函数能将任何类型的值转换为字符串。

  • 转换规则:

    • null => null
    • undefined => undefined
    • 值有 toString() 就会调用该方法(没有参数),并返回相应的结果。
    var value1 = 10;
    var value2 = true;
    var value3 = null;
    var value4;
    
    alert(String(value1)); // "10"
    alert(String(value2)); // "true"
    alert(String(value3)); // "null"
    alert(String(value4)); // "undefined"
    

5.7. object类型

ECMAScript 中的对象其实就是一组数据和功能的集合。对象可以通过执行 new 操作符后跟要创建的对象类型的名称来创建。

var o = new Object(); // 创建对象
var o = new Object;   // 有效,但不推荐

仅仅创建对象并没有什么用,但关键要理解一个重要思想:EMAScript 中 Object 类型是所有它的实例的基础

Ojbect 的每个实例都具有下列属性和方法:

  • constructor:保存着用于创建对象的函数。构造函数(constructor) 就是Object()
  • hasOwnProperty(propertyName):用于检查给定的属性在当前对象实例中(而不是在实例的原型中)是否存在。必须以字符串形式指定。
  • isPrototypeOf(object):用于检查传入的对象是否是当前对象的原型。
  • propertyIsEnumerable(propertyName):用于检查给定的属性是否能够使用 for-in 语句来枚举,必须以字符串形式指定。
  • toLocalString():返回对象的字符串表示,该字符串与执行环境的地区对应。
  • toString():返回对象的字符串表示。
  • valueOf():返回对象的字符串、数值或布尔值表示。通常与 toString() 方法的返回值相同。

由于在ECMAScript中 object 是所有对象的基础,因此所有对象都具有这些基本的属性和方法。

6. 操作符

ECNA-262 描述了一组用于操作数据值的操作符,(算数运算符、位操作符、关系操作符、相等操作符)。ECMAScript 操作符的与众不同之处,它们能够适用于很多值,例如(字符串、布尔值、对象)。在应用对象时,相应操作符通常会调用对象的 valueOf 或 toString 方法,以便取得可以操作的值。

6.1. 一元操作符

var a = 1;
a++; // 先使用,再自增 
++a; // 先自增,再使用

6.2. 位操作符

  1. 按位非 ~ (NOT) 转二进制,进制位取反,返回十进制
  2. 按位与 & (AND) 转二进制,取相同为1的进制位,返回十进制
  3. 按位或 | (OR) 转二进制,取有1的进制位,返回十进制
  4. 按位异或 ^ (XOR)转二进制,只有1和0对比才返回1,其他都为0,返回十进制
  5. 左移 (<<) 转二进制,左边是值右边是移动的参数,转二进制,全部值移动X参数
  6. 有符号右移(>>) 于上述相同,如果超出移动范围,返回0
  7. 无符号右移(>>>) 会把数值转为 正数二进制,右移。无符号只有正数,无负数

6.3. 布尔操作符

  1. 逻辑非 !
  2. 逻辑与 &&
  3. 逻辑或 ||

6.4. 乘性操作符

  1. 乘法 *
  2. 除法 /
  3. 求模 %

6.5. 加性操作符

  1. 加法 +
  2. 减法 –

6.6. 关系操作符

  1. 小于 <
  2. 大于 >
  3. 小于等于 <=
  4. 大于等于 >=

6.7. 相等操作符

// 全等和不全等区别

var a = 1;
var b = "1";

alert(a == b);  // true,  不全等,会先转换相同类型,再进行比较。
alert(a === b); // false, 全等,不转换类型,直接比较。

6.8. 条件操作符

// 三目运算符,num1大于 num2 就返回 num1 否则 num2
var max = (num1 > num2) ? num1 : num2;

6.9. 赋值操作符

乘/赋值(*=)
除/赋值(/=)
模/赋值(%=)
加/赋值(+=)
减/赋值(-=)
左移/赋值(<<=)
有符号右移/赋值(>>=)
无符号右移/赋值(>>>=)

6.10. 逗号操作符

var num1=1, num2=2, num3=3; // 逗号运算符
var num = (5, 1, 4, 8, 0);  // 这样赋值,只取最后一个0

7. 语句

ECMA-262 规定了一组语句(也称流控制语句)。从本质上看,语句定义了 ECMAScript 中主要的语句,语句通常使用一个或多个关键字来完成给定任务。

7.1. if语句

if(i>25)
    alert("Greater than 25.")           // 单行语句
else{ 
    alert("Less than or equal to 25."); // 代码块中的语句
}

7.2. Do-while语句

do {
    statement
}while (expression);

7.3. for 语句

for(initialization; expression; post-loop-expression){
  statement
}

7.4. for-in语句

for (property in expression){
  statement
}

7.5. label语句

start: for (var i=0; i < count; i++){
    alert(i)
}
// 这个例子中定义的 start 标签可以在将来由 break 或 continue 语句引用。

7.6. break 和 continue语句

var num = 0;
for (var i=1; i<10; i++){
  if(i % 5 ==0){
    // break;
    // break outermost;
    // continue outermost;
  }
  num++;
}
alert(num); // 4

7.7. with语句

// 定义 with 语句的目的主要是为了简化多次编写同一个对象的工作:
var qs = localtion.search.substring(1);
var hostName = localtion.hostname;
var url = localtion.href;

// with 语句的作用是将代码的作用域设置到一个特定的对象中。
with (location) {
    var qs = search.substring(1);
    var hostName = hostname;
    var url = href;
}

7.8. switch语句

虽然 ECMAScript 中的 switch 语句借鉴其他语言,但这个语句也有自己的特色。

  • 可以使用任何数据类型(字符串、对象),每个case 可以是(常量、变量、表达式)

switch ("hellow world") { case 25: alert("25"); case 35: alert("35"); break; case num < 0: break; case num >= 10 && num <= 20: break; case "hello" + "world": break; default: alert("Other"); break; }

8. 函数

// 1.函数
function sayHi(name) {
  return; // 返回,下面永远不会调用。
  alert("hello" + name)
}

8.1. 理解参数

该函数和其他大多数语言的函数的参数有所不同:

  • 不介意传进多少参数
  • 不介意传任何类型
  • 因为ECMAScript中的参数在内部是用一个数组来表示的。
  • ECMAScript 中的所有参数传递都是值,不可能通过引用传递参数。

8.2. 没有重载

ECMAScript 函数没有传统意义上那样实现重载。如果定义两个名字相同的函数,后面定义的函数会覆盖前面的。

9. 总结

  • ECMAScript 中的基本数据类型包括 Undefined、Null、Boolean、Number、String
  • ECMAScript 没有为整数、浮点数分别定义不同的数据类型,Number可以表示所有数值
  • ECMAScript 也有一种复杂数据类型 Object,该类型是这门语言所有对象的基础类型
  • ECMAScript 提供了很多与C及其他类语言相同的基本操作符、算数、布尔、关系、想定、赋值操作符等
  • ECMAScript 借鉴了很多流程控制语句,如if、for、switch等,但与其他语言有诸多不同之处
  • 无需指定函数返回值,函数在任何时候都可以返回任何值
  • 函数没有重载概念,因为函数参数是一个或多个值的数组形式传递
  • 函数可以传递任意数量的参数,并且可以通过 arguments 对象来访问这些参数

这些都是 JavaScript 基础概念,需要熟记。世界那么多语言,万变不离其宗。。

发表评论