第5章 引用类型 所有引用对象的名字不过是指针而已

199 阅读17分钟

引用类型的值(对象)是引用类型的实例。ECMAScript提供了很多原生引用类型,如Object

Object 类型

创建一个对象:

var person=new Object();person.name="xiaohong";person.age=12;

可以使用对象字面量来简写:

var person={    name:"xiaohong",    age:12};

属性名也可以用字符串,没什么区别。

创建对象时,也可以留空花括号:

var person={}; //与 var person=new Object(); 含义相同,只包含默认属性和方法

可以用方括号来表示访问属性,属性名需要字符串表示,也可以用变量代替:

var person=new Object();
person["name"]="xiaoming";  //和person.name="xiaoming"是一个意思
var m="name";
alert(person[m]);

Array 类型

数组中每一项可以是不同的数据类型值。

创建一个数组:

var colors1=new Array();
var colors2=Array(); //可以省略new
var colors3=Array(3); //创建含3项的数组
var colors4=Array("xiao") //创建一个包含一项""xiao"。非数值时为具体项数

可以使用数组字面量来简写:

var colors=["green","bule","black"];
var colors1=[]; //空数组
var colors2=[,,,] //不推荐,每一项都是undefined值

读取和设置数组可以:

var colors=["green","bule"];
alert(colors[0]);  //读取第一项
colors[1]="red";  //修改第二项
colors[3]="black"  //新增2项,其中一项为undefined

数组项数保存在length属性中。length不是只读的,可以通过设置这个属性,在末尾新增或者去除项数。

var colors=["red","bule","green"];colors.length=2; //去掉末尾项alert(colors[2]); //undefcolors[colors.length]="yellow"; //末尾新增一项alert(colors[2]); //yellow

当把一个值放在超出数组大小的位置上时,数组就会重新计算其长度,即长度为索引加1。中间空的值为undefined。

检测数组

对于单个网页可以用 value instanceof Array

对于多个网页可以用 Array.isArray()方法:Array.isArray(value)

转化方法

每个对象都有toString()、toLocaleString()、toValue()方法。转化不影响原本的数组。

一般情况下数组会每一项都调用toString(),返回的是拼接成一个以逗号为分隔符的字符串。

toLocaleString()和toString()差不多,只是每一项调用的是toLocaleString()方法。

toValue()方法返回的还是数组。

join()方法可以使用不同的分隔符构建字符串。在没有接受参数或者参数为undefined的情况下默认为逗号。

alert()会自动转型为字符串。

var person1={    toString:function(){        return "nihao";    },    toLocaleString:function(){        return "xwt";    }};var person2={    toString:function(){        return "nihao11";    },    toLocaleString:function(){        return "xwt11";    }};var person=[person1,person2];alert(person);  //nihao,nihao11alert(person.toString()); //nihao,nihao11alert(person.toLocaleString()); //xwt,xwt11alert(person.join("||")); //nihao||nihao11

栈方法

后进先出,末端操作。执行了便会修改原来数组。

push()方法:接收任意数量的参数,逐个添加到数组末尾,并返回修改后数组长度。

pop()方法:从数组末尾移除一项,并返回移除项。

队列方法

先进先出,首尾操作。执行了便会修改原来数组。用push()末尾增加项。这里列出两种前端操作方法。

shift()方法:移除数组中第一项,并返回该项。

unshift()方法:在数组前端添加项,并返回修改后的数组长度。

重排序方法

会改变原数组,返回的是经排序后的数组

reverse()方法:反转数组。

sort()方法:将数组中每一项转化为字符串比较大小后,按升序排列。也可以自己写个函数,作为参数,自定义比较规则,函数中交换返回正数,不交换负数,相等0。

function compare(value1,value2){    if(value1>value2){        return 1;    }    else if(value2>value1){        return -1;    }    else{        return 0;    }}var array=[1,15,10,5];array.sort(compare);console.log(array); //[1,5,10,15]

操作方法

concat()方法:不影响原数组。创建一个新的数组副本。参数可以是数组或者数组项。

var xwt=["ni","shi"],xwtCopy=xwt.concat(":",["sha","bi"]);alert(xwt);  //ni,shialert(xwtCopy); //ni,shi,:,sha,bi

slice()方法:不影响原数组。基于当前数组,创建一个新数组。参数分别为起始和结束位置(返回的项不包括结束位置的项)。只有一个参数时,表示起始位置至末尾所有项。

如果参数中有负数,则实际为各自加上数组长度。起始位置大于结束位置则返回空数组。

var xwt=["ni","shi","da","sha","zi"];var xwtc=xwt.slice(0,2);var xwtCopy=xwt.slice(2);alert(xwt);  //ni,shi,da,sha,zialert(xwtc) //ni,shialert(xwtCopy) //da,sha,zi

splice()方法:会改变原数组。splice(起始位置,删除的项数,[插入项])。返回删除的项组成的数组,如果没删除,则为空数组。注意插入项插在起始位置前面。

var xwt=["ni","shi","da","sha","zi"];var a=xwt.splice(0,2); //da,sha,zialert(xwt); //ni,shialert(a);xwt.splice(1,0,"ge","da","da"); alert(xwt); //da,ge,da,da,sha,zi 插在起始位置前

位置方法

indexOf():返回查找的项在数组中的位置。从头查找

lastIndexOf():返回查找的项在数组中的位置。从尾巴查找

都接收两个参数:要查找的项和可选的起始查找位置。找不到时,返回-1。

迭代方法

每个方法都接受2个参数:要在每一项上运行的函数和(可选的)运行该函数的作用域对象。

函数会接收三个参数:数组项的值、该项在数组中对应的位置、数组对象本身,需要返回的必须要return。

every():运行每一项都true,则返回true。

some():运行每一项,有一项是true,则返回true。

filter():运行每一项,返回结果是true项组成的数组。

map():运行每一项,返回处理结果组成的数组。

forEach():运行每一项,没返回值。类似for循环。

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

var arr=[1,5,9,7,9,8];var num=0;var erverArray=arr.every(function(item,index,array){    return item>2;});var someArray=arr.some(function(item,index,array){    return item>2;})var filterArray=arr.filter(function(item,index,array){    return item>2;});var mapArray=arr.map(function(item,index,array){    return item+2;})arr.forEach(function(item,index,array){    num+=1;})console.log(erverArray); //falesconsole.log(someArray); //trueconsole.log(filterArray); //[5, 9, 7, 9, 8]console.log(mapArray); //[3, 7, 11, 9, 11, 10]console.log(num); //6

归并方法

reduce()方法:参数一个可选的每一项上都调用的函数和一个初始值。参数函数会接受4个参数:前一个值、当前值、项的索引、数组对象。这个函数返回的任何值会作为第一个参数传给下一项。

reduceRight()方法:从数组尾部往前遍历,其他和reduce()方法一样。

使用reduce()求和例子如下:

var arr=[1,5,9,7,9,8];var num=arr.reduce(function(pre,cur,index,array){    return pre+cur;},1);alert(num); //40

reduce()第二个参数省略时,第一次迭代发生在数组第二项,否则pre为初始值,cur为数组第一项。

Date 类型

创建一个日期对象:

var now=new Date(); //调用构造函数不传参数的情况下,会自动获得当前时间

Date.parse()方法:能接受表示日期的字符串参数,返回一个创建的日期。参数格式有:

  • ”月/日/年“——"5/7/2020"
  • 英文月名 日,年——"May 7,2004"
  • 英文星期 英文月名 日 年 时:分:秒 时区——"Tue May 7 2020 11:29:32 GTM-0700"
  • ISO 8601扩展格式 YYYY-MM-DDTHH:mm:ss:sssZ——"2020-05-07T11:29:32"
Date.UTC()方法:参数为数字:年,月(基于0月开始,一月为0)和(可选的)日、时、分、秒。日默认为1,时分秒默认0。

构造函数Date()能根据接受的参数,省略Date.parse()和Date.UTC()。

var date1=new Date();var date2=new Date(Date.parse("5/7/2020"));var date3=new Date(Date.UTC(2020,4,7));var date4=new Date(2020,4,7);
//以上值都为:Thu May 07 2020 00:00:00 GMT+0800 (中国标准时间)

Date.now()方法:返回调用这个方法时的时间毫秒数。

var arr=[1,2,3,4,5,6];var start=Date.now();arr.reduce(function(pre,cru,index,array){    return pre+cru;},2);var stop=Date.now();alert(stop-start);

继承的方法

Date类型也重写了toString()、toLocaleString()、valueOf()方法(返回日期毫秒数)。

比较日期的时候,使用valueOf()方法。

日期格式化和操作日期的一些方法:P102

日期格式化主要是转化字符串。

操作日期主要是分别获取(get)和设置(set)年 月 日 时 分 秒的一些方法。

RegExp 类型

创建一个正则表达式:

var expression=new RegExp("[bc]at","i")

字面量:

var expression=/ pattern / flags;

正则的字面量和RegExp()构造函数创建的不一样。字面量会共享一个实例,而构造函数创建的每个实例都是新实例。P105

exec()方法:接受一个参数,即要应用的字符串,返回包含第一个匹配项信息的数组,没有的情况下返回null。数组第一项为整个模式匹配的字符串,其他项为捕获项。如果没有捕获项,则数组只有一项。不是全局模式,每次调用返回的都是第一个匹配项。全局模式,每次调用都是返回下一个匹配项。

test()方法:只接受一个字符串参数。可以用来验证用户输入的内容是否与某个模式匹配。

转字符串都是返回对象的字面量表达,与创建方式无关。

Function 类型

每个函数都是Function类型的实例,都有与其他引用类型一样的具体属性和方法。

声明一个函数对象:

function sum(sum1,sum2){    return sum1+sum2;}

用函数表达式创建:

var sum=function(sum1,sum2){    return sum1+sum2;};

用函数表达式声明要注意}后面的逗号。

也可以用new Function()构造函数创建。

函数声明和函数表达式唯一的区别是:解析器会率先读取函数声明,并使其在执行任何代码之前可用。至于函数表达式,必须等到解析器执行它所在的代码行。

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

作为值的函数

函数名本身就是变量,可在函数中把另一个函数作为参数传入。

function callSomeFunction(someFuction,obj){    return someFuction(obj);}function sum(sum1){    return sum1+2;}var result=callSomeFunction(sum,2);console.log(result);  //4

函数为引用数据类型可以作为值,当然也可以从一个函数中返回另一个函数。

function compareObject(property){    return function(obj1,obj2){        if (obj1[property]>obj2[property]){            return -1;        }        else if(obj1[property]<obj2[property]){            return -1;        }        else{            return 0;        }    }}var data=[{name:"nihao",age:12},{name:"w",age:13},{name:"hh",age:14}];data.sort(compareObject("age"));console.log(data); //按年龄降序排列

函数内部属性

函数内部有两个特殊对象(内部的属性类似于在函数里自己var一个,作用域来说只能在函数内使用):

  • arguments类数组对象,保存传入的参数。arguments中有个callee属性指向函数(指针指向函数,便可以用这个属性调用函数)。
  • this:指向的是执行这个函数的环境对象(不是函数本身的活动对象,是它作用域链下一级。所有引用类型的对象,对可看成变量对象)
  • caller:保存着调用当前函数的函数引用。

function outer(){    inner();}function inner(){    alert(inner.caller); //返回outer()方法}outer();

函数的属性和方法

和函数内部的属性不同,这个是继承Functione类型的。

  • length:表示希望接收的命名参数个数。
  • prototype:对于引用类型而言,是保存它们所有实例方法的所在,例如valueOf()等实际保存在这个属性中,只是通过实例访问罢了。
  • apply()、call():非继承的方法。用来在特定的作用域中调用函数(运行了函数),实际上等于设置函数内的this值。接受两个参数:一个是运行函数的作用域;两个方法唯一的区别是,apply()的另一个参数是数组,call()的另一个参数必须一一罗列出来。减少了代码的耦合,不用非得放到一个对象中this才能指代该对象。

var o={color:"blue"};var color="red";function bd(){    return this.color; }alert(bd()); //redalert(bd.call(o));//blue
alert(bd.apply(o));//blue

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

var o={color:"blue"};var color="red";function bd(){    return this.color; }alert(bd()); //redvar bd2=bd.bind(o);alert(bd2());//blue

  • toString()、toLocaleString()会返回函数的代码,每个浏览器不一样。

基本包装类型

有了它,能像操作对象一样操作基本数据类型。

Number、Boolean、String三个特殊的引用类型是对于基本数据类型设计的。因为基本数据类型不是对象,值不能进行操作,但事实上我们能操作。如:

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

为什么能操作呢,起始读取访问时,系统后台会完成以下处理:

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

一般的引用类型与基本包装类型的最大区别:使用new创建的引用类型的实例,在执行流离开当前作用域时,一直保存在内存中。而自动创建的基本包装类型对象,只存在于代码执行的一瞬间,然后立即被销毁。

不在绝对必要的情况下,不要显示地调用基本包装类型创建对象,容易和基本数据类型弄混。

Object()构造函数也会像工厂模式一样,根据传入值的类型返回相应基本包装类型的实例:

var str=new Object("nihaoa");alert(str instanceof String); //true

要注意调用new创建对象和转型函数是有区别的:

var value="23";var num=new Number(value);alert(typeof num);  //objectvar num1=Number(value);alert(typeof num1);//number

Boolean 基本包装类型

建议永远不要使用Boolean对象,容易混淆。特别是&&、||操作符中。

Number 基本包装类型

  • toString()方法:可带一个参数,设置进制转化为字符串。
  • toFixed()方法:按照指定的小数位数返回数值的字符。能够自动舍入的特性,方便处理货币。

var num=12.008;alert(num.toFixed(2)); //12.01

toExponential():按照指定的小数位数返回指数表示法的字符串。

String 基本包装类型

这里的方法操作完,原字符串都不变,都是创建新的字符串。这与对象,数组不一样,数组concat()和slice()是创建副本,还有迭代、归并方法不变,其他都改变原数组。对象要体现可便,基本类型体现不可变。

也可以用方括号加索引来访问其中的字符。

  • length属性:字符串中包含多少个字符。
①字符方法:用于访问字符串中特定的字符:
  • charAt():参数为位置的数字,返回一个字符
  • charCodeAt():参数为位置的数字,返回这个字符的编码

②字符串操作方法:都不影响原字符串:

  • concat():与数组中的类似。将一个或多个字符串拼接起来,返回拼接后的字符串。(事实上,字符串拼接一般用+)
  • slice():与数组中的类似。返回起始位置至结束位置的字符串,不包含结束位置。参数为负数时,都加上字符串长度
  • substring():正数时,和slice()用法一样。负数时,第一个参数加字符串长度,第二个转换为0。
  • substr():第一个参数为起始位置,第二个参数是取字符的个数。参数为负时都转换为0。
③字符串位置方法:
  • indexOf():从前往后找,返回位置,没找到返回-1.。第二个参数可以指定从哪个位置开始找。
  • lastIndexOf():从后往前找,返回位置,没找到返回-1。第二个参数可以指定从哪个位置开始找。
④字符串trim()方法,不影响原字符串:
  • trim():创建一个字符串副本,删除前缀和后缀所有空格。

var s="  你好  啊   ";alert(s);        //"  你好  啊   "alert(s.trim()); //"你好  啊"

⑤字符串大小写转换方法,不改变原字符串:

  • toLowerCase():转换为小写。
  • toUpperCase():转换为大写。
  • toLocaleLowerCase()toLocaleUpperCase()分别是针对地区的大小写转化。在不知道地区的情况下,用这个更靠谱。

⑥字符串的模式匹配方法,不改变原字符串:

  • match():和regExp的exec相同。只接受一个参数:regExp对象或者正则表达式。

var ptest="cat,bat,gat";var reg=/.at/g;console.log(ptest.match(reg)); //["cat", "bat", "gat"]console.log(reg.exec(ptest)); //["cat", index: 0, input: "cat,bat,gat", groups: undefined]

  • search():参数和match()相同,返回字符串中第一个匹配项的索引,没找到就返回-1。
  • replace():替换子字符串。第二个参数也可以是函数,使用更加灵活。

var s="cat,bat,sat";var s1=s.replace("at","ond");var s2=s.replace(/at/g,"ond");console.log(s); //cat,bat,satconsole.log(s1); //cont,bat,satconsole.log(s2); //cond,bond,sond

  • split():用指定的分隔符,将字符串分割成多个字符串,放入数组中,返回一个数组。第二个参数,可以用于指定数组的大小。

var ha="cat,ni,mie,m";console.log(ha.split(",")); //["cat", "ni", "mie", "m"]

⑦localeCompare()方法

  • localeCompare():用于比较两个字符串。大的情况下返回正数,相等返回0,小的时候返回负数。由于具体返回值不确定,可以这样用来约束返回值:

var stringvalue="nihao";function compareStr(text){    var result=stringvalue.localeCompare(text);    switch(true){        case result> 0:            return 1;            break;        case result<0:            return -1;            break;        default:return 0;    }}alert(compareStr("hh")); //1

⑧formCharCode()方法

  • formCharCode:String的构造函数本身的方法,接收一或多个字符编码,然后将他们转化为字符串。

alert(String.fromCharCode(104,101,108,108,111));//hello

单体内置对象

内置对象:不依赖宿主环境的对象,这些对象在程序执行之前就已经存在了。开发人员不必显式的实例化内置对象。前面已经介绍了很多内置对象,如Object、Aarry、String。ECMAScript还定义了两个单体内置对象Global和Math。

Global 对象

所有在全局中定义的函数和变量,都是Global的属性。

①URL编码函数

  • encodeURL():编码整个url,只把空格转化为%20。对应的解码函数decodeURL()
  • encodeURLComponent():所有的特殊符号都会编码,所以一般只用于编码最后一段。对应的解码函数decodeURLComponent()

②eval()函数

  • eval():会将传入的参数当作ECMAScript语句来执行。非严格模式下,和平时写代码没什么区别,但是里面创建的任何变量和函数都不会被提升,它们只在eval()执行的时候创建。严格模式下,外部不能访问eval()中定义的变量。

eval("alert('nihao')"); //就是弹出"nihao"弹出框

如特殊值undefined、NaN、infinite都是Global的属性,构造函数Object等都是Global属性。

注:永远不要给特殊值赋值。

window对象:web浏览器都是将这个全局对象作为window对象的一部分。因此在全局作用域中声明的所有变量和函数,都成了window对象的属性。

/** 用立即执行函数表达式来获取Global对象。在浏览器中也是window对象*/var global=function(){    return this;}(); 

Math 对象

保存数学公式信息的公共位置。

①Math属性:p134

max()min()方法:确定一组数中的最大最小值。一般用于避免多余的循环或者在if语句中确定最小最大值。

var math=Math.min(12,34,89,23);alert(math); //12

要在数组中找到最小最大值,可以如下使用:

var values=[1,2,3,4,5]var max=Math.max.apply(Math,values); //这里很好的使用了apply()方法的传参功能

③舍入方法

  • Math.ceil():向上舍入为整数。
  • Math.floor():向下舍入为整数。
  • Math.round():四舍五入为整数。
④random()方法

random():返回大于等于0,小于1的一个随机数。

/**取两个数区间的随机整数*/function selectForm(lowerValue,upperValue){    var choice=upperValue-lowerValue+1;    return Math.floor(Math.random()*choice+lowerValue);}alert(selectForm(2,10));var color=["red","green","yellow"];var form=color[selectForm(0,color.length-1)]; //随机返回数组中一项

⑤一些不太用的方法:P136