第三章 基本概念

199 阅读17分钟

1、语法

严格模式

严格模式是为 JavaScript 定义了一种不同的解析与执行模型。在严格模式下,ECMAScript 中的一些不确定的行为将得到处理,而且对某些不安全的操作也会抛出错误。要在整个脚本中启用严格模式,可以在顶部添加如下代码:

"use strict";

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

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

2、数据类型

数据类型包括5中基本数据类型(Undefined、Null、Boolean、Number和String)和一种复杂数据类型(Object),可以通过typeof操作符来判断。此外,typeof还可以用来区分函数(返回function)和其他对象。typeof 是一个操作符而不是函数,因此后边的括号尽管可以使用,但不是必需的。

var message = "hello";
alert(typeof message);
alert(typeof (message));

Undefined

对未初始化的变量执行 typeof 操作符会返回 undefined 值,而对未声明的变量执行 typeof 操作符同样也会返回 undefined 值。

var message;
//var age;
alert(typeof message);//undefined
alert(typeof age);//undefined

Null

null 值表示一个空对象指针,而这也正是使用 typeof 操作符检测 null 值时会返回"object"的原因

var car = null;
alert(typeof car);//object

如果定义的变量准备在将来用于保存对象,那么最好将该变量初始化为 null 而不是其他值。这样一来,只要直接检查 null 值就可以知道相应的变量是否已经保存了一个对象的引用,如下面的例子所示:

if (car != null){
    // 对 car 对象执行某些操作
} 

实际上,undefined 值是派生自 null 值的,因此 ECMA-262 规定对它们的相等性测试要返回 true:

alert(null == undefined); //true 
alert(null === undefined);//false

Boolen

转型函数 Boolean()用于将一个值转换为其对应的 Boolean 值,对任何数据类型的值调用 Boolean()函数会返回一个 Boolean 值。

数据类型 转换为true的值 转换为false的值
Boolen true false
String 任何非空字符串 ""(空字符串)
Number 任何非零数值(包括无穷大) 0和NAN
Object 任何对象(包括空对象) null
Undefined undefined

Number

0除以0返回NaN(整数除以0为Infinity 或-Infinity,Infinity 除以Infinity 为 NaN,Infinity乘以0为NaN,Infinity乘以非0数值为Infinity 或-Infinity),NaN是一个特殊的数值,它和任何值不相等,包括本身。isNaN()在接收到一个值之后,会尝试将这个值转换为数值。某些不是数值的值会直接转换为数值,例如字符串"10"或 Boolean 值。而任何不能被转换为数值的值都会导致这个函数返回 true。

alert(isNaN(NaN)); //true
alert(isNaN(10)); //false(10是一个数值)
alert(isNaN("10")); //false(可以被转换成数值10)
alert(isNaN(""));//false
alert(isNaN("blue")); //true(不能转换成数值)
alert(isNaN(true)); //false(可以被转换成数值1)

数值转换

  • Number()

    • Boolean 值,true 和 false 将分别被转换为 1 和 0。

    • 数字值,只是简单的传入和返回。

    • null 值,返回 0。

    • undefined,返回 NaN。

    • 字符串:

      • 字符串中只包含数字(包括前面带正号或负号的情况),则将其转换为十进制数值,即"1" 会变成 1,"123"会变成 123,而"011"会变成 11(忽略前导零);
      • 字符串中包含有效的浮点格式,如"1.1",则将其转换为对应的浮点数值(忽略前导零);
      • 字符串中包含有效的十六进制格式,例如"0xf",则将其转换为相同大小的十进制整数值;
      • 空字符串,转换为 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 
    
  • parseInt()

    在使用 parseInt()解析像八进制字面量的字符串时,ECMAScript 3 和 5 存在分歧。例如: //ECMAScript 3 认为是 56(八进制),ECMAScript 5 认为是 70(十进制) var num = parseInt("070"); 在 ECMAScript 3 JavaScript 引擎中,"070"被当成八进制字面量,因此转换后的值是十进制的 56。 而在 ECMAScript 5 JavaScript 引擎中,parseInt()已经不具有解析八进制值的能力,因此前导的零会被认为无效,从而将这个值当成"70",结果就得到十进制的 70。为了消除在使用 parseInt()函数时可能导致的上述困惑,可以为这个函数提供第二个参数:转换时使用的基数(即多少进制)。如果知道要解析的值是十六进制格式的字符串,那么指定基数 16 作为第二个参数,可以保证得到正确的结果,例如: var num = parseInt("0xAF", 16); //175 实际上,如果指定了 16 作为第二个参数,字符串可以不带前面的"0x",如下所示:

    var num1 = parseInt("AF", 16); //175 
    var num2 = parseInt("AF"); //NaN 
    //parseInt的一些其他例子
    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(八进制数)
    var num6 = parseInt("70"); // 70(十进制数)
    var num7 = parseInt("0xf"); // 15(十六进制数)
    
  • parseFloat()

    parseFloat()与 parseInt()有两个区别:一、第一个小数点有效,二、它始终都会忽略前导的零。

    parseFloat()可以识别浮点数值格式,也包括十进制整数格式。但十六进制格式的字符串则始终会被转换成 0。由于 parseFloat()只解析十进制值,因此它没有用第二个参数指定基数的用法。最后还要注意一点:如果字符串包含的是一个可解析为整数的数(没有小数点,或者小数点后都是零),parseFloat()会返回整数。

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 

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" 
    
  • String()方法

    该方法可以将任何类型的值转换为字符串,包括null和undefined,转换规则如下:

    • 如果值有 toString()方法,则调用该方法(没有参数)并返回相应的结果;

    • 如果值是 null,则返回"null";

    • 如果值是 undefined,则返回"undefined"。

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" 

Object

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

  • constructor:构造函数,保存着用于创建当前对象的函数。

  • hasOwnProperty(propertyName):用于检查给定的属性在当前对象实例中(而不是在实例的原型中)是否存在。属性名(propertyName)作为参数,必须为字符串(如:o.hasOwnProperty("name"))。

  • isPrototypeOf(object):用于检查传入的对象是否是传入对象的原型。

  • propertyIsEnumerable(propertyName):用于检查给定的属性是否能够使用 for-in 语句来枚举。与 hasOwnProperty()方法一样,作为参数的属性名必须为字符串。

  • toLocaleString():返回对象的字符串表示,该字符串与执行环境的地区对应。

  • toString():返回对象的字符串表示。

  • valueOf():返回对象的字符串、数值或布尔值表示。通常与 toString()方法的返回值相同。

3、操作符

一元操作符

  1. ++、--

不仅适用于整数,还可以用于字符串、布尔值、浮 点数值和对象。在应用于不同的值时,递增和递减操作符遵循下列规则:

  • 在应用于一个包含有效数字字符的字符串时,先将其转换为数字值,再执行加减 1 的操作。字 符串变量变成数值变量。

  • 在应用于一个不包含有效数字字符的字符串时,将变量的值设置为 NaN(第 4 章将详细讨论)。 字符串变量变成数值变量。

  • 在应用于布尔值 false 时,先将其转换为 0 再执行加减 1 的操作。布尔值变量变成数值变量。

  • 在应用于布尔值 true 时,先将其转换为 1 再执行加减 1 的操作。布尔值变量变成数值变量。

  • 在应用于浮点数值时,执行加减 1 的操作。

  • 在应用于对象时,先调用对象的 valueOf()方法(第 5 章将详细讨论)以取得一个可供操作的 值。然后对该值应用前述规则。如果结果是 NaN,则在调用 toString()方法后再应用前述规 则。对象变量变成数值变量。

var s1 = "2";
var s2 = "z";
var b = false;
var f = 1.1;
var o = {
 	valueOf: function() {
 	return -1;
 	}
};
s1++; // 值变成数值 3
s2++; // 值变成 NaN
b++; // 值变成数值 1
f--; // 值变成 0.10000000000000009(由于浮点舍入错误所致)
o--; // 值变成数值-2
  1. +、-

对非数值应用一元加操作符时,该操作符会像 Number()转型函数一样对这个值执行转换。 换句话说,布尔值 false 和 true 将被转换为 0 和 1,字符串值会被按照一组特殊的规则进行解析,而 对象是先调用它们的 valueOf()和(或)toString()方法,再转换得到的值。

var s1 = "01";
var s2 = "1.1";
var s3 = "z";
var b = false;
var f = 1.1;
var o = {
 valueOf: function() {
 return -1;
 }
};
s1 = +s1; // 值变成数值 1
s2 = +s2; // 值变成数值 1.1
s3 = +s3; // 值变成 NaN
b = +b; // 值变成数值 0
f = +f; // 值未变,仍然是 1.1
o = +o; // 值变成数值-1 

位操作符

  1. 按位非(NOT)

    按位非操作符由一个波浪线(~)表示,执行按位非的结果就是返回数值的反码。

var num1 = 25; // 二进制 00000000000000000000000000011001
var num2 = ~num1; // 二进制 11111111111111111111111111100110
alert(num2); // -26 
var num1 = 25;
var num2 = -num1 - 1;
alert(num2); // "-26" 
//虽然以上代码也能返回同样的结果,但由于按位非是在数值表示的最底层执行操作,因此速度更快。
  1. 按位与(AND)

    按位与操作符由一个和号字符(&)表示,它有两个操作符数。从本质上讲,按位与操作就是将两个数值的每一位对齐,只在两个数值的对应位都是 1 时才返回 1,任何一位是 0,结果都是 0。

    var result = 25 & 3;
    alert(result); //1 
    //25  = 0000 0000 0000 0000 0000 0000 0001 1001
    //3   = 0000 0000 0000 0000 0000 0000 0000 0011
      ---------------------------------------------
    //AND = 0000 0000 0000 0000 0000 0000 0000 0001 
    
  2. 按位或(OR)

    按位或操作符由一个竖线符号(|)表示,也有两个操作数。按位或操作在有一个位是 1 的情况下就返回 1,而只有在两个位都是 0 的情况下才返回 0。

    var result = 25 | 3;
    alert(result); //27 
    //25 = 0000 0000 0000 0000 0000 0000 0001 1001
    //3  = 0000 0000 0000 0000 0000 0000 0000 0011
      --------------------------------------------
    //OR = 0000 0000 0000 0000 0000 0000 0001 1011 
    
  3. 按位异或(XOR)

    按位异或操作符由一个插入符号(^)表示,也有两个操作数。按位异或与按位或的不同之处在于,这个操作在两个数值对应位上只有一个 1 时才返回 1,如果对应的两位都是 1 或都是 0,则返回 0(相同为0,不同为1)。

    var result = 25 ^ 3;
    alert(result); //26 
    //25  = 0000 0000 0000 0000 0000 0000 0001 1001
    //3   = 0000 0000 0000 0000 0000 0000 0000 0011
      ---------------------------------------------
    //XOR = 0000 0000 0000 0000 0000 0000 0001 1010 
    
  4. 左移

    左移操作符由两个小于号(<<)表示,这个操作符会将数值的所有位向左移动指定的位数。

    var oldValue = 2; // 等于二进制的 10
    var newValue = oldValue << 5; // 等于二进制的 1000000,十进制的 64 
    //在向左移位后,原数值的右侧多出了 5 个空位。左移操作会以 0 来填充这些空位,以便得到的结果是一个完整的 32 位二进制数
    

    左移不会影响操作数的符号位。换句话说,如果将-2 向左移动 5 位,结果将是-64,而非 64。

  5. 有符号的右移

    有符号的右移操作符由两个大于号(>>)表示,这个操作符会将数值向右移动,但保留符号位(即 正负号标记),此时会用符号位的值来填充所有空位。有符号的右移操作与左移操作恰好相反。

    var oldValue = 64; // 等于二进制的 1000000
    var newValue = oldValue >> 5; // 等于二进制的 10 ,即十进制的 2 
    
  6. 无符号右移

    无符号右移操作符由 3 个大于号(>>>)表示,这个操作符会将数值的所有 32 位都向右移动。对正数来说,无符号右移的结果与有符号右移相同。但是对负数来说,情况就不一样了。首先,无符号右移是以 0 来填充空位,而不是像有符号右移那样以符号位的值来填充空位。所以,对正数的无符号右移与有符号右移结果相同,但对负数的结果就不一样了。其次,无符号右移操作符会把负数的二进制码当成正数的二进制码。而且,由于负数以其绝对值的二进制补码形式表示,因此就会导致无符号右移后的结果非常之大。

    var oldValue = 64; // 等于二进制的 1000000
    var newValue = oldValue >>> 5; // 等于二进制的 10 ,即十进制的 2 
    var oldValue = -64; // 等于二进制的 11111111111111111111111111000000
    var newValue = oldValue >>> 5; // 等于十进制的 134217726 
    

布尔操作符

  1. 逻辑与(&&) 逻辑与操作可以应用于任何类型的操作数,而不仅仅是布尔值。在有一个操作数不是布尔值的情况 下,逻辑与操作就不一定返回布尔值;此时,它遵循下列规则:

    • 如果第一个操作数是对象,则返回第二个操作数;

    • 如果第二个操作数是对象,则只有在第一个操作数的求值结果为 true 的情况下才会返回该对象;

    • 如果两个操作数都是对象,则返回第二个操作数;

    • 如果有一个操作数是 null,则返回 null;

    • 如果有一个操作数是 NaN,则返回 NaN;

    • 如果有一个操作数是 undefined,则返回 undefined。

  2. 逻辑或(||)

    与逻辑与操作相似,如果有一个操作数不是布尔值,逻辑或也不一定返回布尔值;此时,它遵循下列规则:

    • 如果第一个操作数是对象,则返回第一个操作数;

    • 如果第一个操作数的求值结果为 false,则返回第二个操作数;

    • 如果两个操作数都是对象,则返回第一个操作数;

    • 如果两个操作数都是 null,则返回 null;

    • 如果两个操作数都是 NaN,则返回 NaN;

    • 如果两个操作数都是 undefined,则返回 undefined。

乘性操作符

var a1 = NaN * 1;//NaN
var a2 = 1 * NaN;//NaN
var a3 = Infinity * 0;//NaN
var a4 = Infinity * -2;//-Infinity
var a5 = Infinity * Infinity;//Infinity

var b1 = NaN / 1;//NaN
var b2 = 1 / NaN;//NaN
var b3 = 0/0;//NaN
var b4 = 2/0;//Infinity
var b5 = Infinity / Infinity;//NaN

var c1 = Infinity % 5;//NaN
var c2 = 5 % Infinity;//5
var c3 = 6 % 0;//NaN
var c4 = 0 % 0;//NaN
var c5 = 0 % 6;//0
var c6 = Infinity % Infinity;//NaN

加性操作符

var a1 = Infinity + Infinity;//Infinity
var a2 = -Infinity + -Infinity;//-Infinity
var a3 = Infinity + -Infinity;//NaN
var a4 = +0 + +0;//+0
var a5 = -0 + -0;//-0
var a6 = +0 + -0;//+0

var b1 = Infinity - Infinity;//NaN
var b2 = -Infinity - -Infinity;//NaN
var b3 = Infinity - -Infinity;//Infinity
var b4 = -Infinity - Infinity;//-Infinity
var b5 = +0 - +0;//+0
var b6 = +0 - -0;//-0
var b7 = -0 - -0;//+0

关系操作符

如果两个操作数都是字符串,则比较两个字符串对应的字符编码值。

任何操作数与 NaN 进行 关系比较,结果都是 false。

var result = "Brick" < "alphabet"; //true 
var result = "Brick".toLowerCase() < "alphabet".toLowerCase(); //false 
var result = "23" < "3"; //true 
var result = "23" < 3; //false 
var result = "a" < 3; // false,因为"a"被转换成了 NaN 
var result1 = NaN < 3; //false
var result2 = NaN >= 3; //false 

相等操作符

null == undefined;//true
"NaN" == NaN;//false
5 == NaN;//false
NaN == NaN;//false
NaN != NaN;//true
undefined == 0;//false
null == 0;//false
"5" != 5;//false

4、语句

label语句

使用 label 语句可以在代码中添加标签,以便将来使用。加标签的语句一般都要与 for 语句等循环语句配合使用。

label:statement

start: for (var i=0; i < count; i++) {
 alert(i);
} 

var num = 0;
outermost:
for (var i=0; i < 10; i++) {
    for (var j=0; j < 10; j++) {
        if (i == 5 && j == 5) {
            break outermost;
        }
        num++;
    }
}
alert(num); //55 

var num = 0;
outermost:
for (var i=0; i < 10; i++) {
    for (var j=0; j < 10; j++) {
        if (i == 5 && j == 5) {
            continue outermost;
        }
        num++;
    }
}
alert(num); //95 

with语句

with 语句的作用是将代码的作用域设置到一个特定的对象中,主要是为了简化多次编写同一个对象的工作。严格模式下不允许使用 with 语句,否则将视为语法错误。

with (expression) statement;

var qs = location.search.substring(1);
var hostName = location.hostname;
var url = location.href; 
with(location){
    var qs = search.substring(1);
 	var hostName = hostname;
 	var url = href;
} 

5、函数

参数

参数在内部是用一个数组arguments来表示的,可以使用方括号语法访 问它的每一个元素(即第一个元素是 arguments[0],第二个元素是 argumetns[1],以此类推),使 用 length 属性来确定传递进来多少个参数。arguments 对象可以与命名参数一起使用;arguments的值永远与对应命名参数的值保持同步

function sayHi(name, message) {
 	alert("Hello " + name + "," + message);
} 
function sayHi() {
 	alert("Hello " + arguments[0] + "," + arguments[1]);
}
function doAdd(num1, num2) {
 	if(arguments.length == 1) {
 	alert(num1 + 10);
    } else if (arguments.length == 2) {
        alert(arguments[0] + num2);
    }
} 

没有重载

ECMAScript 函数不能像传统意义上那样实现重载。而在其他语言(如 Java)中,可以为一个函数编写两个定义,只要这两个定义的签名(接受的参数的类型和数量)不同即可。