(本文源自“JS高级程序设计”第5章读后梳理)
这一章主要讲的是JavaScript中的原生引用类型,包含的内容为:
(注,和“JS基础——作用域、原型、闭包初探(上)”中的内容一样,这些都是JS中十分基础的知识,要十分熟悉的记忆在脑子里面。)
依稀记得看过的其他一些博主写的文章,谈到学习JS基础,要有系统的去学习,作为小白的我,自学,也不知道啥是有系统的学习,那我干脆,直接把这本基础经典书的目录给背了下来。
还是有些些用,至少脑子里是知道哪些内容在哪里。 X)
应该没有人比我的方法更笨了。。。
接着说正题:
引用类型是JavaScript中的一种将数据和方法组织起来的数据结构。
不能把它们叫做类,虽然JavaScript是面向对象的编程语言,但是JavaScript中是没有类和接口这些基本结构的,所以对引用类型的称呼更准确的是“对象定义”,因为它定义了同一类对象应该具有的属性和方法。
应用类型的值就是它的一个实例,可以通过new操作符+构造函数来创建。
这一章我们通过目录的的顺序来讲:Object、Array、Date、RegExp、Function、基本包装函数、单体内置对象。
一、Object类型
Object类型有三个关键点:
- 创建Object实例的第一种创建方式:
var person = new Object();
-
创建Object实例的第二种方式:对象字面量
var person = { name:"dingding", job:"software engineer", age:29 };
-
方括号访问符:(这个不重要)
就是通过方括号访问实例中的变量。假如有变量名是“first name”,这种玩意,不可能通过点运算符去访问,这个时候就需要通过这个方括号访问。(这是我不是很明白,有什么变量能定义为first name的形式?不应该是驼峰写法——firstName吗?)//如果实例中有一个变量名字长成了first name这个样子 var name = person.first name;//显然,这是不可能的 var name = person["first name"];//正解
二、Array类型
Array类型一些基本的创建方式这里就不详细的描述了,着重的还是去记忆一些Array类型的方法:
检测数组、转换方法、栈方法、队列方法、重排序方法、操作方法、位置方法、迭代方法、归并方法。
总共九个小的专题,一个一个的来:
- 检测数组:Array.isArray(value),检测value是不是一个数组;
- 转换方法:toString()、toLocaleString()、valueOf()、join()。
- 前两个的作用就不说了。
- valueOf()返回数组本身。
- join(),将数组转换为字符串,以传入join()的字符为间隔。如果没有传入任何值,或者“undefined”,就按照默认的方式转换(和toString()相同)——以逗号为间隔。
- 栈方法:push()、pop()。
- 栈方法,“后进先出”,就像一个“没有出口的盒子”,一个一个的把数据放进去,后放进去的先拿出来。
- push(),将数据放入数组的最后一位,可以一次性push()多个数据,返回push()后数组长度;
- pop(),将数据从栈中弹出,一次只能弹出一个,返回弹出的数据。
- 队列方法:shift()、unshift()。
- 队列,“先进先出”,就把它想象成普通的排队好了,排在前面的先出去。
- shift(),将数组第一个数据弹出,一次一个,返回弹出的数据。搭配push()使用。
- unshift(),将数据推入数组第一位,一个可多个,返回数组长度,搭配pop()使用。
- 重排序方法:reverse()、sort()
- reverse(),将数组的数据,倒叙排列。
- sort()将数组升序排列——即从小到达,注意这里的从小到达,是字符的序号,而不是数字的大小。
var arr =[1,3,5,15,25]; arr.sort();//[1,15,25,3,5],是按照字符序号升序,而不是数字
想要通过数字的大小排列,我们就需要给sort()方法传入一个compare()方法:
var arr = [1,3,15,5,25]; function compare(value1,value2){ if(value1 < value2){ return -1; }else if(value1 > value2){ return 1; }else{ return 0; } } arr.sort(compare);//[1,3,5,15,25]
- 操作方法:concat()、slice()、splice()。
- concat(),可接受多个参数,创建数组的副本,将数据添加到数组的末尾,返回数组。
- slice(),接受两个参数,截取,两个参数分表表示开始截取位置,和结束截取位置。
- 返回原数组截取段的副本,结束截取位置的值不包含,可以这么记忆——数组中的[index1,index2)。
- 如果两个参数中有负数,表示的就是数组的长度加上该负数的结果。
- splice(),这个方法会直接在数组上操作,操作后,原数组会发生变化,不同于前两个方法。接收参数的方式很灵活,但可以理解为接收三段参数:
- 第一个参数表示截取开始位置;
- 第二个参数表示,要删除的项数,会删除掉包括第一个位置的数据哦;
- 第三个参数及以后的参数,都表示要插入到第一个参数位置及之后的数据。
- 当第二参数为0的时候,可以通过第三个参数实现插入数据;当第二个参数不为0的时候,可以实现替换数据。
- splice()函数返回截取出来的数组。
- 位置方法:indexOf()、lastIndexOf()。
- 查找位置,传入两个参数,第一位是要找到数据,第二位开始查找的位置;
- indexOf()从前往后找,lastIndexOf()从后往前找;
- 返回索引值。
- 迭代方法:every()、some()、filter()、map()、forEach()。
- 这些方法都需要传入一个匿名函数,匿名函数有接收三个参数:
function(item,index,array){ //需要进行的操作 }
-
every(),对数组中的每一个项,进行一个操作,如果数组每一个项在匿名函数中都return true,那么every()函数返回true,反之,返回false。
var arr = [1,2,3,4,5]; //要所有的项都满足>2,那么every()才会返回true //这里every()返回false var val = arr.every(function(item,index,array){ item>2; });
-
some(),与上面every()的操作相同,但是如果数组中有一个符合条件,就返回true。所以上面如果调用的是some()就返回true。
-
filter(),返回满足条件的值组成的数组。
var arr = [1,2,3,4]; var val = arr.filter(function(item,index,array){ return item>2; });//返回一个数组,为[3,4,5]
-
map(),对数组的每一项进行一个操作,返回经过操作后的数组值 所组成的数组。
var arr = [1,2,3,4,5]; var val = arr.map(function(item,index,array){ retrun item*2; });//返回一个数组,[2,4,6,8,10]
-
forEach(),对数组的每一项运行一个操作,没有返回值。
var arr = [1,2,3,4]; var val = arr.forEach(function(item,index,array){ //执行某些操作,然后返回这个数组 });
- 这些方法都需要传入一个匿名函数,匿名函数有接收三个参数:
-
归并方法:reduce()、reduceRight()。
-
对数组进行迭代,并归并为一个值;
-
reduce(),从前到后归并;reduceRight(),从后到前归并。
-
需要向reduce()、reduceRight()中传递一个匿名函数,匿名函数包含四个参数——前一项,当前项,索引,数组。看代码:
var arr = [1,2,3,4,5]; var val1 = arr.reduce(function(prev,cur,index,array){ //需要进行的操作 return prev + cur; });//返回整个数组项相加的结果
-
三、Date类型
继承的方法,日期格式化方法,日期/时间组件方法
其他的都不细说了,简单的记一下两个关键点:
- 日期的格式化书写方式:
- 6/13/2020
- january 12,2020
- Tue January 12 2020 00:00:00 GMT-0700
- 2020-01-12T00:00:00
- 加时间戳
//获取代码运行到此处的时间点 var timeBegin = +new Date(); ... var timeEnd = +new Date(); //获取代码运行到此处的时间点 var timeBegin = Date.now(); ... var timeEnd = Date.now();
四、RegExp类型
有助于理解RegExp的是一种方式是记住三种的东西:
RegExp实例中是我们要应用的正则表达式,在目标字符串(String)上面应用我们的模式(pattern),然后得到我们的匹配项(match)。
正则表达式,两种创建方式:字面量、RegExp实例
RegExp字面量这样创建:
//这里的pattern就是我们要用到的正则表达式字面量,
//flag有三个值:i,g,m,分别表示忽略大小写,对整个字符串应用,对多行应用
var expression = /pattern/flag;
RegExp实例像这样创建:
//pattern就是要应用的模式,flag的取值也是i,g,m
var expression = new RegExp("pattern","flag");
注意:在模式中使用的元字符都需要转义:{ [ ( * + \ . ^ | ? $ ) ] }
RegExp实例属性,RegExp实例方法、RegExp构造函数,可以用这个图来概括:
- exec()传入目标字符串,然后返回一个数组,数组中的内容为图中所示;
- test()传入目标字符串,如果目标字符串上有符合调用者——RegExp实例(或者看做是pattern)的匹配项,则返回true;没有,则返回false。
- toString()、toLocaleString(),返回正则表达式的字面量。
- valueOf(),返回正则表达式本身。
- 长属性名都可以用相应的短属性名来代替。只不过这些短属性名大都不是有效的ES标识符,因此必须通过方括号语法来访问:
var text = "this has been a short summer"; var pattern = /(.)hort/g; if(pattern.test(text)){ alert(RegExp.$_); //this has been a short summer alert(RegExp["$&"]); //short alert(RegExp["$+"]); //s alert(RegExp["$`"]); //this has been a alert(RegExp["$'"]); //summer alert(RegExp["$*"]); //false alert(RegExp.$1); //s }
-
第一个捕获组访问符的简写形式:$1,第二捕获组访问符简写形式为:$2。。。。
五、Function类型
按顺序介绍:没有重载(深入理解)、函数声明与函数表达式、作为值的函数、函数内部属性、函数属性和方法。
在这之前我们要知道创建Function类型实例的三种方式:
- 函数声明:
function addNum(num1,num2){ //要执行的函数体 }
-
函数表达式:
var addNum = function(){ //要执行的内容 }
-
利用new创建实例:(不推荐这种方式,会导致解析两次代码,解析ES代码,第二次解析传入构造函数中的字符串)
//接收三个参数,最后一个参数为再实例中进行的操作 //不推荐这种使用方式是因为创建的过程中,需要 var addNum = new Function("num1","num2","return num1+num2");
开始一个一个的介绍:
- 没有重载:前面讲到过,因为函数的接收参数是通过一个数组接受的,虽然定义函数的时候可以定义一定数量要求的参数,比如要求传入两个参数,但是在实际调用的时候时,可以传入一个参数、两个参数、三个参数、四个参数,甚至更多。
- 这个时候函数是没有函数签名的,真正的重载是做不到的。
- 在ES5中,函数名,其实是一个指向堆内存中一个函数体,后面重写的同名函数,会将前面的同名函数覆盖掉。
- 函数声明与函数表达式:具体的形式前面都讲到了,这里助于一个点就是:
- 函数声明提升:function declaration hoisting,在对函数求值时,JavaScript引擎在第一遍声明函数并将它们放到源代码树的顶部。
//由于addNum()是通过函数声明创建的,所以addNum()会被放到顶部, //所以先调用,或声明,也可以得到正确的答案 alert(addNum(1,3));//4 function addNum(num1,num2){ return num1+num2; }
- 函数声明提升:function declaration hoisting,在对函数求值时,JavaScript引擎在第一遍声明函数并将它们放到源代码树的顶部。
-
作为值的函数:即一个函数作为另一个函数的值,被return,这里我们在闭包再重点讲解。。。
function compare(propertyName){ return function(object1,object2){ name1 = object1[propertyName]; name2 = object2[propertyName]; if(name1 < name2){ return -1; }else if(name1 > name2){ return 1; }else{ return 0; } } }
-
函数的内部属性:arguments.callee,this,caller。
-
arguments.callee,取得当前函数的引用,可以应用于递归函数的解耦,这个属性在严格模式不能使用。
-
this,表示当前函数的执行环境对象;
-
caller表示调用当前函数的函数的引用。
function outer(){ inner(); } function inner(){ alert(inner.caller); } outer();
这里调用outer(),会调用inner()函数,inner函数内部又会通过caller,将调用inner的函数(也就是outer())的函数体给通过警告框展示出来。
注意:这样可能会导致outer()的函数体内容泄漏。
在严格模式下,不允许向caller属性赋值。
-
-
函数属性和方法:
-
内部属性:length,prototype。
length就是定义函数的定义的参数个数;
prototype,原型,在后面将原型的时候,再细说。 -
内部方法:apply(),call(),bind()。
-
apply(),接收两个参数,调用者要运行的环境(也就是调用者的this值),和 传入调用者的参数数组(可以是Array实例,也可以是arguments对象)。
-
call(),接收两个参数,和apply类似,只是第二参数传入时,必须逐个列举出来。
-
bind(),这个方法会创建一个实例,然后将调用者的this值绑定为传入bind()的参数。
-
这些方法的实现细节不在这里细讲,但需要去搞清楚。
-
这些方法的优点,会在后面讲到——函数绑定、函数柯里化。。
-
-
六、基本包装类
ES中的三种特殊的引用类型:Boolean、Number、String。
它们三个是基本类型,但在创建以后,ES会在后台创建一个对应的基本包装类,以便我们能够调用响应的方法。
就是说,这三种类型本来时基本类型,但在创建后,后台会为它们创建一个对应的对象,方便调用关于它们的一些方法,来操作它们。
- Boolean:没啥可说的,,因为建议永远不要使用Boolean对象。
- Number:同样不建议直接实例化Number,和Boolean一样。三个方法,有兴趣了解的童鞋自己看书哦。
- toFixed(),将数字保留小数位,保留的位数由传入函数的参数值决定;
- toExpronential(),将数字保留位数,但是这是指数,同样,保留位数由传入函数的参数数值决定;
- toPrecision(),将数字保留位数,至于是小数还是指数,由引擎“自己看着办”。
- String:记住这么一些方法就好了。。这里都不将这些方法了,大家可以自行看书了解。。
- 字符方法:charAt()、charCodeAt()。
- 字符串操作方法:concat()、slice()、substring()、substr()。
- 字符位置方法:indexOf()、lastIndexOf()。
- trim()。
- toLowerCase()、toUpperCase()。
- 模式方法:match()、search()、replace()、split()。
- localeCompare()。
- fromCharCode()。
- HTML方法。
七、单体内置对象
ES中对内置对象的定义是:不依赖于宿主环境的对象,这些对象在ES程序执行之前就已经存在了。
意思是说开发人员不必显示的实例化内置对象,因为它们已经实例化了。
Object、Array、String等都是内置对象。
这里介绍两个ES定义的单体内置对象:Global和Math。
- Global对象:终极“兜底”对象,不属于任何其他对象的属性和方法,最终都是它的属性和方法。
- 我现在还不知道怎么用它这个东西,先知道有这么个东西存在,到以后在下去细致的了解吧。。。
- 记住三个方法:
- encodeURIComponent(),对uri的某一段进行编码,把uri的所有没标准字符进行编码转换;
对应的就是decodeURIComponent(), - encodeURI(),对整个uri进行编码。与上面这个方法的区别就是:
encodeURI()不会对本身属于URI的特殊字符进行编码,如冒号、正斜杠、问号、井字号。而encodeURIComponent()会对发现的所有非标准字符进行编码。
一般来说,使用encodeURIComponent()方法的时候要比使用encodeURI()更多,因为在实践中更常见的是对查询字符串参数而不是基础URI进行编码。 - eval()方法,ES中最强大的一个方法,这个方法就像是一个完整的ES解析器。
- 只接受一个参数,即要执行的ES字符串。
- 具体的大家自行看书。
- encodeURIComponent(),对uri的某一段进行编码,把uri的所有没标准字符进行编码转换;
- Math对象,记住几个方法:
- ceil(),向上舍入;
- floor(),向下舍入;
- round(),“四舍五入”;
- random(),随机数,范围[0,1)。