第5章:引用类型

引用类型的值(对象)是引用类型的一个实例。在ECMAScript中,引用类型是一种数据结构,用于将数据和功能组织在一起。新对象是使用new操作符后跟一个构造函数来创建的。t

5.1 Object类型

创建Object实例的方式有两种。

第一种使用new操作符后跟Object构造函数。

var person = new Object();

person.name = "Jason";

person.age = 23;

第二种是使用对象字面量表示法。

var person =  {

    name: "Jason",

    age: 23

};

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


向函数传递大量可选参数

这种传递参数的模式最适合需要向函数传入大量可选参数的情况。命名参数虽然可以处理,但是如果在有多个可选参数的情况下,会显得不够灵活。最好的做法是对那些必须值使用命名参数,而多个可选值使用对象字面量封装。

访问对象字面量属性,除了点表示法还可以使用方括号访问。(除非必须使用变量来访问属性,否则建议使用点表示法)

5.2 Array类型

ECMAScript数组与其它语言的数组相同点:都是数据的有序列表。

不同点:ECMAScript数组的每一项都可保存任何类型的数据。数组的大小是可以动态调整的,可以随着数据的添加自动增长以容纳新数据。

创建数组的基本方式有两种:

第一种是使用new操作符后跟Array构造函数。(使用Array构造函数可以省略new操作符

var colors = new Array(20);  //创建了一个length值为20的数组

var  rank = new Array(“first”, “second”, "thirdly"); //创建了一个包含三个字符值的数组

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

var color = ["red", "blue", "orange"]

5.2.1 检测数组

Array.isArray()  确定某个值是不是数组 返回结果为布尔值

var  num = [1, 2, 3];

Array.isArray(num); //true

5.2.2  数组转换方法

toString() 会返回由数组中每个值的字符串形式拼接而成的一个以逗号分隔的字符串。

valueOf()  返回的还是数组本身。

5.2.3 数组栈方法

ECMAScript数组提供了一些让数组的行为类似其他数据的方法,可以使数组表现得像栈一样。栈是一种后进先出的数据结构。

push()  在向数组末尾添加任意数量的参数,并返回修改后数组的长度。

var arr = new Array();

var count = arr.push('red', 'blank');

console.log(count); //2

pop()  从数组末尾移除最后一项,减少数组的length值,然后返回被移除的项。

var item = arr.pop();

console.log(item); //blank

5.2.4 数组队列方法

shift() 移除数组的第一项并返回该项,数组长度减一。

var arr = new Array();

var count = arr.push('red', 'blank'); //count    2

var item = arr.shift(); //item  red

unshiift() 在数组的前端添加任意个项并返回新数组的长度。

console.log(arr); //blank

var newLength =  arr.unshift("orange", "pink"); //newLength  3

console.log(arr.toString()); // orange, pink, blank

5.2.5 数组重排序方法

数组中存在两个重排序的方法:

reverse() 反转数组项

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

values.reverse();

console.log(values); //[5, 4, 3, 2, 1]

 sort()  默认情况下是按升序排列数组项—最小值在前面,最大值在后面。

sort()会调用每个数组项的toString()方法,然后得到比较字符串,以确定如何排序。即使数组中每一项都是数值,比较的也是字符串。

var  values = [0, 1, 5, 10, 15];

console.log(values.sort()); // [0, 1, 10, 15, 5];

由于在字符比较时,10和15位于5的前面,所以顺序会达不到我们想要的结果。

最佳方案:sort()方法可以接收一个比较函数作为参数,以便指定哪个值可以位于哪个值的前面。

比较函数接收两个参数,如果第一个参数应该位于第二个参数前面就返回一个负数,如果第一个参数应该位于第二个后面就返回-1,如果相同返回0。


sort()

对于数值类型或者其valueOf方法会返回数值类型的对象类型。可以使用一个简单的比较函数,这个函数用一个值去减另一个值。(第二个值减第一个是从大到小排序,第一个减第二个是从小到大排序)

function compare(value1, value2) {

    return    value2 - value1; 

}

5.2.6 数组操作方法

concat() 基于当前数组中的所有项创建一个新数组。这个方法会先创建当前数组的一个副本,然后将接收到的参数添加到这个副本的末尾,最后返回新的数组。在没有传递参数的情况下,只是复制当前数组并返回副本。如果传递给concat()方法的是一或者多个数组,该方法会将这些数组的每一项添加到结果数组中。

var nums = [0, 1, 10, 5, 15];

var newNums = nums.concat('yellow', ['blank', 'pink']);

console.log(newNums); //[0,1,10,5,15,'yellow', 'blank', 'pink']

slice() 它能够基于当前数组中的一或者多个项创建一个新数组。slice()接受一或者两个参数,即返回项的起始和结束位置。在只有一个参数的情况下,会返回从该参数的起始位置到数组末尾的所有项。如果有两个参数会返回起始和结束之间的项(不包括结束位置的项)。slice()方法并不会影响原始数组。

splice() 有三种用途,主要是向数组的中部插入项。

删除:可以删除任意数量的项,需要传入两个参数要删除的第一项的位置和要删除的项数

var num2 = [1,2,3,4,5,6];

num2.splice(3,2); //删除从数组下标第三个位置开始的两个项

console.log(num2); // [1,2,3,6]

插入:可以向指定位置插入任意数量的项,需要三个参数起始位置、0(要删除的项数)和要插入的项。如果要插入多个项,可以再传入第四个第五个至任意多个项。

var num2 = [1,2,3,4,5,6];

num2.splice(1, 0 , 'yellow', 'pink');

console.log(num2); //[1, 'yellow', 'pink', 2,3,4,5,6]

替换:可以向指定位置插入任意数量的项,同时删除任意数量的项,需要指定三个参数起始位置、要删除的项数、和要插入的任意数量的项数。插入的项数不必要和要删除的项数相等。

var num3 = [1,2,3,4,5,6];

var removed = num3.splice(2, 3, 'yellow', 'green');

console.log(removed); //[3,4,5]

console.log(num3); //[1,2,'yellow', 'green', 6]

splice()方法始终都会返回一个数组,该数组包含从原始数组删除的项(如果没有删除的项会返回空数组)

5.2.7 数组位置方法

ECMAScript5为数组实例添加了两个位置方法:

indexOf() 从数组的开头,索引位置为0开始查找

lastIndexOf()  从数组的末尾,开始往前查找。

这两个方法都接收两个参数:要查找的项和表示查找起点点位置的索引(可选)。这两个方法都会返回要查找的项在数组中的位置,或者没有找到的情况下返回-1。在比较第一个参数和数组的每一项时,使用的是全等操作符(===),要求查找的项必须严格相等。

5.2.8  数组迭代方法

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

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

var num = [0,1,2,3,4,5];

var everyResult = num.every(function(item, index, array) {

    return item > 2;

})

console.log(everyResult); //false

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

var num = [0,1,2,3,4,5];

var filterResult = num.filter(function(item, index, array) {

return item > 3;

})

console.log(filterResult); // [4,5]

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

var person = [

    {name:'Jason', age:23},

    {name:'Porter', age:35},

    {name:'Carter', age:18}

];

function getAge(arr, str) {  //根据姓名获取年龄

    var age = 0;

    arr.forEach(function(item, index) {

        if(item.name === str) {

        age = item.age;

      }

});

    return age;

}

var age = getAge(person, 'Jason');

console.log(age); // 23

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

var num = [0,1,2,3,4,5];

var mapResult = num.map(function(item, index, array) {

    return item * 2;

});

console.log(mapResult); // [0,2,4,6,8,10]

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

var num = [0,1,2,3,4,5];

var someResult = num.some(function(item, index, array) {

    return item > 2;

}) ;

some.log(someResult); //true

5.2.9 归并方法

ECMAScript5 新增加了两个归并数组的方法:这两个方法都会迭代数组的所有项,然后构建一个最终返回值。

reduce():从数组的第一项开始,逐个遍历到最后。

reduceRight():从数组最后一项开始,逐个遍历到第一项。

reduce()和reduceRight()这两个方法都接收两个参数一个在每一项上调用的函数和(可选)作为归并基础的初始值。传给reduce()reduceRight()的函数都接收4个参数:前一个值、当前值、项的索引和数组对象。

这个函数返回的任何值都会作为第一个参数自动传给下一项。第一次迭代发生在数组的第二项上,因此第一个参数是数组的第一项,第二个参数是数组的第二项。

var values = [1,56,89,36,3.78,2.66];

var sum = values.reduce(function(prev, current, index, array) {

    return prev + current;

});

console.log(sum); //188.44

5.5 Function类型

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

定义函数函数可以使用函数声明语法定义和函数表达式定义。

function sum(num1, num2) { //函数声明语法

    return num1 + num2;

}

var sum1 = function(num1, num2) { //函数表达式

    return num1 + num2;

};

在使用表达式定义函数时,没有必要使用函数名——通过定义一个变量即可引用函数。函数末尾要有分号,就像声明其它变量一样。

由于函数名只是一个指向函数的指针,所以函数名与包含对象指针的其它变量没有区别,因此一个函数可以有多个名字。

function sum(num1, num2) {

    return num1 + num2;

}

console.log(sum(10,2)); //12

var anotherSum = sum;

console.log(anotherSum(10, 7)); //17          

5.5.1 没有重载

重载:就是函数或者方法有相同的名称,但是参数列表不相同的情形,这样的同名不同参数的函数或者方法之间,互相称之为重载函数或者方法。所以说,重载主要需要两点:第一,同样的函数名。第二,不同的函数参数。

在ECMAScript中,如果声明了两个同名函数,结果会是后面的函数覆盖前面的函数。

5.5.2 函数声明和函数表达式

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

console.log(sum(1,2)); // 3

function sum(num1, num2) {

    return num1 + num2;

}

以上代码,在代码开始执行前,解析器就已经通过了一个名为函数声明提升的过程,读取并将函数声明添加到执行环境中对代码求值时,JavaScript引擎在第一遍会声明函数并将它们放到源代码树的顶部所以,即使声明函数的代码在调用它的代码后面,JavaScript引擎也能把函数声明提升到顶部。

console.log(sum(1,2)); // sum is not a function

var sum = function(num1, num2) {

    return num1 + num2;

}

5.5.3 作为值的函数

由于ECMAScript中的函数名本身就是变量,所以函数也可以作为值来使用,当函数变量名后面不跟括号的时候,变量名是一个指向函数的指针。


将函数作为参数传递给另一个函数

从一个函数返回另一个函数


根据某个属性排序

5.5.4 函数内部的属性 

在函数内部有两个特殊的对象:argument和this。

argument是一个类数组对象,保存着传入函数中的所有参数。它有一个名叫callee的属性,该属性是一个指针,指向拥有这个argument对象的函数。

this引用的是函数执行的环境对象——也可以说是this值(当在网页的全局作用域中调用函数时,this对象引用的是window)

caller:保存这调用当前函数的函数的引用。

function outer() {

    inner()

}

function inner() {

    console.log(arguments.callee.caller);

}

outer(); //[Function: outer]

5.5.5 函数的属性和方法

由于ECMAScript中的函数也是对象,所以函数也有属性和方法。每个函数都包含两个属性:

length属性表示函数希望接收的命名参数个数。

prototype属性,对于引用类型而言,prototype是保存它们所有实例方法的真正所在。例如toString()和valueOf()方法实际上都是保存在prototype名下,只不过是通过各自对象的实例访问。在ECMAScript5中,prototype属性是不可枚举的。

每个函数都包含两个非继承而来的方法:apply()和call()。这两个方法都是在特定的作用域中调用函数,实际上等于设置函数体内的this对象的值。

apply()接收两个参数:一个是在其中运行函数的作用域,另一个是参数数组(Array实例或者arguments对象)。


apply()

call():第一个是在其中运行的函数的作用域,剩下的是逐个列举出来的参数。

function sum(num1, num2) {

    return num1 + num2;}

function callSum(sum1, sum2) {

    return sum.call(this, sum1, sum2);

}

console.log(callSum(50, 55.5)); //105.5

bind():会创建一个函数的实例,其this值会被绑定到传给bind()函数的值。

window.color ='red';

var o = {color:'green'};

function logColor() {

    console.log(this.color);

}

logColor();// red

var objectLogColor =logColor.bind(o);

objectLogColor(); //blue

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容