目录

第五章 - 引用类型

本章内容

  • 使用对象
  • 创建并操作数组
  • 理解基本的 JavaScript 类型
  • 使用基本类型和基本包装类型

引用类型的值(即对象)是引用类型的一个实例。在 ECMAScript 中,引用类型是一种数据结构,用于将数据和功能组织在一起。引用类型有时候也被称为对象定义,因为它们描述的是一类对象所具有的属性和方法。被称为类并不妥当,因为它不具备传统的面向对象语言所支持的类和接口等基本结构。 新对象是使用 new 操作符后跟一个构造函数来创建的。构造函数本身就是一个函数,只不过该函数是出于创建新对象的目的而定义的。

var person = new Object();

这行代码创建了 Object 引用类型的一个新实例,然后把该实例保存在了变量 person 中。

5.1 Object 类型

创建 Object 实例的方式有两种。 第一种是使用 new 操作符后跟 Object 构造函数:

var person = new Object(); 
person.name = "Nicholas"; 
person.age = 29;

另一种方式是使用对象字面量表示法:

var person = { 
    name : "Nicholas", 
    age : 29 
};

在使用对象字面量语法时,属性名也可以使用字符串。

在通过对象字面量定义对象时,实际上不会调用 Object 构造函数。

际上,对象字面量是向函数传递大量可选参数的首选方式,例如:

function displayInfo(args) { 
    var output = ""; 
 
    if (typeof args.name == "string"){ 
        output += "Name: " + args.name + "\n"; 
    } 
 
    if (typeof args.age == "number") { 
        output += "Age: " + args.age + "\n"; 
    } 
 
    alert(output); 
} 
 
displayInfo({  
    name: "Nicholas",  
    age: 29 
}); 
 
displayInfo({ 
    name: "Greg" 
}); 

函数 displayInfo()接受一个名为 args的参数。这个参数可能带有一个名为 name或 age 的属性,也可能这两个属性都有或者都没有。

这种传递参数的模式最适合需要向函数传入大量可选参数的情形。最好的做法是对那些必需值使用命名参数,而使用对象字面量来封装多个可选参数。 访问对象属性时可使用点表示法,也可以使用方括号表示法。 通常,除非必须使用变量来访问属性,否则我们建议使用点表示法。

5.2 Array 类型

与其他语言不同的是,ECMAScript 数组的每一项可以保存任何类型的数据。而且,ECMAScript 数组的大小是可以动态调整的,即可以随着数据的添加自动增长以容纳新增数据。(可从对象的角度来理解) 创建数组的基本方式有两种。第一种是使用 Array 构造函数:

var colors = new Array(); 
var colors = new Array(3); // 创建一个包含 3 项的数组 
var names = new Array("Greg"); // 创建一个包含 1 项,即字符串"Greg"的数组

另外,在使用 Array 构造函数时也可以省略 new 操作符。 第二种基本方式是使用数组字面量表示法:

var colors = ["red", "blue", "green"]; // 创建一个包含 3 个字符串的数组 
var names = []; // 创建一个空数组 

在读取和设置数组的值时,要使用方括号并提供相应值的基于 0 的数字索引。(因为用点访问属性为数字实在是太奇怪了啊) 数组的length 属性的值为数组中最大索引值+1(数组最后一项的索引始终是 length-1);而且通过设置可以从数组的末尾移除项或向数组中添加新项。

5.2.1 检测数组

对于一个网页,或者一个全局作用域而言,使用 instanceof 操作符即可,但如果网页中包含多个框架,那实际上就存在两个以上不同的全局执行环境,从而存在两个以上不同版本的 Array 构造函数。 为了解决这个问题,ECMAScript 5 新增了 **Array.isArray()**方法:

if (Array.isArray(value)){ 
    //对数组执行某些操作 
} 

5.2.2 转换方法

如前所述,所有对象都具有 toLocaleString()、toString()和 valueOf()方法。其中,调用数组的 toString()方法会返回由数组中每个值的字符串形式拼接而成的一个以逗号分隔的字符串。而调用 **valueOf()**返回的还是数组。

var colors = ["red", "blue", "green"];
colors.toString(); // "red,blue,green"
colors.valueOf(); // ["red", "blue", "green"]
colors; // ["red", "blue", "green"]

而如果使用 join()方法,则可以使用不同的分隔符来构建这个字符串(不传默认使用逗号分隔):

var colors = ["red", "green", "blue"]; 
console.log(colors.join(","));       // "red,green,blue" 
console.log(colors.join("||"));      // "red||green||blue"

5.2.3 栈方法

ECMAScript 为数组专门提供了 **push()**和 **pop()**方法,以便实现类似栈的行为。 push()方法可以接收任意数量的参数,把它们逐个添加到数组末尾,并返回修改后数组的长度。 pop()方法则从数组末尾移除最后一项,减少数组的 length 值,然后返回移除的项。

5.2.4 队列方法

队列数据结构的访问规则是 FIFO(First-In-First-Out,先进先出)。弹出第一项并返回的方法为shift(),再结合**push()**方法。 ECMAScript 还为数组提供了一个 **unshift()方法:能在数组前端添加任意个项并返回新数组的长度。同时使用 unshift()和 pop()**方法,可以从相反的方向来模拟队列,即在数组的前端添加项,从数组末端移除项。

5.2.5 重排序方法

数组中已经存在两个可以直接用来重排序的方法:reverse()和 sort()

  • sort()方法: 默认按升序排列数组项。为了实现排序,sort()方法会调用每个数组项的 toString()转型方法,然后比较得到的字符串,以确定如何排序。即使数组中的每一项都是数值,sort()方法比较的也是字符串。 sort()方法可以接收一个比较函数作为参数,以便我们指定哪个值位于哪个值的前面。比较函数接收两个参数:
function compare(value1, value2) { 
    if (value1 < value2) { 
        return -1; //看负数在哪,顺序就在哪
    } else if (value1 > value2) { 
        return 1; 
    } else { 
        return 0; 
    } 
}
var values = [0, 1, 5, 10, 15]; 
values.sort(compare); 
alert(values); //0,1,5,10,15 

对于数值类型或者其 valueOf()方法会返回数值类型的对象类型,可使用更简单的比较函数:

function compare(value1, value2){ 
    return value2 - value1; 
}

5.2.6 操作方法

**concat()**方法可以基于当前数组中的所有项创建一个新数组:

var colors = ["red", "green", "blue"]; 
var colors2 = colors.concat("yellow", ["black", "brown"]); 
 
alert(colors);     //red,green,blue         
alert(colors2);    //red,green,blue,yellow,black,brown 

**slice()**方法它能够基于当前数组中的一或多个项创建一个新数组,slice()方法可以接受一或两个参数,即要返回项的起始和结束位置。

var colors = ["red", "green", "blue", "yellow", "purple"]; 
var colors2 = colors.slice(1); 
var colors3 = colors.slice(1,4); 
 
alert(colors2);   //green,blue,yellow,purple 
alert(colors3);   //green,blue,yellow 

如果 slice()方法的参数中有一个负数,则用数组长度加上该数来确定相应的位置。如果结束位置小于起始位置,则返回空数组。

**splice()**方法比较强大(会修改原数组),始终都会返回一个数组,该数组中包含从原始数组中删除的项。使用方式有三种:

  • 删除:可以删除任意数量的项,只需指定 2 个参数:要删除的第一项的位置和要删除的项数。
  • 可以向指定位置插入任意数量的项,只需提供 3 个参数:起始位置、0(要删除的项数)和要插入的项。
  • 替换:可以向指定位置插入任意数量的项,且同时删除任意数量的项,只需指定 3 个参数:起始位置、要删除的项数和要插入的任意数量的项。

5.2.7 位置方法

两个位置方法:indexOf()lastIndexOf()。这两个方法都接收两个参数:要查找的项和表示查找起点位置的索引(可选)。其中,indexOf()方法从数组的开头(位置 0)开始向后查找,lastIndexOf()方法则从数组的末尾开始向前查找。

5.2.8 迭代方法

定义了 5 个迭代方法。每个方法都接收两个参数:要在每一项上运行的函数和(可选的)运行该函数的作用域对象——影响 this 的值。传入方法中的函数会接收三个参数:数组项的值、该项在数组中的位置和数组对象本身。给出5个迭代方法的作用:

  • every():对数组中的每一项运行给定函数,如果该函数对每一项都返回 true,则返回 true。
  • filter():对数组中的每一项运行给定函数,返回该函数会返回 true 的项组成的数组。(用于过滤)
  • forEach():对数组中的每一项运行给定函数。这个方法没有返回值。(类似for循环迭代数组)
  • map():对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组。
  • some():对数组中的每一项运行给定函数,如果该函数对任一项返回 true,则返回 true。

以上方法都不会修改数组中的包含的值。

5.2.9 归并方法

ECMAScript 5 还新增了两个归并数组的方法:**reduce()**和 reduceRight()。这两个方法都会迭代数组的所有项,然后构建一个最终返回的值。其中,reduce()方法从数组的第一项开始,逐个遍历到最后。而 reduceRight()则从数组的最后一项开始,向前遍历到第一项。 (区别取决于要从哪头开始遍历数组) reduce()和 reduceRight()的函数接收 4 个参数:前一个值、当前值、项的索引和数组对象。这个函数返回的任何值都会作为第一个参数自动传给下一项。第一次迭代发生在数组的第二项上,因此第一个参数是数组的第一项,第二个参数就是数组的第二项。

var values = [1,2,3,4,5]; 
var sum = values.reduce(function(prev, cur, index, array){ 
    return prev + cur;  
}); 
alert(sum); //15 

5.3 Date 类型

ECMAScript 中的 Date 类型是在早期 Java 中的 java.util.Date 类基础上构建的。 要创建一个日期对象,使用 new 操作符和 Date 构造函数即可:

var  now = new Date(); 

ECMAScript 提供了两个方法:Date.parse()和 Date.UTC()。 **Date.parse()**方法接收一个表示日期的字符串参数(“月/日/年”,如 6/13/2004; ),然后尝试根据这个字符串返回相应日期的毫秒数。实际上,如果直接将表示日期的字符串传递给 Date 构造函数,也会在后台调用 Date.parse():

var someDate = new Date(Date.parse("May 25, 2004"));
// 即 
var  someDate = new Date("May 25, 2004"); 

使用 **Date.UTC()**方法的例子:

// GMT 时间 2000 年 1 月 1 日午夜零时 
var y2k = new Date(Date.UTC(2000, 0)); 
 
// GMT 时间 2005 年 5 月 5 日下午 5:55:55 
var allFives = new Date(Date.UTC(2005, 4, 5, 17, 55, 55)
);
// Date 构造函数也会模仿 Date.UTC()
// 本地时间 2000 年 1 月 1 日午夜零时 
var y2k = new Date(2000, 0); 

ECMAScript 5 添加了 Data.now()方法,返回表示调用这个方法时的日期和时间的毫秒数。 例如我们可以取得开始到结束所经过的时间:

//取得开始时间 
var start = Date.now(); 
 
//调用函数 
doSomething(); 
 
//取得停止时间 
var stop = Date.now(), 
     result = stop  start;

5.3.1 继承的方法

与其他引用类型一样,Date 类型也重写了 toLocaleString()、toString()和 valueOf()方法; Date 类型的 toLocaleString()方法会按照与浏览器设置的地区相适应的格式返回日期和时间:

c = new Date().toLocaleString() // "2020/3/21 下午12:07:13"

toString()方法则通常返回带有时区信息的日期和时间:

c = new Date().toString() // "Sat Mar 21 2020 12:08:31 GMT+0800 (中国标准时间)"

valueOf()方法,则根本不返回字符串,而是返回日期的毫秒表示:

5.3.2 日期格式化方法

Date 类型还有一些专门用于将日期格式化为字符串的方法,这些方法如下。

  • toDateString()——以特定于实现的格式显示星期几、月、日和年;
  • toTimeString()——以特定于实现的格式显示时、分、秒和时区;
  • toLocaleDateString()——以特定于地区的格式显示星期几、月、日和年;
  • toLocaleTimeString()——以特定于实现的格式显示时、分、秒;
  • toUTCString()——以特定于实现的格式完整的 UTC 日期。

5.3.3 日期/时间组件方法

Date 类型的方法如下表所示(部分):

方法 说明
getTime() 返回表示日期的毫秒数;与valueOf()方法返回的值相同
setTime(毫秒) 以毫秒数设置日期,会改变整个日期
getFullYear() 取得4位数的年份(如2007而非仅07)

5.4 RegExp 类型

ECMAScript 通过 RegExp 类型来支持正则表达式。使用下面类似 Perl 的语法,就可以创建一个正则表达式。

var expression = / pattern / flags ;

(待续)

5.5 Function 类型

ECMAScript 中什么最有意思,我想那莫过于函数了:函数是对象。每个函数都是 Function 类型的实例,而且都与其他引用类型一样具有属性和方法。因此函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定。 由于函数名仅仅是指向函数的指针,因此函数名与包含对象指针的其他变量没有什么不同。换句话说,一个函数可能会有多个名字:

function sum(num1, num2){ 
    return num1 + num2; 
} 
alert(sum(10,10));        //20 
 
var anotherSum = sum; 
alert(anotherSum(10,10)); //20 
 
sum = null; 
alert(anotherSum(10,10)); //20 

5.5.1 没有重载(深入理解)

将函数名想象为指针,也有助于理解为什么 ECMAScript 中没有函数重载的概念。

5.5.2 函数声明与函数表达式

解析器会率先读取函数声明,并使其在执行任何代码之前可用(可以访问)(函数声明提升(function declaration hoisting));至于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解释执行。

alert(sum(10,10)); 
function sum(num1, num2){ 
    return num1 + num2; 
} 

5.5.3 作为值的函数

因为 ECMAScript 中的函数名本身就是变量,所以函数也可以作为值来使用。

function callSomeFunction(someFunction, someArgument){ 
    return someFunction(someArgument); 
} 

这里的 callSomeFunction()函数是通用的,即无论第一个参数中传递进来的是什么函数,它都会返回执行第一个参数后的结果。要访问函数的指针而不执行函数的话,必须去掉函数名后面的那对圆括号。 当然,可以从一个函数中返回另一个函数,而且这也是极为有用的一种技术,比如我们想要根据某个对象属性对数组进行排序而设计的比较函数,它接收一个属性名,然后根据这个属性名来创建一个比较函数:

function createComparisonFunction(propertyName) { 
    // 这里的object1\2哪来的,看看sort对比较函数的定义
    return function(object1, object2){ 
        var value1 = object1[propertyName]; 
        var value2 = object2[propertyName]; 
 
        if (value1 < value2){ 
            return -1; 
        } else if (value1 > value2){ 
            return 1; 
        } else { 
            return 0; 
        } 
    }; 
}
var data = [{name: "Zachary", age: 28}, {name: "Nicholas", age: 29}]; 
 
data.sort(createComparisonFunction("name")); 
alert(data[0].name);  //Nicholas 
 
data.sort(createComparisonFunction("age")); 
alert(data[0].name);  //Zachary 

5.5.4 函数内部属性

在函数内部,有两个特殊的对象:argumentsthis。虽然 arguments 的主要用途是保存函数参数,但这个对象还有一个名叫 callee 的属性,该属性是一个指针,指向拥有这个 arguments 对象的函数。于是递归函数中的函数名可以用arguments.callee表示:

function factorial(num){ 
    if (num <=1) { 
        return 1; 
    } else { 
        return num * arguments.callee(num-1) 
    } 
} 

函数内部的另一个特殊对象是 this,其行为与 Java 和 C#中的 this 大致类似。this引用的是函数据以执行的环境对象,来看例子:

window.color = "red"; 
var o = { color: "blue" }; 
 
function sayColor(){ 
    alert(this.color); 
} 
 
sayColor();     //"red" 
 
o.sayColor = sayColor; 
o.sayColor();   //"blue" 

ECMAScript 5 也规范化了另一个函数对象的属性:caller。这个属性中保存着调用当前函数的函数的引用,如果是在全局作用域中调用当前函数,它的值为 null。

function outer(){ 
    inner();  
} 
 
function inner(){ 
    console.log(inner.caller); 
} 
 
outer(); // 输出outer()函数的源码

当函数在严格模式下运行时,访问 arguments.callee 会导致错误。arguments.caller 属性在严格模式下访问它也会导致错误。这是为了加强这门语言的安全性,这样第三方代码就不能在相同的环境里窥视其他代码了。

5.5.5 函数属性和方法

函数是对象,因此也有属性和方法。每个函数都包含两个属性:length 和 prototype。 length 属性表示函数希望接收的命名参数的个数。 prototype 属性保存所有实例方法。在 ECMAScript 5 中,prototype 属性是不可枚举的,因此使用 for-in 无法发现。

每个函数都包含两个非继承而来的方法:**apply()**和 call()。这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内 this 对象的值。 首先,apply()方法接收两个参数:一个是在其中运行函数的作用域,另一个是参数数组。其中,第二个参数可以是 Array 的实例,也可以是arguments 对象。例如:

function sum(num1, num2){ 
    return num1 + num2; 
} 
 
function callSum1(num1, num2){ 
 return sum.apply(this, arguments); // 传入 arguments 对象 
} 
 
function callSum2(num1, num2){ 
    return sum.apply(this, [num1, num2]);    // 传入数组 
} 
 
alert(callSum1(10,10));   //20 
alert(callSum2(10,10));   //20

callSum1()在执行 sum()函数时传入了 this 作为 this 值(因为是在全局作用域中调用的,所以传入的就是 window 对象)。 call()方法与 apply()方法的作用相同,它们的区别仅在于接收参数的方式不同:第一个参数是 this 值没有变化,变化的是其余参数都直接传递给函数。在使用call()方法时,传递给函数的参数必须逐个列举出来,如下面的例子所示:

function sum(num1, num2){ 
    return num1 + num2; 
} 
 
function callSum(num1, num2){ 
    return sum.call(this, num1, num2); 
} 
 
alert(callSum(10,10));   //20 

它们真正强大的地方是能够扩充函数赖以运行的作用域

window.color = "red"; 
var o = { color: "blue" }; 
 
function sayColor(){ 
    alert(this.color); 
} 
 
sayColor();                //red 
 
sayColor.call(this);       //red 
sayColor.call(window);     //red 
sayColor.call(o);          //blue

当运行 sayColor.call(o)时,函数的执行环境就不一样了,因为此时函数体内的 this 对象指向了 o,于是结果显示的是"blue"。 使用 call()(或 apply())来扩充作用域的最大好处,就是对象不需要与方法有任何耦合关系。(对比前文5.5.4的例子,需要将sayColor()放入o对象里;而这里使用了call/apply就比较方便了)

ECMAScript 5 还定义了一个方法:bind()。这个方法会创建一个函数的实例,其 this 值会被绑定到传给 bind()函数的值。例如:

window.color = "red"; 
var o = { color: "blue" }; 
var h = {color:'yellow'}
function sayColor(){ 
    console.log(this.color); 
}  
var objectSayColor = sayColor.bind(o); 
objectSayColor();    // blue
var h_say_color = sayColor.bind(h);
h_say_color(); // yellow

5.6 基本包装类型

为了便于操作基本类型值,ECMAScript 还提供了 3 个特殊的引用类型:BooleanNumberString实际上,每当读取一个基本类型值的时候,后台就会创建一个对应的基本包装类型的对象,从而让我们能够调用一些方法来操作这些数据。

var s1 = "some text"; 
var s2 = s1.substring(2);

我们知道,基本类型值不是对象,因而从逻辑上讲它们不应该有方法。其实,为了让我们实现这种直观的操作,后台已经自动完成了一系列的处理:

  • (1) 创建 String 类型的一个实例;
  • (2) 在实例上调用指定的方法;
  • (3) 销毁这个实例。

可以将以上三个步骤想象成是执行了下列 ECMAScript 代码。

var s1 = new String("some text"); 
var s2 = s1.substring(2); 
s1 =  null; 

经过此番处理,基本的字符串值就变得跟对象一样了。而且,上面这三个步骤也分别适用于 Boolean和 Number 类型对应的布尔值和数字值。 (所以叫“包装类型”吗:))

引用类型与基本包装类型的主要区别就是对象的生存期。使用 new 操作符创建的引用类型的实例,在执行流离开当前作用域之前都一直保存在内存中。而自动创建的基本包装类型的对象,则只存在于一行代码的执行瞬间,然后立即被销毁。意味着基本类型值不能添加属性和方法

var s1 = "some text"; 
s1.color = "red"; 
alert(s1.color); //undefined 

对基本包装类型的实例调用 typeof 会返回"object",而且所有基本包装类型的对在布尔表达式中都会被转换为布尔值 true(包括falseObject)。

let a = 'ddadad'
typeof a // "string"

let bb = new String()
typeof bb // "object"
// Object 构造函数也会像工厂方法一样,根据传入值的类型返回相应基本包装类型的实例
var obj = new Object("some text"); 
alert(obj instanceof String);   //true 

要注意的是,使用 new 调用基本包装类型的构造函数,与直接调用同名的转型函数是不一样的。

var value = "25"; 
var number = Number(value);  //转型函数 
alert(typeof number);        //"number" 
 
var obj = new Number(value); //构造函数 
alert(typeof obj);           //"object"

5.6.1 Boolean类型

Boolean 对象在 ECMAScript 中的用处不大,因为它经常会造成人们的误解。

var falseObject = new Boolean(false); 
var result = falseObject && true; 
alert(result);  //true

基本类型与引用类型的布尔值还有两个区别:

alert(typeof falseObject);   //object 
alert(typeof falseValue);    //boolean 
alert(falseObject instanceof Boolean); //true 
alert(falseValue instanceof Boolean); //false

理解区别但永远不要使用Boolean对象(所以这是谁拍脑门子规定的???)

5.6.2 Number类型

创建 Number 对象:

var numberObject = new Number(10); 
// 实际上不建议直接实例化Number对象,原因与显式创建 Boolean 对象一样
// var a = 10 它不香吗???

Number 类型也重写了 valueOf()、toLocaleString()和 toString()方法。 valueOf()方法返回对象表示的基本类型的数值,另外两个方法则返回字符串形式的数值。toString()方法传递一个表示基数的参数,告诉它返回几进制数值的字符串形式。 除了继承的方法,Number 类型还提供了一些用于将数值格式化为字符串的方法,都有有自动舍入的特性:

  • toFixed()方法会按照指定的小数位返回数值的字符串表示:
var num = 10; 
alert(num.toFixed(2));     //"10.00" 
  • toExponential()方法返回以指数表示法(也称 e 表示法)表示的数值的字符串形式。也接收一个参数:
var num = 10; 
alert(num.toExponential(1)); //"1.0e+1" 
  • toPrecision()方法会返回某个数值的最合适的格式,这个方法接收一个参数,即表示数值的所有数字的位数(不包括指数部分)。

5.6.3 String类型

继承的 valueOf()、toLocaleString()和 toString()方法,都返回对象所表示的基本字符串值。 String 类型的每个实例都有一个 length 属性,表示字符串中包含多个字符。 String 类型提供了很多方法,用于辅助完成对 ECMAScript 中字符串的解析和操作。

  • 字符方法 两个用于访问字符串中特定字符的方法是:charAt()和 charCodeAt():
var stringValue = "hello world"; 
alert(stringValue.charAt(1));   //"e" 
alert(stringValue.charCodeAt(1));   //输出"101" 
alert(stringValue[1]);   //"e" 
  • 字符串操作方法 concat(),用于将一或多个字符串拼接起来,返回拼接得到的新字符串:
var stringValue = "hello "; 
var result = stringValue.concat("world"); 
alert(result); //"hello world" 

但实践中使用更多的还是加号操作符(+)。 slice()、substr()和 substring()都会返回被操作字符串的一个子字符串,而且也都接受一或两个参数。

var stringValue = "hello world"; 
alert(stringValue.slice(3)); //"lo world" 
alert(stringValue.substring(3)); //"lo world" 
alert(stringValue.substr(3)); //"lo world" 
alert(stringValue.slice(3, 7)); //"lo w" 
alert(stringValue.substring(3,7)); //"lo w" 
alert(stringValue.substr(3, 7)); //"lo worl" 

传递负值时行为各不相同,这里不介绍了。(感觉用不到)

  • 字符串位置方法 indexOf()和 lastIndexOf()这两个方法都是从一个字符串中搜索给定的子字符串,然后返子字符串的位置(如果没有找到该子字符串,则返回-1)。两者区别就是一前一后的区别。 这两个方法都可以接收可选的第二个参数,表示从字符串中的哪个位置开始搜索。

  • trim()方法 ECMAScript 5 为所有字符串定义了 trim()方法。这个方法会创建一个字符串的副本,删除前置及后缀的所有空格,然后返回结果。

var stringValue = "   hello world   "; 
var trimmedStringValue = stringValue.trim(); 
alert(stringValue);            //"   hello world   " 
alert(trimmedStringValue);     //"hello world" 
  • 字符串大小写转换方法 toLowerCase()和 toUpperCase()

  • 字符串的模式匹配方法 **match()**只接受一个参数,要么是一个正则表达式,要么是一个 RegExp 对象。本质上与调用 RegExp 的 exec()方法相同。

var text = "cat, bat, sat, fat";  
var pattern = /.at/; 
 
//与 pattern.exec(text)相同 
var matches = text.match(pattern);         
alert(matches.index); //0 
alert(matches[0]); //"cat" 
alert(pattern.lastIndex); //0 

另一个用于查找模式的方法是search()。这个方法的唯一参数与 match()方法的参数相同:由字符串或 RegExp 对象指定的一个正则表达式。search()方法返回字符串中第一个匹配项的索引;如果没有找到匹配项,则返回-1。而且,search()方法始终是从字符串开头向后查找模式。

var text = "cat, bat, sat, fat";  
var pos = text.search(/at/); 
alert(pos);   //1

替换子字符串的操作,ECMAScript 提供了 **replace()**方法。

var text = "cat, bat, sat, fat";  
var result = text.replace("at", "ond"); 
alert(result);    //"cond, bat, sat, fat" 
 
result = text.replace(/at/g, "ond"); 
alert(result);    //"cond, bond, sond, fond"

最后一个与模式匹配有关的方法是 split(),这个方法可以基于指定的分隔符将一个字符串分割成多个子字符串,并将结果放在一个数组中。split()方法可以接受可选的第二个参数,用于指定数组的大小:

var colorText = "red,blue,green,yellow"; 
var colors1 = colorText.split(",");          //["red", "blue", "green", "yellow"] 
var colors2 = colorText.split(",", 2);       //["red", "blue"] 
var colors3 = colorText.split(/[^\,]+/);     //["", ",", ",", ",", ""] 
  • localeCompare()方法 localeCompare(),这个方法比较两个字符串,并返回下列值中的一个:
    • 如果字符串在字母表中应该排在字符串参数之前,则返回一个负数(大多数情况下是-1,具体的值要视实现而定);
    • 如果字符串等于字符串参数,则返回 0;
    • 如果字符串在字母表中应该排在字符串参数之后,则返回一个正数(大多数情况下是 1,具体的值同样要视实现而定)。
var stringValue = "yellow";        
alert(stringValue.localeCompare("brick"));      //1 
alert(stringValue.localeCompare("yellow"));      //0 
alert(stringValue.localeCompare("zoo"));         //-1
  • fromCharCode()方法 fromCharCode()。这个方法的任务是接收一或多个字符编码,然后将它们转换成一个字符串。与实例方法 charCodeAt()相反:
alert(String.fromCharCode(104, 101, 108, 108, 111)); //"hello" 
  • HTML 方法 早期方法,尽量不使用。

5.7 单体内置对象

ECMA-262 对内置对象的定义是:“由 ECMAScript 实现提供的、不依赖于宿主环境的对象,这些对象在 ECMAScript 程序执行之前就已经存在了。” 除了前面所介绍的Object、Array 和 String,还定义了两个单体内置对象:Global 和 Math。

Global对象

在某种意义上是作为一个终极的“兜底儿对象”:不属于任何其他对象的属性和方法,最终都是它的属性和方法。所有在全局作用域中定义的属性和函数,都是 Global 对象的属性。

  • URI 编码方法 Global 对象的 **encodeURI()**和 encodeURIComponent()方法可以对 URI(Uniform Resource Identifiers,通用资源标识符)进行编码,以便发送给浏览器。有效的 URI 中不能包含某些字符,所以通过编码之后浏览器就能识别了。 两者编码的区别
var uri = "http://www.wrox.com/illegal value.htm#start"; 
 
//"http://www.wrox.com/illegal%20value.htm#start" 
alert(encodeURI(uri)); 
 
//"http%3A%2F%2Fwww.wrox.com%2Fillegal%20value.htm%23start" 
alert(encodeURIComponent(uri));

使用 encodeURI()编码后的结果是除了空格之外的其他字符都原封不动,只有空格被替换成了%20。而 encodeURIComponent()方法则会使用对应的编码替换所有非字母数字字符。这也正是可以对整个 URI 使用 encodeURI(),而只能对附加在现有 URI 后面的字符串使用encodeURIComponent()的原因所在。 与 encodeURI()和 encodeURIComponent()方法对应的两个方法分别是decodeURI()和decodeURIComponent()。不能混用。

  • eval()方法 eval()方法就像是一个完整的 ECMAScript 解析器,它只接受一个参数,即要执行的 ECMAScript(或 JavaScript)字符串。通过 eval()执行的代码被认为是包含该次调用的执行环境的一部分,意味着通过 eval()执行的代码可以引用在包含环境中定义的变量:
var msg = "hello world"; 
eval("alert(msg)");    //"hello world"

在 eval()中创建的任何变量或函数都不会被提升。严格模式下,在外部访问不到 eval()中创建的任何变量或函数。

能够解释代码字符串的能力非常强大,但也非常危险。防范代码注入

  • Global 对象的属性 略。

  • window 对象 ECMAScript 虽然没有指出如何直接访问 Global 对象,但 Web 浏览器都是将这个全局对象作为window 对象的一部分加以实现的。于是全局变量或函数都是window对象的属性。

var color = "red"; 
 
function sayColor(){ 
    alert(window.color); 
} 
 
window.sayColor();  //"red" 

5.7.2 Math对象

  • Math 对象的属性
属性 说明
Math.E 自然对数的底数,即常量e的值
Math.LN10 10的自然对数
Math.LN2 2的自然对数
Math.LOG2E 以2为底e的对数
Math.LOG10E 以10为底e的对数
Math.PI π的值
Math.SQRT1_2 1/2的平方根(即2的平方根的倒数)
Math.SQRT2 2的平方根
  • min()和 max()方法 找最大最小值,两个方法都可以接收任意多个数值参数。这两个方法经常用于避免多余的循环和在 if 语句中确定一组数的最大值。 要找到数组中的最大或最小值,可这样使用 apply()方法:
var values = [1, 2, 3, 4, 5, 6, 7, 8]; 
var max = Math.max.apply(Math, values); 
  • 舍入方法 小数值舍入为整数的几个方法:Math.ceil()、Math.floor()和 Math.round()。 遵循下列舍入规则:

  • Math.ceil()执行向上舍入,即它总是将数值向上舍入为最接近的整数;

  • Math.floor()执行向下舍入,即它总是将数值向下舍入为最接近的整数;

  • Math.round()执行标准舍入,即它总是将数值四舍五入为最接近的整数(这也是我们在数学课上学到的舍入规则)。

  • random()方法 Math.random()方法返回大于等于 0 小于 1 的一个随机数。 利用 Math.random()从某个整数范围内随机选择一个值:

 =  Math.floor(Math.random() * 可能值的总数 + 第一个可能的值) 
// 如2 - 10
var  num = Math.floor(Math.random() * 9 + 1); 
  • 其他方法
方法 说明 方法 说明
Math.abs(num) 返回num的绝对值 Math.asin(x) 返回x的反正弦值
Math.exp(num) 返回Math.E的num次幂 Math.atan(x) 返回x的反正切值
Math.log(num) 返回num的自然对数 Math.atan2(y,x) 返回y/x的反正切值
Math.pow(num,power) 返回num的power次幂 Math.cos(x) 返回x的余弦值
Math.sqrt(num) 返回num的平方根 Math.sin(x) 返回x的正弦值
Math.acos(x) 返回x的反余弦值 Math.tan(x) 返回x的正切值

5.8 小结

对象在 JavaScript 中被称为引用类型的值,而且有一些内置的引用类型可以用来创建特定的对象。简要总结:

  • 引用类型与传统面向对象程序设计中的类相似,但实现不同;
  • Object 是一个基础类型,其他所有类型都从 Object 继承了基本的行为;
  • Array 类型是一组值的有序列表,同时还提供了操作和转换这些值的功能;
  • Date 类型提供了有关日期和时间的信息,包括当前日期和时间以及相关的计算功能;
  • RegExp 类型是 ECMAScript 支持正则表达式的一个接口,提供了最基本的和一些高级的正则表达式功能。

函数实际上是 Function 类型的实例,因此函数也是对象,所以函数也拥有方法。 三种基本包装类型分别是:Boolean、Number 和 String,所以 JavaScript 中的基本类型值可以被当作对象来访问。共同的特征:

  • 每个包装类型都映射到同名的基本类型;
  • 在读取模式下访问基本类型值时,就会创建对应的基本包装类型的一个对象,从而方便了数据 操作;
  • 操作基本类型值的语句一经执行完毕,就会立即销毁新创建的包装对象。

Web 浏览器实现了承担Global的 window 对象。 Math 对象提供了很多属性和方法,用于辅助完成复杂的数学计算任务。