引用类型的值(对象)是引用类型的一个实例。在ECMAScript中,引用类型是一种数据结构,用于将数据和功能组织在一起。它也常被称为类,但这种称呼并不太妥当。引用类型有时候也被称为对象定义,因为它们描述的是一类对象所具有的属性和方法。
- 对象是某个特定引用类型的实例。
- 新对象是使用new操作符和一个构造函数来创建的。(构造函数本身就是一个函数,该函数是出于创建新对象的目的定义的)
一. Object类型
- 创建Object实例的两种方法
- 第一种是使用new操作符后跟Object构造函数
var person = new Object(); person.name = 'Nicholas'; person.age = 29;
- 第二种就是使用对象字面量表示法
var person = { name: 'Nicholas', age: 29, };
- 访问对象属性时使用的都是点表示法,也可以使用方括号表示法来访问对象的属性。
- 使用方括号语法时,应该讲要访问的属性以字符串的形式放在方括号中
alert(person["name"]);
- 方括号语法的主要优点是可以通过变量来访问变量
var propertyName = 'name'; alert(person[propertyName]);
- 使用方括号语法时,应该讲要访问的属性以字符串的形式放在方括号中
二. Array类型
- 创建数组的基本方式有两种
- 第一种是使用Array构造函数
var colors = new Array(); var colors = new Array(20);// 创建一个length值为20的数组 var colors = new Array('red', 'blue', 'green');// 创建一个包含3个字符串的数组
- 第二种基本方式是使用数组字面量表示法
var colors = ['red', 'blue', 'green'];// 创建一个包含3个字符串的数组 var names = [];// 创建一个空数组
- 一个数组如果设置某个值的索引超过了数组现有项数,数组就会自动增加到该索引值加1的长度。
var colors = ['red', 'blue', 'green'];// 定义一个字符串数组
alert(colors[0]);// 显示第一项
colors[2] = 'black';// 修改第三项
colors[3] = 'brown';// 新增第四项
- 数组的length可读可写,通过设置length,可以将数组的末尾移除项或向数组中添加新项
var colors = ['red', 'blue', 'green'];// length为3
colors.length = 2;
alert(colors[3]);// undefined
- 检测数组使用Array.isArray()方法
用法:if (Array.isArray(value)) { // 对数组执行操作 }
- 栈方法
- 栈是一种LIFO(后进先出)的数据结构,也就是最新添加的项最早被移除。而栈中项的插入和移除只发生在一个位置——栈的顶部。
- push()方法可以接收任意数量的参数,把它们逐个添加到数组末尾,并返回修改后数组的长度。
- pop()方法则从数组末尾移除最后一项,减少数组的length值,然后返回移除的项。
- 队列方法
- 队列数据结构的访问规则是FIFO(先进先出)。队列在列表的末端添加项,从列表的前端移除项。
- push()方法可以接收任意数量的参数,把它们逐个添加到数组末尾,并返回修改后数组的长度。
- shift()方法,它能够移除数组中的第一个项并返回该项,同时数组的length减1。(unshift()方法用途与shift()相反,它能在数组前端添加任意个项并返回新数组的长度)
- 重排序方法
- reverse()方法会反转数组项的顺序。
- sort()方法按升序排列数组项——即最小的值位于最前面,最大的值排在最后面。(为了实现排序,sort()方法会调用每个数组项的toString()转型方法,然后比较得到的字符串,以确定如何排序)。
- 即使数组中的每一项都是数值,sort()方法比较的也是字符串
var values = [0, 1, 5, 10, 15]; values.sort(); alert(values);// 0, 1, 10, 15, 5
- sort()方法可以接收一个比较函数作为参数,以便指定哪个值在哪个值前面
function compare (value1, value2) { if (value1 < value2) { return -1;// 不需要交换顺序 } else if (value1 > value2) { return 1;// 需要交换顺序 } else { return 0; } } var values = [0, 1, 5, 10, 15]; values.sort(compare); alert(values);// 0, 1, 5, 10, 15
- 操作方法
- concat()方法可以基于当前数组中的所有项创建一个新数组(这个方法会先创建当前数组的一个副本,然后将接收到的参数添加到这个副本的末尾,最后返回新构建的数组)
- 如果传递给concat()方法的是一个或者多个数组,该方法会将这些数组中的每一项都添加到目的数组的末尾;如果传递的值不是数组,这些值就会被简单的添加到目的数组的末尾。
- slice()方法能够基于当前数组中的一或多个项创建一个新数组。(不影响原数组)
- 可以接收一或两个参数,即要返回项的起始和结束位置。
- 只有一个参数的情况下,slice()方法返回从该参数指定位置开始到当前数组末尾的所有项。
- 如果有两个参数,该方法返回起始和结束位置之间的项(不包括结束位置的项)
- 如果slice()方法的参数中有一个负数,则用数组长度加上该数来确定相应的位置。例如length为5中的数组中
slice(-2, -1)
和slice(3 ,4)
得到的结果一样。 - 如果结束位置小于起始位置,就会返回空数组。
- 如果slice()方法的参数中有一个负数,则用数组长度加上该数来确定相应的位置。例如length为5中的数组中
- splice()方法(听说是最强大的数组方法)
-
删除:可以删除任意数量的项,需要指定两个参数——要删除的第一项位置和要删除的项数。例如:
splice(0, 2)
会删除数组中的前两项。 -
插入:可以向指定位置插入任意数量的项,需要指定三个参数——起始位置、0(要删除的项数)和要插入的项。如果要插入多个项,可以再传入第四、第五,以至任意多个项。例如:
splice(2, 0, 'red', 'green')
会从当前数组的位置2开始插入字符串'red'和'green'。 -
替换:可以向指定位置插入任意数量的项,且同时删除任意数量的项。需要指定三个参数——起始位置、要删除的项数和要插入的任意数量的项。插入的项数不必与删除的项数相等。例如:
splice(2, 1, 'red', 'green')
会删除当前数组位置2的项,然后在从位置2开始插入字符串'red'和'green'。
-
删除:可以删除任意数量的项,需要指定两个参数——要删除的第一项位置和要删除的项数。例如:
- 位置方法
- indexOf()和lastIndexOf()这两个方法都接收两个参数——要查找的项和(可选的)表示查找起点位置的索引。
- indexOf()方法从数组的开头(位置0)开始向后查找,lastIndexOf()方法则从数组的末尾开始向前查找。返回要查找的项在数组中的位置,或者没有找到的情况下返回-1。
- 比较第一个参数与数组中的每一项时,会使用全等操作符;也就是说,要求查找的项必须严格相等(就想使用===一样)
var numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1]; alert(numbers.indexOf(4));// 3 alert(numbers.lastIndexOf(4));// 5 alert(numbers.indexOf(4, 4));// 5 alert(numbers.lastIndexOf(4, 4));// 3 var person = { name: 'Nicholas' }; var people = [{ name: 'Nicholas' }]; var morePeople = [person]; alert(people.indexOf(person));// -1 alert(morePeople.indexOf(person));// 0
- 迭代方法
- every()、filter()、forEach()、map()、some()中每个方法都接收两个参数:要在每一项上运行的函数和(可选的)运行该函数的作用域对象——影响this的值。(都不会修改原数组中包含的值)
- 传入这些方法中的函数会接收三个参数:数组项的值、该项在数组中的位置和数组对象本身。
- every():对数组中的每一项运行特定函数,如果该函数对每一项都返回true,则返回true。
- 传入的函数必须对每一项都返回true,这个方法才会返回true,否则为false。
var numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1]; var everyResult = numbers.every(function (item, index, array) { return (item > 2); }); alert(everyResult);// false
- some():对数组中的每一项运行特定函数,如果该函数对任一项返回true,则返回true。
- 只要传入的函数对数组中的某一项返回true,就会返回true
var numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1]; var someResult = numbers.some(function (item, index, array) { return (item > 2); }); alert(someResult);// true
- filter():对数组中的每一项运行特定函数,返回该函数会返回true的项组成的数组。
var numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1]; var filterResult = numbers.filter(function (item, index, array) { return (item > 2); }); alert(filterResult);// [3, 4, 5, 4, 3]
- map():对数组中的每一项运行特定函数,返回每次函数调用的结果组成的数组。
- 返回的数组的每一项都是在原始数组中的对应项上运行传入函数的结果
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]
- forEach():对数组的每一项运行特定函数,这个方法没有返回值
- 对数组中的每一项运行传入的函数。这个方法没有返回值,本质上与使用for循环迭代数组一样。
var numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1]; numbers.forEach(function (item, index, array) { // 执行某些操作 });
- 归并方法
- reduce()和reduceRight()这两个方法都会迭代数组的所有项,然后构建一个最终返回的值。reduce()方法从数组的第一项开始,逐个遍历到最后。reduceRight()方法从数组的最后一项开始,向前逐个遍历到第一项
- 两个方法都接收两个参数:一个在每一项上调用的函数和(可选的)作为归并基础的初始值。函数接收4个参数——前一个值、当前值、项的索引和数组对象。这个函数返回的任何值都会作为第一个参数自动传给下一项。第一次迭代发生在数组的第二项上。因此第一个参数是数组的第一项,第二个参数就是数组的第二项。
var values = [1, 2, 3, 4, 5];
var sum1 = values.reduce(function (prev, cur, index, array) {
return prev + cur;
});
var sum2 = values.reduceRight(function (prev, cur, index, array) {
return prev + cur;
});
alert(sum1);// 15
alert(sum2);// 15
三. Date类型
- 要创建一个日期对象,使用new操作符和Date构造函数即可
let now = new Date();
。在调用Date构造函数而不传递参数的情况下,新创建的对象自动获得当前日期和时间。
四. RegExp类型
- 正则表达式的匹配模式支持的3个标志
- g:表示全局模式,即模式将被应用于所有字符串,而非在发现第一个匹配项时立即停止。
- i:表示不区分大小写模式,即在确定匹配项时忽略模式和字符串的大小写。
- m:表示多行模式,即在到达一行文本末尾时还会继续查找下一行中是否存在与模式匹配的项。
- 创建正则表达式
- 字面量形式来定义:
let pattern = /[bc]ar/gi;
- 使用RegExp构造函数:它接收两个参数,一个是要匹配的字符串模式,另一个是可选的标志字符串。
let pattern = new RegExp('[bc]ar', 'gi');
五. Function类型
函数实际上是对象。每个函数都是Function类型的实例,而且都与其他引用类型一样具有属性和方法。由于函数是对象,因此函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定。
- 函数通常是使用函数声明语法定义的
function sum (num1, num2) {
return num1 + num2;
}
var sum = function (num1, num2) {
return num1 + num2;
}
// 两种都可以
- 函数名仅仅是指向函数的指针,所以一个函数可能有多个名字
- 没有重载(深入理解)
function addSomeNumber (num) {
return num + 100;
}
function addSomeNumber (num) {
return num + 200;
}
let result = addSomeNumber(100);// 300
虽然声明了两个同名函数,结果是后面的函数覆盖了前面的函数。所以说js不存在重载。
- 作为值的函数
- 函数名本身就是变量,所以函数可以作为值来使用
function callSomeFunction(someFunction, someArgument) {
return someFunction(someArgument);
}
function add10(num) {
return num + 10;
}
// 要访问函数的指针而不执行函数的话,必须去掉函数名后面的那对圆括号
var result = callSomeFunction(add10, 10);
console.log(result);// 20
- 函数内部属性
-
函数内部有两个特殊的对象——arguments和this。
- arguments是一个类数组的对象,包涵着传入函数中的所有参数。但这个对象还有一个名叫callee的属性,该属性是一个指针,指向拥有这个arguments对象的函数。
function factorial(num) { if (num <= 1) { return 1; } else { return num * arguments.callee(num - 1); } } // 这样,无论引用函数时使用什么名字,都可以保证正常完成递归 var trueFactorial = factorial; console.log(trueFactorial(5));// 120
- this引用的是函数据以执行的环境对象
window.color = "red"; var o = { color: "blue" }; function sayColor() { console.log(this.color); } sayColor();// "red" o.sayColor = sayColor; o.sayColor();// "blue"
-
函数对象的一个属性 —— caller(这个属性中保存着调用当前函数的函数的引用)
function outer() { inner(); } function inner() { console.log(arguments.callee.caller); } outer();// 显示outer()函数的源码
-
函数属性和方法 —— 函数是对象,因此函数也有属性和方法
-
每个函数都包含两个非继承而来的方法:apply()和call()。这两个方法的用途都是在特定的作用域中调用函数,实际上的部分等于设置函数体内this对象的值。
- apply()方法接收两个参数:一个是在其中运行函数的作用域,另一个是参数数组(其中,第二个参数可以是Array的实例,也可以是arguments对象)
function sum(num1, num2) { return num1 + num2; } // 传入arguments对象 function callSum1(num1, num2) { // 因为是在全局作用域中调用的,所以传入的就是window对象和arguments对象 return sum.apply(this, arguments); } // 传入数组 function callSum2(num1, num2) { return sum.apply(this, [num1, num2]); } console.log(callSum1(10, 10));// 20 console.log(callSum2(10, 20));// 30
- call()方法也接收两个参数(与apply()有些区别):第一个参数也是在其中运行函数的作用域,变化的是其余参数都会直接传递给函数
function sum(num1, num2) { return num1 + num2; } function callSum(num1, num2) { return sum.call(this, num1, num2); } console.log(callSum(10, 10));// 20
apply()和call()厉害的地方在于能够扩充函数赖以运行的作用域
window.color = "red"; var o = { color: "blue" }; function sayColor() { console.log(this.color); } sayColor();// "red" sayColor.call(this);// "red" sayColor.call(window);// "red" sayColor.call(o);// "blue" sayColor.apply(this, []);// "red" sayColor.apply(o, []);// "blue"
-