JavaScript高级程序设计(读书笔记)

Posted tribody

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript高级程序设计(读书笔记)相关的知识,希望对你有一定的参考价值。

本笔记汇总了作者认为“javascript高级程序设计”这本书的前七章知识重点,仅供参考。

第五章 引用类型

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

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

函数实际上是Function类型的实例,因此函数也是对象;而这一点正是JavaScript最有特色的地方。由于函数是对象,所以函数也拥有方法,可以用来增强其行为。
因为有了基本包装类型,所以JavaScript中的基本类型值可以被当作对象来访问。三种基本包装类型分别是:Boolean、Number和String。以下是他们共同的特征:

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

在所有代码执行之前,作用域中就已经存在两个内置对象:Global和Math。在大多数ECMAScript实现中都不能直接访问Global对象;不过,Web浏览器实现了承担该角色的window对象。全局变量和函数都是Global对象的属性。Math对象提供了很多属性和方法,用于辅助完成复杂的数学计算任务。

ECMAScript从技术上讲是一门面向对象语言,但它不具备传统的面向对象语言所支持的类和接口等基本结构。引用类型有时也被称为对象定义,因为它们描述的是一类对象所具有的属性和方法。对象是某个特定引用类型的实例


Object类型

大多数引用类型值都是Object类型的实例,Object是ECMAScript中使用最多的一个类型。创建Object实例的方式有两种。第一种是使用new操作符后跟Object构造函数,如

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

另一种是使用对象字面量表示法,如

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

一般来说,访问对象属性时使用的都是点表示法,JavaScript中也可以使用方括号表示法来访问对象的属性。如

alert(person["name"]);              //"Nicholas"
alert(person.name);                 //"Nicholas"

Array类型

ECMAScript中的数组与其他多数语言中的数组有着很大的区别。虽然ECMAScript数组与其他语言中的数组都是数据的有序列表,但与其他语言不同的是,ECMAScript数组的每一项可以保存任何类型的数据。

表示方法
创建数组的基本方式有两种。第一种是使用Array构造函数,如果预先知道数组要保存的项目数量,也可以给构造函数传递该数量,而该数量会自动变成length属性的值;给构造函数传递值也可以创建数组。如

var colors = new Array();//空数组
var colors = new Array(20);//创建一个包含3项的数组
var colors = new Array("red", "blue", "green");//创建一个包含三个字符串值的数组
var names = Array("Greg");//创建一个包含一个字符串值的数组

第二种是使用数组字面量表示法。如

var colors = ["red", "blue", "green"];//创建一个包含3个字符串的数组
var names = [];//创建一个空数组
var values = [1,2,];//禁忌,会创建一个包含2或3项的数组
var options = [, , , , ,];//禁忌,会创建一个包含5或6项的数组

length属性
数组length的属性不是只读的,通过设置这个属性,可以从数组的末尾移除项或向数组中添加新项。

检测数组
自从ECMAScript3作出规定后,就出现了确定某个对象是不是数组的经典问题。对于一个网页,或者一个全局作用域而言,使用instanceof操作符就能得到满意结果。

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

如果网页中包含多个框架,那实际上就存在两个以上不同的全局执行环境,从而存在两个以上不同版本的Array构造函数。如果从一个框架向另一个框架传入一个数组,那么传入的数组与第二个框架中原生创建的数组分别具有各自不同的构造函数。为了解决这个问题,ECMAScript5新增了Array.isArray()方法。

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

转换方法

toLocaleString() :
toString() :
valueOf() :

栈方法和队列方法

ECMAScript提供了一种让数组的行为类似于其他数据结构的方法。

实现栈的方式(LIFO)

  • push(): 栈中项插入
  • pop():栈中项移除

实现队列的方法(FIFO)

  • push():向数组末端添加项
  • shift():从数组前段取得项

重排序方法

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

reverse()方法会反转数组项的顺序。

默认情况下,sort()方法按升序排列数组项。为了实现排序,sort()方法会调用每个数组项的toString()转型方法,然后比较得到的字符串,以确定如何排序。
另外,sort()方法可以接收一个比较函数作为参数。比较函数接受两个参数,如果第一个参数应该位于第二个之前则返回一个负数,如果两个参数相等则返回0,如果第一个参数应该位于第二个之后则返回一个正数。实际操作可以根据需要自行定义。如最简单的一个例子

function compare(value1, value2)    {
    if (value1 < value2) {
        return -1;
    } else if (value1 > value2) {
        return 1;
    } else {
        return 0;
    }
}

只需将其作为参数传递给sort()方法即可。

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;
}

操作方法

ECMAScript为操作已经包含在数组中的项提供了很多内置方法。

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

splice()
该方法非常强大,用法很多。它的主要用途是向数组的中部插入项,使用方式有3中:

  • 删除:可以删除任意数量的项, 只需指定两个参数:要删除的第一项的位置和要删除的项数。例如,splice(0, 2)会删除数组中的前两项。
  • 插入:可以向指定位置插入任意数量的项,只需提供三个参数:起始位置、0(要删除的项数)和要插入的项。如果要插入多个项,可以再传入第四、第五,甚至任意多个项。例如splice(2, 0, "red", "green")会从当前数组的位置2开始插入字符串”red”和”green”。
  • 替换:可以向指定位置插入任意数量的项,且同时删除任意数量的项,只需提供三个参数:起始位置、要删除的项数和要插入的项。如果要插入多个项,可以再传入第四、第五,甚至任意多个项。例如splice(2, 1, "red", "green")会删除当前数组位置2的项,然后再从当前数组的位置2开始插入字符串”red”和”green”。

splice()方法始终都会返回一个数组,该数组中包含从原始数组中删除的项(如果没有删除任何项,则返回一个空数组)。下面的代码展示了上述3中用法:

var colors = ["red", "green", "blue"];
var removed = colors.splice(0, 1);      //删除第一项
alert(colors);      //green, blue
alert(removed); //red, 返回的数组中只包含一项

removed = colors.splice(1, 0, "yellow", "orange");      //从位置1开始插入两项
alert(colors);      //green, yellow, orange, blue
alert(removed); //返回的是一个空数组

removed = colors.splice(1, 1, "red", "purple");     //插入两项,删除一项
alert(colors);      //green, red, purple, orange, blue
alert(removed); //yellow, 返回的数组中只包含一项

位置方法

ECMAScript5为数组实例添加了两个位置方法:indexOf()lastIndexOf()。这两个方法都接受两个参数:要查找的项和(可选的)表示查找起点位置的索引。不同的是,indexOf()方法从数组的开头向后查找,laseIndexOf()方法则从数组的末尾开始向前查找。
这两个方法都返回要查找的项在数组中的位置,没找到返回-1.在比较第一个参数与数组中的每一项时,会使用全等操作符;也就是说,要查找的项必须严格相等。

var person = {  name: "Nicholas" };
var people = [{ name: "Nicholas" }];

var morePeople = [person];

alert(people.indexOf(person));          //-1
alert(morePeople.indexOf(person));  //0

迭代方法

ECMAScript5为数组定义了5个迭代方法。每个方法都接受两个参数:要在每一项上运行的函数和(可选的)运行该函数的作用域——影响this的值。传入这些方法中的函数会接收三个参数:数组项的值、该想在数组中的位置和数组对象本身。根据使用的方法不同,这个函数执行后的返回值可能会也可能不会影响方法的返回值。以下是这5个迭代方法的作用。

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

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

其中,every()filter()方法最相似。

var numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];

var everyResult = numbers.every(function(item, index, array) {
    return (item > 2);
});

alert(everyResult);     //false

var someResult = numbers.some(function(item, index, array) {
    return (item > 2);
});

alert(someResult);      //true

filter()简称为滤波器,作用也有点类似滤波器。可以用来过滤出符合条件项。

var numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];

var filterResult = numbers.filter(function(item, index, array) {
    return (item > 2);
});

alert(filterResult);            //[3, 4, 5, 4, 3]

map()可以用来创建包含的项与另一个数组一一对应的项。

var numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];

var mapResult = numbers.filter(function(item, index, array) {
    return item * 2;
});

alert(mapResult);           //[2, 4, 6, 8, 10, 8, 6, 4, 2]

forEach()本质上和使用for循环迭代数组一样。

var numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];

numbers.forEach(function(item, index, array) {
    //执行某些操作
});

归并方法

ECMAScript5还新增了两个归并数组的方法:reduce()reduceRight()。这两个方法都会迭代数组的所有项,然后构建一个最终返回的值。reduce()从前到后,reduceRight()从后到前。
这两个方法都接收两个参数:一个在每一项上调用的函数和(可选的)作为归并基础的初始值。传给reduce()reduceRight()的函数接收4个参数:前一个值、当前值、项的索引和数组对象。这个函数返回的任何值都会作为第一个参数自动传给下一项。第一次迭代发生在数组的第二项上,因此第一个参数是数组的第一项,第二个参数就是数组的第二项。
使用reduce()方法可以执行数组中所有值求和操作。

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

Date类型

ECMAScript中的Date类型是在早期Java中的java.util.Date类基础上构建的。为此,Date类型使用自UTC(Coordinated Universal Time,国际协调时间)1970年1月1日午夜(零时)开始经过的毫秒数来保存日期。

创建日期对象,使用new操作符和Date构造函数即可。

var now = new Date()

无参数传递时自动获取系统当前日期和时间。如果想根据特定日期时间创建日期对象,传入该日期毫秒数。为简化计算过程,ECMPScript提供两个方法:Date.parse()Date.UTC()
其中, Date.parse()接收一个表示日期的字符串。ECMA-262没有定义 Date.parse()应该支持哪种日期格式,因此这个方法的行为因实现而异,而且通过时因地区而异。将地区设置为美国的浏览器通常都接受下列日期格式:

  • “月/日/年“,如6/13/2004;
  • “英文月名 日,年”,如January 12,2004;
  • “英文星期几 英文月名 日 年 时:分:秒 时区”,如Tue May 25 2004 00:00:00 GMT-0700。
  • ISO 8601 扩展格式 YYYY-MM-DDTHH:mm:ss.sssZ(例如 2004-05-25T00:00:00)。只有兼容ECMAScript5的实现支持这种格式。

例如:

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

如果传入Date.parse()方法的字符串不能表示日期,那么它会返回NaN。实际上,如果直接讲字符串传递给Date构造函数,也会在后台调用Date.parse()

Date.UTC()方法同样也返回表示日期的毫秒数。 Date.UTC()的参数分别是年份、基于0的月份(0-11)、月中的哪一天、小时数(0-23)、分钟、秒以及毫秒数。前两个参数是必须的,其他默认为0。

//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));

与其他引用类型一样,日期类型继承了toLocaleString()toString()valueOf()方法。Date类型的toLocaleString()方法会按照与浏览器设置的地区相适应的格式返回日期和时间。valueOf()不返回字符串,而是返回日期的毫秒表示。

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

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

其他相关日期/时间组件方法,可参考w3cschool的JavaScript Date对象

RegExp对象

ECMAScript通过RegExp类型来支持正则表达式。

var expression = / pattern / flags ;

其中的模式(pattern)部分可以是任何简单或复杂的正则表达式,可以包含字符类、限定符、分组、向前查找以及反向引用。每个正则表达式都可带有一或多个标志(flags),用以表明正则表达式的行为。正则表达式匹配模式支持3个标志。

  • g:表示全局模式(global),即模式将被用于所有字符串,而非在发现第一个匹配项是立即停止;
  • i:表示不区分大小写(case-insensitive)模式,即在确定匹配项是忽略模式与字符串的大小写;
  • m:表示多行(multiline)模式,即在到达一行文本末尾时还会继续查找下一行中是否存在与模式匹配的项。

与其他语言中的正则表达式类似,模式中使用的所有元字符都必须转义。
关于正则表达式的基本介绍,参考菜鸟教程上的正则表达式教程
另外,关于RegExp对象的介绍,可以参考w3cschool上的JavaScript RegExp对象。在这里,就不赘述了。无论哪一门语言,在对字符串的处理上,正则表达式都是一个强大的工具,一定需要掌握。

Function类型

ECMAScript中最特殊的当属函数了,因为,函数实际上是对象!每个函数都是Function类型的实例,而且和其他引用类型一样具有属性和方法。由于函数是对象,因此函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定。

函数声明与函数表达式

解析器在向执行环境中加载数据时,对函数声明和函数表达式并非一视同仁。解析器会率先读取函数声明,并使其执行代码之前可用(可以访问);至于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解析执行。因此,除了什么时候可以通过变量访问函数这一点区别外,函数声明与函数表达式的语法其实是等价的。

//使用函数声明
alert(sum(10, 10));
function sum(num1, num2) {
    return num1 + num2;
}

上例能够正常运行,因为在代码执行前,解析器就已经通过一个名为函数声明提升(function declaration hoisting)的过程,读取并将函数声明添加到执行环境中。对代码求值时,JavaScript引擎在第一遍会声明函数并将他们放到源代码树的顶部。所以,即使声明函数的代码在调用它的带码后面,JavaScript引擎也能把函数声明提升到顶部。而下例函数表达式则不能运行

//使用函数表达式
alert(sum(10, 10));
var sum = function(num1, num2) {
    return num1 + num2;
}

没有重载!
由于函数是对象,函数名实际上是一个指向函数对象的指针,不会与某个函数绑定,这正是ECMAScript中没有函数重载概念的原因。个人理解,除此之外,由于ECMAScript中的变量为松散类型,因此对于传入函数的参数类型无法加以限制,因此无法像C++或者Java那样根据传入参数类型或者数量选择调用函数,这也是造成ECMAScript无法重载的原因之一。

//函数声明
function addSomeNumber(num) {
    return num + 100;
}

function addSomeNumber(num) {
    return num + 200;
}
var result = addSomeNumber(100);    //300

该例声明两个同名函数,结果是后面的函数覆盖了前面的。该例与下面的代码几乎没有区别:

var addSomeNumber = function (num) {
    return num + 100;
};

addSomeNumber = function (num) {
    return num + 200;
};

var result = addSomeNumber(100);        //300

作为返回值的函数
由于ECMAScript中的函数名本身就是变量,因此函数也可以作为值来使用。即可以作为参数或者返回值。

//函数作为参数
function callSomeFunction (someFunction, someArgument) {
    return somFunction (someArgument);
}

function add10 (num) {
    return num + 10;
}

var result1 = callSomeFunction (add10, 10);
alert(result1);     //20

function getGreeting (name) {
    return "Hello, " + name;
}

var result2 = callSomeFunction (getGreeting, "Nicholas");
alert(result2);     //"Hello, Nicholas"

函数作为返回值是极有用的技术,是“闭包”技术的基础之一。
比较典型的如数组sort()方法的比较函数,它需要接收两个参数,比较它们的值。可以使假设有一个对象数组,要根据某个对象属性排序,怎么办呢?

function createComparisonFunction (propertyName) {
    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

函数内部属性(重点)
在函数内部,有两个特殊的对象:argumentsthis

arguments的主要用途是保存函数参数,这个对象还有一个名叫callee的属性,该属性是一个指针,指向拥有这个arguments对象的函数。它可以完成函数体与函数名的解耦,如下面这个阶乘函数的应用:

//与函数名紧紧耦合
function factorial (num) {
    if (num <= 1) {
        return 1;
    } else {
        return num * factorial (num - 1)
    }
}

//使用arguments.callee替代函数名,消除耦合
function factorial (num) {
    if (num <= 1) {
        return 1;
    } else {
        return num * arguments.callee (num - 1)
    }
}

这样,无论引用函数时使用的是什么名字,都可以保证正常完成递归调用。如

var trueFactorial = factorial;

factorial = function () {
    return 0;
};
alert(trueFactiorial(5));           //120
alert(factorial(5));                    //0

函数内部另一个特殊对象是this,其行为与Java和C#中的this大致类似。即,this引用的是函数据以执行的环境对象——或者也可以说是this值(当在网页的全局作用域中调用函数时,this对象引用的就是window)。如下例

window.color = "red";
var o = { color: "blue"};

function sayColor() {
    alert(this.color);
}

sayColor();             //"red"

o.sayColor = sayColor;
o.sayColor();           //"blue"

sayColor()在全局域中定义,当在全局域中调用时,this引用的是全局对象window;当把这个函数赋给对象o并调用o.sayColor()是,this引用的是对象o

ECMAScript 5也规范化了另一个函数对象的属性:caller。这个属性保存着调用当前函数的函数的引用,如果是在全局域中调用当前函数,它的值为null。使用方式类似于callee,在此不赘述。

函数属性和方法(重点)
ECMAScript中函数是对象,因此也有属性和方法。

每个函数都包含两个属性:lengthprototype。其中,length表示函数希望接收的命名参数的个数;对于ECMAScript中的引用类型来说,prototype是保存他们所有实例方法的真正所在。
诸如toString()valueOf()等方法实际上都保存在prototype名下,只不过是通过各自对象的实例访问它们罢了。在创建自定义引用类型以及实现继承时,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

call()方法与apply()方法的作用相同,他们的区别仅在于接收参数的方式不同。对于call()方法而言,第一个参数是this值没有变化,变化的是其余参数都直接传递给函数。即,在使用call()方法时,传递给函数的参数必须逐个列举出来,如下:

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

alert(callSum(10, 10));     //20

事实上,传递参数并非apply()call()真正用武之地,它们真正强大的地方是能够扩充函数赖以运行的作用域。例如

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

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

window.color = "red";
var o = { color: "blue" };

function sayColor() {
    alert(this.color);
}
var objectSayColor = sayColor.bind(o);
objectSayColor();           //blue

基本包装类型

和Java类似,为了便于操作基本类型值,ECMAScript也提供了3个特殊的引用类型:BooleanNumberString。每当读取一个基本类型值的时候,后台就会创建一个对应的基本包装类型的对象,从而让我们能够调用一些方法来操作这些数据。有点类似于Java的自动拆装箱过程,以String类型为例

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

在访问s1时,访问过程处于读取模式,后台自动完成下列处理:

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

以上三个步骤可以想象成下列代码

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

以上三个步骤同样适用于BooleanNumber类型对应的布尔值和数字值。
引用类型与基本包装类型主要区别就是对象的生存期。使用new操作符创建的引用类型的实例,在执行流离开当前引用域之前一直都保存在内存中。而自动创建的基本包装类型的对象,则只存在一行代码的执行瞬间,然后立即销毁。这意味着不能在运行时为基本类型值添加属性和方法。

Boolean类型

Boolean类型是与布尔值对应的引用类型,建议永远不要使用Boolean对象。

Number类型

除了继承的方法外,Number类型还提供了一些用于将数值格式化为字符串的方。
- toFixed():按指定小数位返回数值的字符串表示。
- toExponential():返回以指数表示法表示的数值的字符串形式。
- toPrecision():返回固定大小格式,也可能返回指数格式,具体规则是看哪种格式比较合适。该方法接收一个参数指定表示数值的所有数字的位数。

String类型

String类型提供了很多方法,用于辅助完成对ECMAScript中字符串的解析和操作。

字符方法
两个用于访问字符串中特定字符的方法时:charAt()charCodeAt()。两个方法都接收一个参数,即基于0饿字符位置。charAt()返回指定位置字符,charCodeAt()返回指定位置字符编码。

字符串操作方法
concat()
用于将一或多个字符串拼接起来,接受任意多个参数。

slice()substr()substring()
这三个方法基于字符串创建新字符串。它们都会返回被操作字符串的一个子字符串,而且也都接受一或两个参数。第一个参数指定子字符串的开始位置,第二个参数(在指定的情况下)表示子字符串到哪里结束。具体来说,slice()substr()的第二个参数指定的是子字符串最后一个字符后面的位置。而substr()的第二个参数指定的则是返回的字符数。如果没有给这些方法传递第二个参数,则将字符串的长度作为结束位置。

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

当传递的参数是负值时,它们的行为就不尽相同了。其中,slice()方法会将传入的赋值与字符串长度相加,substr()方法将负的第一个参数加上字符串的长度,而将负的第二个参数转换为0.最后,substring()方法会把所有负值参数都转换为0.

var StringObject = "hello world";
alert(StringObject .slice(-3));             //输出 "rld"
alert(StringObject .substring(-3));         //输出 "hello world"
alert(StringObject .substr(-3));            //输出 "rld"
alert(StringObject .slice(3, -4));          //输出 "lo w"
alert(StringObject .substring(3, -4));      //输出 "hel"
alert(StringObject .substr(3, -4));         //输出 "" 空字符串

字符串位置方法
有两个可以从字符串中查找子字符串的方法:indexOf()lastIndexOf()。前者从前往后搜索,后者反之。
两个方法都可接收可选的第二个参数,表明从字符串哪个位置开始搜索。
在使用第二个参数的情况下,可以通过循环调用indexOf()lastIndexOf()来找到所有匹配的子字符串。如下:

var stringValue = "Lorem ipsum dolor sit amet, consectetur adipisicing elit";
var positions = new Array();
var pos = stringValue.indexOf("e");

while (pos > -1) {
    positions.push(pos);
    pos = stringValue.indexOf("e", pos + 1);
}

alert(positions);       //"3, 24, 32, 35, 52"

trim()方法
该方法创建一个字符串的副本,删除前置及后缀的所有空格,然后返回结果。

字符串大小写转换方法
ECMAScript中涉及字符串大小写转换的方法有4个:toLowerCase()toLocaleLowerCase()toUpperCase()toLocaleUpperCase()

字符串的模式匹配方法

match()
在字符串上调用这个方法,本质上与调用RegExpexec()方法相同。match()只接受一个参数,正则表达式或者RegExp对象。和RegExp对象的exec()方法一样,match()方法会返回一个数组:数组的第一项是与整个模式匹配的字符串,之后的每一项(如果有)保存着与正则表达式中的捕获组匹配的字符串。

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()方法相同,返回字符串中第一个匹配项的索引,如果没有返回-1。

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



replace()
接受两个参数,第一个参数可以是一个RegExp对象或者一个字符串(这个字符串不会被转换为正则表达式),第二个参数可以是一个字符串或者一个函数。如果第一个参数是字符串,那么只会替换第一个字符串,想替换所有字符串只能提供一个正则表达式,并且要指定全局(g)标志。

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

如果第二个参数是字符串,那么还可以使用一些特殊的字符序列,将正则表达式操作得到的值插入到结果字符串中。下表列出了ECMAScript提供的这些特殊的字符序列。

字符序列 替换文本
$$ $
$& 匹配整个模式的子字符串。与RegExp.lastMatch的值相同
$’ 匹配的子字符串之前的子字符串。与RegExp.leftContext的值相同
$` 匹配的子字符串之前的子字符串。与RegExp.rightContext的值相同
$n 匹配第n个捕获组的子字符组,其中nn等于0~9。例如,$1是匹配第一个捕获组的子字符串,$2是匹配第二个捕获组的子字符串,以此类推。如果正则表达式中没有定义捕获组,则使用空字符串
$nn 匹配第nn个捕获组的子字符串,其中nn等于01~99。例如$01是匹配第一个捕获组的子字符串,$02是匹配第二个捕获组的子字符串,以此类推。如果正则表达式中没有定义捕获组,则使用空字符串

通过这些特殊的字符序列,可以使用最近一次匹配结果中的内容,如下

var text = "cat, bat, sat, fat";
result = text.replace(/(.at)/g, "word ($1)");
alert(result);          //word (cat), word (bat), word(sat), word (fat)

replace()方法的第二个参数也可以是一个函数。在只有一个匹配项(即与模式匹配的字符串)的情况下,会向这个函数传递3个参数:模式的匹配项、模式匹配项在字符串中的位置和原始字符串。在正则表达式定义了多个捕获组的情况下,传递给函数的参数一次是模式的匹配项、第一个捕获组的匹配项、第二个捕获组的匹配项……,但最后两个参数仍然分别是模式的匹配项在字符串中的位置和原始字符串。这个函数应该返回一个字符串,表示应该被替换的匹配项使用函数作为replace()方法的第二个参数可以实现更加精细的替换操作,如下例

\\返回html实体
function htmlEscape(text) {
    return text.replace(/[<>"&]/g, function(match, pos, originalText) {
        switch(match) {
            case "<":
                return "&lt;";
            case ">":
                return "&gt;";
            case "&":
                return "&amp;";
            case "\"":
                return "&quot;"
        }
    });
}

alert(htmlEscape("<p class=\"greeting\">Hello world!</p>"));
//&lt;p class=&quot;greeting&quot;&gt;Hello world!&lt;/p&gt;


split()
该方法可以基于指定的分隔符将一个字符串分割成多个子字符串,并将结果放在一个数组中。分隔符可以是字符串,也可以是一个RegExp对象(这个方法不会将字符串看成正则表达式)。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()方法
该方法比较两个字符串,并返回下列值中的一个:

  • 如果字符串在字母表中应该排在字符串参数之前,则返回一个负数(大多数情况下是-1,具体的值要视实现而定);
  • 如果字符串等于字符串参数,则返回0;
  • 如果字符串在字母表中应该排在字符串参数之后,则返回一个正数(大多数情况下是1,具体的值同样要使实现而定)。
var stringValue = "yellow";
alert(stringValue.localeCompare("brick"));          //1
alert(stringValue.localeCompare("yellow"));     //0
alert(stringValue.localeCompare("zoo"));            //-1

fromCharCode()方法
String构造函数本身还有一个静态方法:fromCharCode()。这个方法的任务是接收一或多个字符编码,然后将它们转换成一个字符串。从

以上是关于JavaScript高级程序设计(读书笔记)的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript高级程序设计(读书笔记)

JavaScript高级程序设计-读书笔记

《Javascript高级程序设计》读书笔记

JavaScript 高级程序设计读书笔记

《javascript高级程序设计》读书笔记javascript简单介绍

《JavaScript高级程序设计》读书笔记