红宝书《JavaScript高级程序设计》学习笔记(五)引用类型

目录

 

5.1 Object类型

5.2 Array类型

5.3 Date类型

5.4 RegExp类型

5.5 Function类型

5.6 基本包装类型

5.7 单体内置对象


5.1 Object类型

创建Object实例的方法有两种

(1)使用new操作符后跟Object构造函数

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

(2)对象字面量表示法

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

在使用字面量语法时,属性名也可以使用字符串。也可以使用数字,但数字会自动转化为字符串。

let person ={};//与new Object();相同

实际上,对象字面量也是向函数传递大量可选参数的首选方式。

对于对象的属性,可以用“对象.属性”的形式,也可以用“对象[属性]”的形式,二者从功能上没有任何区别,但方括号语法可以通过变量访问属性,或者属性名含有空格时,方括号表示法能完美胜任。通常,除非必须使用变量访问属性,否则建议使用点表示法

//变量
var propertyName = "name"; 
alert(person[propertyName]); //"Nicholas"


//变量含有空格
person["first name"] = "Nicholas";

5.2 Array类型

ECMAScript中的数组的大小是可以动态调整的,即可以随着数据的添加自动增长以容纳新增数据。

var colors = new Array();//Array构造函数
var colors = new Array(20);//定长
var colors = new Array("red", "blue", "green");//直接传入字符串

在使用Array构造函数时也可以省略new操作符。

创建数组的第二种基本方式是使用数组字面量表示法。数组字面量由一对包含数组项的方括号表示,多个数组项之间以逗号隔开。

var colors = ["red", "blue", "green"]; // 定义一个字符串数组
alert(colors[0]); // 显示第一项 
colors[2] = "black"; // 修改第三项
colors[3] = "brown";// 新增第四项

如果设置某个值的索引超过了数组现有项数,如这个例子中的 colors[3]所示,数组就会自动增加到该索引 值加 1 的长度(就这个例子而言,索引是 3,因此数组长度就是 4)。

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

var colors = ["red", "blue", "green"]; // 创建一个包含 3 个字符串的数组 
colors.length = 2;
alert(colors[2]); //undefined

 这个例子中的数组 colors 一开始有 3 个值。将其 length 属性设置为 2 会移除最后一项(位置为 2 的那一项),结果再访问 colors[2]就会显示 undefined 了。如果将其 length 属性设置为大于数组项数的值,则新增的每一项都会取得 undefined 值。

利用length属性可以很方便地在数组末尾添加新项。

var colors = ["red", "blue", "green"];// 创建一个包含 3 个字符串的数组
colors[colors.length] = "black"; //(在位置3)添加一种颜色
colors[colors.length] = "brown"; //(在位置4)再添加一种颜色

Array.isArray()用来确定某个对象是不是数组。

数组使用toString()会返回由数组每个值拼接而成的一个以逗号分割的字符串。valueOf()则为了创建这个字符串会调用数组每一项的toString()方法。虽然toLocaleString()也经常返回和前两个方法相同的值,但也不尽然。它调用的是每一项的toLocaleString()方法,而不是toString()方法。

var colors = ["red", "blue", "green"]; // 创建一个包含 3 个字符串的数组
alert(colors.toString()); // red,blue,green
alert(colors.valueOf()); // red,blue,green
alert(colors);// red,blue,green

如果使用 join()方法,则可以使用不同的分隔符来构建这个字符串。join()方法只接收一个参数,即用作分隔符的字符串,然后返回包含所有数组项的字符串。

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

push()方法接收任意参数逐个添加到数组末尾,并修改数组的长度。pop()方法则从数组末尾移除最后一项。用这两个方法可以模拟出栈和入栈

shift()方法移除数组第一项。利用shift()和push()可以像队列一样使用数组。

ECMAScript还为数组提供了unshift()方法在数组首部添加项,并返回新的长度。

 

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

sort()会按照升序排列数组项。sort()方法比较的是字符串,所以用sort()比较数字的时候要传入一个“比较函数”保证正确的升序。sort(比较函数);比较函数返回1表示“前值要放在后值后”,返回-1要表示“前值要放在后值之前”。

//比较函数
let compare=(value1,value2)=>{
    return value2-value1;
}

concat()基于当前数组的所有项创建一个新数组(延长数组)。

slice()基于当前数组的一或多个项创建新数组(截取数组)。可以接收一或两个参数,即要返回项的起始和结束位置。

 

splice()可以传入两个以上的参数,第一个是要删除的位置,第二个是要删除的项数,后面可以插入任意数量的参数,用于插入任意数量的项。通过这些参数可以实现删除、插入、替换。splice()方法始终返回一个数组。

 

查找起点位置的索引。indexOf()从开头向后查找,lastIndexOf()从末尾向前查找。

 

ECMAScript为数组定义了5种迭代方法:

  • every() 对数组中的每一项运行给定函数,如果函数对每一项都返回true,则返回true。

  • some() 对数组中的每一项运行给定函数,如果函数对任意一项返回true,则返回true。

  • filter() 对数组中的每一项运行给定函数,返回该函数会返回true的项组成的数组。

  • forEach() 对数组中的每一项运行给定函数,这个函数没有返回值。

  • map() 对数组中的每一项运行给定函数,返回每次函数调用结果组成的数组

var numbers = [1,2,3,4,5,4,3,2,1];
//item是每一项 index是下标
var filterResult = numbers.filter(function(item, index, array){
    return (item > 2);
 });
alert(filterResult);//[3,4,5,4,3]
var numbers = [1,2,3,4,5,4,3,2,1];
var mapResult = numbers.map(function(item, index, array){ 
    return item * 2;
});
alert(mapResult); //[2,4,6,8,10,8,6,4,2]

reduce()和reduceRight()会迭代数组的所有项,然后构建一个最终返回的值。

/**
 * 演示一个累加
 */
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类型

Data.parse()方法接收一个表示日期的字符串参数,然后尝试根据这个字符串返回相应日期的毫秒数。

Date.UTC()的参数分别是年份、基于0的月份(一月是 0,二月是 1,以此类推)、月中的哪一天 (1 到 31)、小时数(0 到 23)、分钟、秒以及毫秒数。

Date.now()返回表示调用这个方法时的日期和时间的毫秒数。

Date类型的valueOf()方法返回日期的毫秒表示,可以方便地使用比较操作符来比较日期值。

ECMAScript 推荐现在编写的代码一律使用 toUTCString()方法,它以特定于实现的格式完整的UTC日期。

5.4 RegExp类型

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

var expression = / pattern / flags ;

其中模式(pattern)可以是正则表达式,标志(flags)支持3个标志:

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

与其他语言中的正则表达式类似,模式中使用的所有元字符都必须转义。正则表达式中的元字符包括:

( [ { \ ^ $ | ) ? * + .]}

在前面价格\进行转义,如?变成\? 

 

每个RegExp实例都有如下属性:

  • global:布尔值,表示是否设置了 g 标志。
  • ignoreCase:布尔值,表示是否设置了 i 标志。
  • lastIndex:整数,表示开始搜索下一个匹配项的字符位置,从 0 算起。
  • multiline:布尔值,表示是否设置了 m 标志。
  • source:正则表达式的字符串表示,按照字面量形式而非传入构造函数中的字符串模式返回。

 

RegExp实例有如下方法:

exec()是主要专门为捕获组而设计的。

var text = "mom and dad and baby";
var pattern = /mom( and dad( and baby)?)?/gi;
var matches = pattern.exec(text);

alert(matches.index); //0

alert(matches.input); // "mom and dad and baby"
alert(matches[0]);  // "mom and dad and baby"
alert(matches[1]); // " and dad and baby"
alert(matches[2]);// " and baby"

exec()在不设置全局标志(g)的情况下,始终返回第一个匹配的信息。在设置全局标志的情况下,会继续查找新匹配项。

个人体会:小括号是为了匹配组,即在整体应用pattern后,在应用括号内的pattern。

test(),只接受一个字符串参数,匹配模式返回true,否则返回false,经常被用于if语句中。eg:邮箱验证等。

RegExp构造函数的属性,相当于java中的静态属性,包括:

除了上面几个属性之外,还有多达9个用于存储捕获组的构造函数属性。访问这些属性的语法是RegExp.$1RegExp.$2...RegExp.$9,分别用于存储第一、第二......第九个匹配的捕获组。在 调用 exec()或 test()方法时,这些属性会被自动填充。

5.5 Function类型

在ECMAScript中,每个函数都是Function的实例,函数名实际上是一个指向函数对象的指针。

使用不带圆括号的函数名是访问函数指针,而非调用函数。

ECMAScript中没有重载的概念。

解析器在向执行环 境中加载数据时,对函数声明和函数表达式并非一视同仁。解析器会率先读取函数声明,并使其在执行任何代码之前可用(可以访问);至于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解释执行。

从一个函数中返回另一个函数,这是极为有用的计数。之前我们针对sort()写过比较函数,如果传入的是对象,比较函数能传入根据哪个参数进行排序,那就更好了。例如:可以按照姓名排序,也可按照年龄排序。

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

在函数内部又两个特殊对象arguments和this。arguments主要用于保存函数参数,arguments还有一个名叫callee的属性,是一个指针,指向arguments对象的函数。应用场景:

//递归调用
function factorial(num){ 
     if (num <=1) {
            return 1;
     } else {
            return num * factorial(num-1)
     }
}

//升级版递归调用
function factorial(num){ 
     if (num <=1) {
            return 1;
     } else {
            return num * arguments.callee(num-1)
     }
}

第一代递归调用中的factorial这个函数名被“写死”进了函数体中,耦合性太高,而升级版的递归调用和老版本实现的功能一样,但不会受“factorial”这一串字符串的限制。不论引用的函数是什么名字,都可以保证正常完成递归调用。

this应用的是函数执行的环境对象。

还有个函数对象属性caller保存着调用当前函数的函数的引用,如果在全局作用域调用当前函数,它为null。

每个函数都含有两个属性:length属性表示函数接收都命名参数的个数。prototype保存着toString()和valueOf()等方法,不过是通过各自对象的实例来访问罢了。

每个函数都包含两个非继承而来的方法:apply()和call()。这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。第一个参数都是运行函数作用域,他俩的区别是第二个参数传参的形式不同,apply传入的是参数数组,call传入的是参数们。

但传递参数并非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

使用 call()(或 apply())来扩充作用域的最大好处,就是对象不需要与方法有任何耦合关系。

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

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

在这里,sayColor()调用 bind()并传入对象 o,创建了 objectSayColor()函数。object- SayColor()函数的 this 值等于 o,因此即使是在全局作用域中调用这个函数,也会看到"blue"。

5.6 基本包装类型

基本类型不是对象,从逻辑上讲,它不应该有方法(但它们的确有方法)。其实访问过程处于一种读取模式,后台都会自动完成下列问题:

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

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

对基本包装类型的实例调用“typeof”会返回“object”,而且所有基本包装类型的对象在转化为布尔类型值时都是true。

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

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

变量number中保存的是基本类型值25,而变量obj中保存的是Number的实例。

我们建议永远不用使用Boolean对象。

Number类型的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"

toFixed()方法会按照制定的小数位返回数值的字符串表示。(保留多少位小数)

toExponential()返回以指数表示法(也称e表示法)表示的数值的字符串形式:

var num = 10;
alert(num.toExponential(1)); //"1.0e+1"

不过像这种数值太小的数字,一般不使用toExponential()方法,为了更加“智能”地选择我们该用什么方法表示,我们可以使用toPrecision()方法。它会自动决定是调用toFixed()或toExponential()

对于String类型,charAt()方法以单字符字符串的形式返回给定位置的那个字符(ECMAScript中没有字符类型)。

如果你想返回的不是字符而是字符编码,需要使用charCodeAt()。

ECMAScript还定义了另一个访问个别字符的方法,可以使用方括号加数字索引来访问某个字符:

var stringValue = "hello world";
alert(stringValue[1]);   //"e"

ECMAScript还提供了三个基于字符串创建新字符串的方法:slice()、substr()、sunstring()。都是为了切割字串而存在。

indexOf()和lastIndexOf()是从字符串中查找自字符串的方法。

trim()方法会删除前置和后缀的所有空格,然后返回结果。

与大小写转换有关的方法:toLowerCase()、toLocaleLowerCase()、toUpperCase()和 toLocaleUpperCase()。其中带Locale的方法可以根据地区的运行环境保证正确的转换,所以更稳妥一些。

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

另一个用于查找模式的方法是search(),与match()方法参数相同,返回第一个匹配项的索引。

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

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

与操作字符串有关的最后一个方法是 localeCompare(),这个方法比较两个字符串。

另外,String 构造函数本身还有一个静态方法:fromCharCode()。这个方法的任务是接收一或 多个字符编码,然后将它们转换成一个字符串。从本质上来看,这个方法与实例方法 charCodeAt() 执行的是相反的操作。

5.7 单体内置对象

ECMA-262还定义了两个单体对象Global和Math。

ECMAScript 中的 Global 对象在某种意义上是作为一个终极的“兜底儿对象” 来定义的。换句话说,不属于任何其他对象的属性和方法,最终都是它的属性和方法。事实上,没有全 局变量或全局函数;所有在全局作用域中定义的属性和函数,都是 Global 对象的属性。前面介绍 过的那些函数,诸如 isNaN()、isFinite()、parseInt()以及 parseFloat(),实际上全都是 Global 对象的方法。除此之外,Global 对象还包含其他一些方法。

Global 对象的 encodeURI()和 encodeURIComponent()方法可以对 URI进行编码,以便发送给浏览器。

encodeURI()主要用于整个 URI(例如,http://www.wrox.com/illegal value.htm),而 encode- URIComponent()主要用于对 URI 中的某一段(例如前面 URI 中的 illegal value.htm)进行编码。

与 encodeURI()和 encodeURIComponent()方法对应的两个方法分别是 decodeURI()和 decodeURIComponent()。

eval() 方法就像是一个完整的ECMAScript解析器,它只接受一个参数,即要执行的ECMAScrip(t或JavaScript) 字符串。

当解析器发现代码中调用 eval()方法时,它会将传入的参数当作实际的 ECMAScript 语句来解析, 然后把执行结果插入到原位置。通过 eval()执行的代码被认为是包含该次调用的执行环境的一部分, 因此被执行的代码具有与该执行环境相同的作用域链。

特殊的值undefined、NaN 以及 Infinity 都是 Global 对象的属性。此外,所有原生引用类型的构造函数,像 Object 和 Function,也都是 Global 对象的属性。下表列出了 Global 对象的所有属性:

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

另一种取得 Global 对象的方法是使用以下代码:

var global = function(){ 
    return this;
}();

 以上代码创建了一个立即调用的函数表达式,返回 this 的值。如前所述,在没有给函数明确指定 this 值的情况下(无论是通过将函数添加为对象的方法,还是通过调用 call()或 apply()),this 值等于 Global 对象。而像这样通过简单地返回 this 来取得 Global 对象,在任何执行环境下都是可 行的。

在Math对象中,

min()和 max()方法用于确定一组数值中的最小值和最大值。

Math.ceil()执行向上舍入。

Math.floor()执行向下舍入。

Math.round()执行标准舍入。(四舍五入)

Math.random()方法返回大于等于 0 小于 1 的一个随机数。

发布了122 篇原创文章 · 获赞 25 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/HuoYiHengYuan/article/details/102799854