You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
引用类型的值(对象)是引用类型的一个实例。在 ES 中,引用类型是一种数据结构,用于将数据和功能组织在一起。通常被称为类,但是实际上这种称呼并不妥当。尽管 ES 从技术上讲是一门面向对象语言,但它不具备传统的面向对象语言所支持的类和接口等基本结构。引用类型有时候也被称为对象定义,因为他们描述的是一类对象所具有的属性和方法。
PS:本章节摘自于JS宝典,其中有些描述也已过时,甚至在严格模式下不能使用,或者已经废弃的,但是在了解 ES 的历史背景的情况下,的确可以加深对 JS 的理解。js 是一门非常灵活的语言,所以我甚至以为第一门开发语言就学习 js 是不利于理解面向对象程序设计的,事实上看了以后的确如此。甚至很多方法的设计为了灵活都非常简化,不利于去理解他的作用与原理。
就像前面说的,对象是某个特定引用类型的实例。新对象是使用 new 操作符跟着一个构造函数创建的。而构造函数本身就是一个函数,只不过该函数是出于创建新对象的目的而定义的。
1. Object 类型
到目前为止,我们看到的大多数引用类型都是 Object 类型的实例;而且,Object 也是 ES 中使用最多的一个类型。
//将长度设置为 2 会移除最后一项varcolors=["red","blue","green"];//creates an array with three stringscolors.length=2;alert(colors[2]);//undefined
//将 length 设置为大于数组长度的值,则新增的每一个都是 undefinedvarcolors=["red","blue","green"];//creates an array with three stringscolors.length=4;alert(colors[3]);//undefined
我们还可以利用 length 属性在数组末尾添加新项:
arcolors=["red","blue","green"];//creates an array with three stringscolors[colors.length]="black";//add a colorcolors[colors.length]="brown";//add another coloralert(colors.length);//5alert(colors[3]);//blackalert(colors[4]);//brown
varcolors=newArray();//create an arrayvarcount=colors.push("red","green");//push two itemsalert(count);//2count=colors.push("black");//push another item onalert(count);//3varitem=colors.pop();//get the last itemalert(item);//"black"alert(colors.length);//2
varcolors=newArray();//create an arrayvarcount=colors.push("red","green");//push two itemsalert(count);//2count=colors.push("black");//push another item onalert(count);//3varitem=colors.shift();//get the first itemalert(item);//"red"alert(colors.length);//2
varcolors=newArray();//create an arrayvarcount=colors.unshift("red","green");//push two itemsalert(count);//2count=colors.unshift("black");//push another item onalert(count);//3varitem=colors.pop();//get the first itemalert(item);//"green"alert(colors.length);//2
varcolors=["red","green","blue"];varremoved=colors.splice(0,1);//remove the first itemalert(colors);//green,bluealert(removed);//red - one item arrayremoved=colors.splice(1,0,"yellow","orange");//insert two items at position 1alert(colors);//green,yellow,orange,bluealert(removed);//empty arrayremoved=colors.splice(1,1,"red","purple");//insert two values, remove onealert(colors);//green,red,purple,orange,bluealert(removed);//yellow - one item array
window.color="red";varo={color: "blue"};//定义在全局作用域中,引用了 this 对象functionsayColor(){alert(this.color);}sayColor();//redo.sayColor=sayColor;o.sayColor();//blue
varnumberObject=newNumber(10);varnumberValue=99;//toString() using a radixalert(numberObject.toString());//"10"alert(numberObject.toString(2));//"1010"alert(numberObject.toString(8));//"12"alert(numberObject.toString(10));//"10"alert(numberObject.toString(16));//"a"//toFixed()alert(numberObject.toFixed(2));//outputs "10.00"numberObject=newNumber(99);alert(numberObject.toPrecision(1));//"1e+2"alert(numberObject.toPrecision(2));//"99"alert(numberObject.toPrecision(3));//"99.0"alert(typeofnumberObject);//objectalert(typeofnumberValue);//numberalert(numberObjectinstanceofNumber);//truealert(numberValueinstanceofNumber);//false
就像前面说的,对象是某个特定引用类型的实例。新对象是使用 new 操作符跟着一个构造函数创建的。而构造函数本身就是一个函数,只不过该函数是出于创建新对象的目的而定义的。
1. Object 类型
到目前为止,我们看到的大多数引用类型都是 Object 类型的实例;而且,Object 也是 ES 中使用最多的一个类型。
创建 Object 实例的方式有两种:
使用 new 操作符后跟 Object 构造函数
使用字面量表达式
在通过字面量定义对象时,实际上不会调用 Object 构造函数(数组同理)。
一般来说,访问对象属性时都是用的点表达式,这也是很多面向对象语言中通用的语法。不过当我们需要使用通过变量来访问属性时需要使用方括号表达式来访问属性,其他情况推荐使用点语法。
2. Array 类型
除了 Object 之外,Array 就是我们最常用的类型了。和其他语言一样,也是有序列表,但是区别是 ES 数组的每一项都可以保存任何类型的数据。
关于 length 有个特点,它不是只读的,因此可以通过这个属性,可以从数组的末尾移除或向数组中添加新项:
我们还可以利用 length 属性在数组末尾添加新项:
2.1 检测数组
instanceof 操作符的问题在于,它假定只有一个全局执行环境。如果网页中包含多个框架,那实际上就存在两个以上不同的全局执行环境,从而存在两个以上不同版本的 Array 构造函数。为此,ES5 新增了 Array.isArray() 方法
2.2 转换方法
所有对象都有 toLocaleString()、toString()、valueOf() 方法。
valueOf() 返回数组本身,toString() 返回逗号分隔的字符串。
另外,toLocaleString() 方法一般与上面两个方法相同的值,但也不总是如此。
使用 join 方法则可以使用不同的分隔符来得到这个字符串。
2.3 栈方法
栈是 LIFO 的结构,ES 专门提供了 push 和 pop 方法
push() 可以接收任意数量的参数,把他们逐个添加到数组末尾,并返回数组的长度;
pop() 方法则从数组末尾移除最后一项,减少数组的 length 值,然后返回移除的项;
2.4 队列方法
队列是 FIFO 的结构,shift()、unshift() 就是 ES 提供的方法
shift() 方法能够移除数组的第一个项并返回该项,同时将数组长度减 1。结合使用 shift() 和 push() 方法,可以像使用队列一样使用数组。
unshift() 和上述相反,可以从相反的方向来模拟队列,即在数组的最前端添加项,从数组末端移除项:
2.5 重排序
这里说下 sort ,先看下以下代码:
比较的结果显然不是我们想要的,这是为什么呢?
sort 方法会调用每个数组项的 toString 转型方法,然后比较得到的字符串。即使数组的每一项都是数值,sort 方法比较的也是字符串,下面我们进行传入比较函数来解决这个问题:
2.6 操作方法
concat() --不会改变原始数组
concat() 可以基于当前数组中的所有项创建一个新数组。具体来说,就是创建当前数组一个副本,然后将接收到的参数添加到这个副本的末尾。可以传递多个数组。
slice()--不会改变原始数组
slice() 方法能够基于当前数组中的一个或多个项创建一个新数组。接收一个或两个参数,即要返回项的起始和结束位置。只有一个参数的情况下,默认表示从该参数指定到结尾的所有项。如果有两个,则表示之间的项,但不包含结束位置的项。
splice()--会改变原始数组
splice() 方法始终都会返回一个数组,该数组中包含从原始数组中删除的项。(如果没删除,则返回空数组)
2.7 位置方法
indexOf() 和 lastIndexOf() 都接收两个参数:要查找的项和(可选的)表示查找起点位置的索引。都要返回要查找的项在数组的位置,或者在没找到的情况下返回 -1。比较的时候也是全等操作符。
2.8 迭代方法
ES5 定义了5个迭代方法。每个方法都接收两个参数:要在每一项上运行的函数和(可选的)运行该函数的作用域对象--影响 this 的值。传入的函数会接收三个参数:value、index、arr(本身)。
以下 5 个迭代方法都不会修改数组中的包含的值。
every some
every 和 some 相似,用于查询数组中的项是否满足某个条件。
对于 every 来说,每一个都返回 true 才能返回 true。
而 some 只要有某一个返回 true,就会返回 true。
filter
filter 利用指定的函数确定是否在返回的数组中包含某一项。例如要返回一个都大于 2 的数组:
map
map 则是都在原始数组中的对应值上进行运行函数的结果组成返回值
2.9 归并方法
reduce()和 reduceRight()。这两个方法都会迭代数组的所有项,然后构建一个最终返回的值。
reduce()方法从数组的第一项开始,逐个遍历到最后。
而 reduceRight()则从数组的最后一项开始,向前遍历到第一项。
都接收两个参数:一个在每一项上调用的函数和(可选的)作为归并基础的初始值。而函数则接收 4个参数:前一个值、当前值、索引、数组对象。这个函数的返回值会作为第一个参数自动传给下一项。
reduce()、reduceRight()可以执行求和操作:
3.Date 类型
UTC balabala 好像没啥好说的
4. RegExp 类型
正则我是整体都不是很了解,后面也许、可能会单独讲吧...
5. Function 类型
首先!函数实际上是对象。每个函数都是 Function 类型的实例,而且都与其他引用类型一样具有方法和属性。由于函数是对象,因此函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定。函数通常使用函数声明语法定义的:
以上代码看到,function 关键字后面没有函数名。这是因为再使用函数表达式定义函数时,没有必要使用函数名---通过变量 sum 即可引用函数。
另外还有一种定义函数的方式是使用 Function 构造函数。Function 构造函数可以接收任意数量的参数,但最后一个参数始终都被看成是函数体,而前面的参数则枚举出了新函数的参数。
从技术上讲,这是一个函数表达式。但是不推荐这种用法。因为会导致两次解析(一次常规解析 ES 代码,第二次是解析传入构造函数中的字符串,从而影响性能)。
这种语法对于理解“函数是对象,函数名是指针”的概念是非常直观的。
从下面这段代码我们来加深下函数是对象的概念:
5.1 没有重载
将函数名想象为指针,也有助于理解为什么 ES 中没有函数重载的概念。
以上例子声明了两个同名函数,而结果则是后面的覆盖了前面的函数。其实上面代码和下面的没有什么区别,这样可能就理解了为什么被覆盖了,也理解了为什么函数没有重载的概念:
5.2 函数表达式与函数声明
通常我们在使用函数表达式与函数声明并无什么区别,而实际上,解析器在向执行环境中加载数据时,对函数声明和表达式并非相同。解析器会率先读取到函数声明,并使其在执行任何代码之前可用;至于函数表达式,则必须等解析器执行到他所在的代码行,才会真的被解释执行。
上面代码运行OK,因为在代码执行前,解析器就已经通过一个名为函数声明提升的过程,读取并将函数声明添加到执行环境中。对代码求值时,JavaScript 引擎在第一遍会声明函数并将他们放到源码树的顶部。所以即使声明函数的代码再调用他的代码后,JavaScript 引擎也能把函数声明提升到顶部。
像下面的这个代码就会导致错误:
之所以会报错,原因在于函数位于一个初始化语句中,而不是一个函数声明。
5.3 作为值的函数
可以作为参数传递给另一个函数,如果你能理解函数就是对象,函数名就是指针,那么你就知道函数名本身就是变量,当然也可以作为值来使用。
5.4 函数内部属性
这里要说的是在函数内部的两个特殊对象:arguments 和 this。
1.arguments
arguments 是一个类数组对象,包含着传入函数中的所有属性。主要用途是保存函数参数,这个对象还有一个名叫 callee 的属性,是一个指针,指向拥有这个 arguments 对象的函数。
这是一个阶乘递归算法,递归的问题在于耦合,修改函数名称就要修改两处。为了消除这个耦合,就可以像下面这样使用 arguments.callee
2.this
另一个特殊对象是 this,其行为与 Java 和 C# 中的 this 大致类似。换句话说,this 引用的是函数执行的环境对象——或者也可以说是 this值(当在网页的全局作用域中调用函数时,this 对象引用的就是 window)。
caller
caller 属性保存着调用当前函数的引用,如果是在全局作用域中调用当前函数,它的值为 null。
上面的代码输出了 outer 函数的源码。因为 outer()调用了 inner(),所以 inner.caller()就指向 outer()。当然也可以实现松耦合,可以这么做:
5.5 函数属性和方法
属性
前面提到过,ES 中的函数是对象,因此函数也有属性和方法。每个函数都包含两个属性:length、prototype。其中 length 表示函数希望接收的命名参数的个数,如下:
另一个是 prototype 属性。对于 ES 中的引用类型而言,prototype 是保存他们所有实例方法的真正所在。换句话说,诸如 toString()、valueOf()等方法都保存在 prototype 名下,只不过是通过各自对象的实例访问罢了。后续面向对象会详情讲解 prototype。
call & apply
每个函数都包含两个非继承方法:apply()、call()。这两个的用途都是在特定的作用域中调用函数,实际上等于设置函数体内 this 对象的值。
首先,apply()方法接收两个参数:一个是在其中运行函数的作用域,另一个是参数数组。第二个参数可以是 Array 的实例,也可以是 arguments 对象:
call()方法和 apply()方法作用相同,他们的区别仅在于接收参数的方式不同。对于call()方法,第一个参数是 this 值没有变化,变化的是其余参数都直接传递给函数。也就是说,在使用 call()时,传递给函数的参数必须逐个列出来:
事实上,传递参数并非 apply()和 call()真正的用武之地;他们强大的地方是能够扩充函数赖以运行的作用域。看一个例子:
使用 call()和 apply()来扩充作用域的最大好处,就是对象不再需要与方法有任何耦合关系。
ES5 中还定义了 bind()方法,这个方法会创建一个函数的实例,其 this 值会被绑定到传给 bind()函数的值:
6. 基本包装类型
为了方便操作基本类型值,ES 还提供了 3 个特殊的引用类型:Boolean、Number 和 String。这些类型与其他引用类型类似,但同时也具有与各自的基本类型相应的特殊行为。
实际上,每当读取一个基本类型值的时候,后台就会创建一个对应的基本包装类型的对象,从而让我们能够调用一些方法来操作这些数据。
我们知道这个 s1 当然是基本类型值。而为什么不是对象的 s1 能调用 substring() 方法,从逻辑上不应该。其实,为了让我们呢能直观的操作,后台已经自动完成了一系列的处理。
1)创建 String 类型的一个实例;
2)在实例上调用指定的方法;
3)销毁这个实例;
也可以显示的调用 String、Boolean、Number 来创建基本包装类型的对象。不过,应该在绝对必要的情况下。因为这种做法很容易让人分不清自己是在处理基本类型还是引用类型的值。typeof 判断对于引用类型会返回 “object”。
6.1 Boolean 类型
同理,建议不要使用 Boolean 对象。
6.2 Number 类型
toString()方法对于 Number 传入不同参数代表不同进制;
6.3 String 类型
6.3.1 字符方法
两个用于访问字符串中特定字符的方法是:charAt()和 charCodeAt()。
这两个方法都接收一个参数,即基于 0 的字符位置。其中 charAt() 方法以单字符串的形式返回给特定位置的那个字符:
而 charCodeAt()则返回字符编码:
6.3.2 字符串操作方法
concat()用于将一或多个字符串拼接起来,其实用加号操作符 + 就好了。
ES 提供了三个基于子字符串创建新字符串的方法:slice()、substr()、subString()。
这三个方法都会返回被操作字符串的子字符串,而且都接收一或两个参数。
第一个参数指定子字符串的开始位置,第二个参数(在指定情况下)表示子字符串到哪里结束。
具体来说,slice()和 subString()的第二个参数指定的是子字符串最后一个字符后面的位置。而 substr()的第二个参数指定的则是返回的字符个数。如果没有传递第二个参数,则默认到结尾。与 concat()方法一样,这三个方法都不会改变字符串本身的值。
如果传递负值的话行为就不一样了。
其中,slice()方法会将传入的负值与字符串长度相加,substr()方法将负的第一个参数加上字符串的长度,第二个负的值转换为 0.而 subString()方法会把所有负的都转换为 0。
6.3.3 indexOf、lastIndexOf、trim、大小写
indexOf()、lastIndexOf()方法我们之前也讲过了,如果某个字符只出现一次,那么调用这两个方法会返回相同的位置值。
trim()方法用于删除前置及后缀的所有空格。
toLowerCase()、toUpperCase()
7. 单体内置对象
也就是说,开发人员不必显式的实例化内置对象,因为他们已经实例化了。之前我们所介绍的 Object、Array 和 String 这些都是内置对象。ES 中还定义了两个单体内置对象:Global 和 Math。
7.1 Global 对象
Global(全局)对象比较特别,某种意义上是作为一个终极的兜底对象来定义。换句话说,不属于任何其他对象的属性和方法,都是它的属性和方法。isNaN()、isFinite()、parseInt()这些其实都是 Global 对象的方法。
URI 编码方法
decodeURI、decodeURIComponent、encodeURI、encodeURIComponent
eval
eval()方法很强大,就像一个 ES 解析器,接收一个参数,参数是要执行的 ES 字符串。其实就是可以实现热部署,但是其实很危险,所以的代码注入,防止用户有恶意输入。
window 对象
ES 虽然没有指出如何直接访问 Global 对象,但是 Web 浏览器都是将这个全局对象作为 window 对象的一部分加以实现的。因此,在全局作用域中声明的所有变量和函数,就都成为了 window 对象的属性。
7.2 Math 对象
Math 对象提供很多实用方法。
min、max
取最大值、最小值
random
返回大于等于0 小于1 的一个随机数
舍入
The text was updated successfully, but these errors were encountered: