引用类型的值(对象)是引用类型的一个实例
- Object类型
- Array类型
- Date类型
- RegExp类型
- Function类型
- 基本包装类型
- 单体内置对象
Object
// new操作符后跟Object构造函数
var person = new Object();
person.name='zs';
person.age = 18;
// 对象字面量表示法
var person1 = {
name: 'zs',
age: 18
};
Array
// 使用Array构造函数
var arr = new Array(5);
console.log(arr) //数组里边有5项
var arr1 = new Array('red','green','blue');
console.log(arr1) // ["red", "green", "blue"]
// 使用数组字面量表示法
var arr2 = ['red','green','blue'];
console.log(arr2) // ["red", "green", "blue"]
// 使用length属性在数组末尾添加新项
var arr3 = ['red','green','blue'];
arr3[arr3.length]="black";
console.log(arr3) //["red", "green", "blue", "black"]
**检测数组**
// instanceof
console.log(arr3 instanceof Array)
// Array.isArray()方法
console.log(Array.isArray(arr2))
**转换方法**
// 所有对象的__proto__都有toLocalString()、toString()和valueOf()方法
// 数组的toString()方法
var arr4 = ['red','green','blue'];
console.log(arr4.toString())
// valueOf()方法返回本身
console.log(arr4.valueOf()) //["red", "green", "blue"]
// 栈方法push()和pop()方法
// 队列方法 unshift()和shift()
// sort()方法
// sort()方法按升序排列数组项——最小的值在最前面,最大的值在最后 面,为了实现排序,sort()方法会调用每个数组项
的toString()转型方法,比较得到的字符串,以确定如何排序
var values = [0,2,5,10,15];
values.sort();
console.log(values) //[0, 10, 15, 2, 5]
// sort()方法可以接收一个比较函数作为参数,比较函数接收两个参数,如果第一个参数应该位于第二个之前则返回一个负
数,如果两个参数相等,则返回0,如果第一个参数应该位于第二个之后则返回一个正数
function compare(value1, value2) {
if (value1 < value2) {
return -1;
} else if (value1 > value2) {
return 1;
} else {
return 0;
}
}
// 例
var val1 = [0,1,5,10,15];
val1.sort(compare);
console.log(val1)
**操作方法**
// concat()方法基于当前数组的所有项创建一个新数组。具体来说,这个方法会先创建当前数组的一个副本,然后将接收到
的参数添加到这个副本的末尾,最后返回新构建的数组。如果没有给concat()方法传递参数,它只是复制当前数组并返回副
本。
var colors = ['red','green','blue'];
var colors2 = colors.concat('yellow',['black','brown']);
console.log(colors2)
// slice [slaɪs] 从…切成
// slice()方法基于当前数组中的一或多个项创建一个新数组。slice()方法可以接收一个或连个参数,即要返回项的起始和结束
位置。在只有一个参数的情况下,slice()方法返回从该参数指定位置开始到当前数组末尾的所有项。如果有两个参数,该方
法返回其实和结束位置之间的项--但不包括结束位置的项。注意,slice()方法不会影响原数组。
var colors = ['red','green','blue','purple'];
var colors3 = colors.slice(1);
console.log(colors3) //["green", "blue"."purple"]
var colors4 = colors.slice(1,3) //["green", "blue"]
console.log(colors4)
// 如果 slice()方法的参数中有一个负数,则用数组长度加上该数来确定相应的位
// 置。例如,在一个包含 5 项的数组上调用 slice(-2,-1)与调用 slice(3,4)得到的
// 结果相同。如果结束位置小于起始位置,则返回空数组。
// [splaɪs] 剪接
// splice()方法,常用
- **删除**:可以删除任意数量的项,只需指定2个参数:要伤处的第一项的位置和要删除的项数。例如,splice(0,2)会删除数组中的前两项
- **插入**:可以向指定位置插入任意数量的项,只需提供3个参数;起始位置、0(要删除的项数 )和要插入的项。例如,splice(2,0,‘red’.'green')会从当前数组的位置2开始插入字符串‘red’和‘green’。
- **替换**:可以向指定位置插入任意数量的项,且同时删除任意数量的项,只需指定3个参数:起始位置、要删除的项数和要插入的任意数量的项。例如,splice(2,1,'red','green')删除当前数组位置2的项,然后插入‘red’,'green'
var colors = ["red", "green", "blue"];
var removed = colors.splice(0,1); // 删除第一项
alert(colors); // green,blue
alert(removed); // red,返回的数组中只包含一项
removed = colors.splice(1, 0, "yellow", "orange"); // 从位置 1 开始插入两项
alert(colors); // green,yellow,orange,blue
alert(removed); // 返回的是一个空数组
removed = colors.splice(1, 1, "red", "purple"); // 插入两项,删除一项
alert(colors); // green,red,purple,orange,blue
alert(removed); // yellow,返回的数组中只包含一项
// 位置方法
// indexOf()和lastIndexOf()。这两个方法接收两个参数:要查找的项和(可选的)表示查找起点位置的索引。其中,indexOf()方法从数组的开头(位置0)开始向后查找,lastIndexOf()方法从数组的末尾开始向前查找
// 这两个方法都返回要查找的项在数组中的位置,在没有找到的情况下返回-1
var numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
alert(numbers.indexOf(4)); //3
alert(numbers.indexOf(100)); //-1
迭代方法,可接收三个参数,数组项的值、该值在数组中的位置和数组对象本身
- every() 全部为true,返回true
- some() 任一项为true,返回true
- map() 返回执行结果后的数组
- filter() 返回为true项组成的数组
- forEach() 遍历,没有返回值
Date类型
// Date类型
var now = new Date();
//获取毫秒数
var now = Date.now()
日期格式化方法
- toDateString()——以特定于实现的格式显示星期几、月、日和年;
- toTimeString()——以特定于实现的格式显示时、分、秒和时区;
- toLocaleDateString()——以特定于地区的格式显示星期几、月、日和年;
- toLocaleTimeString()——以特定于实现的格式显示时、分、秒;
- toUTCString()——以特定于实现的格式完整的 UTC 日期。
日期/时间组件方法
RegExp 类型
略
Function 类型
函数实际上是对象。每个函数都是Function类型的实例,和其他引用类型一样具有属性和方法。由于函数是对象,因此函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定。函数通常使用函数声明语法定义
function sum(num1,num2) {
return num1 + num2;
}
//也可以使用函数表达式
var sum = function(num1,num2) {
return num1 + num2
}
// 也可以使用Function构造函数,Function 构造函数可以接收任意数量的参数,
但最后一个参数始终都被看成是函数体,
var sum = new Function("num1", "num2", "return num1 + num2"); // 不推荐
函数表达式定义了变量 sum 并将其初始化为一个函数。function 关键字后面没有函数名就是匿名函数,因为在使用函数表达式定义函数时,没有必要使用函数名——通过变量 sum 即可以引用函数。另外,还要注意函数末尾有一个分号,就像声明其他变量时一样。
使用Function构造函数不推荐,因为这种语法会导致解析两次代码(第一次是解析常规 ECMAScript 代码,第二次是解析传入构造函数中的字符串),从而影响性能。
**由于函数名仅仅是指向函数的指针,**因此函数名与包含对象指针的其他变量没有什么不同。换句话说,一个函数可能会有多个名字,如下面的例子所示。
function sum(num1, num2){
return num1 + num2;
}
alert(sum(10,10)); //20
var anotherSum = sum;
alert(anotherSum(10,10)); //20
sum = null;
alert(anotherSum(10,10)); //20
以上代码首先定义了一个名为 sum()的函数,用于求两个值的和。然后,又声明了变量 anotherSum,并将其设置为与 sum 相等(将 sum 的值赋给 anotherSum)。注意,使用不带圆括号的函数名是访问函
数指针,而非调用函数。此时,anotherSum 和 sum 就都指向了同一个函数,因此 anotherSum()也可以被调用并返回结果。即使将 sum 设置为 null,让它与函数“断绝关系”,但仍然可以正常调用
anotherSum()。
没有重载
将函数名想象为指针,有助于理解为什么ECMAScript没有重载的概念
function addSomeNumber(num) {
return num + 100;
}
function addSomeNumber(num) {
return num + 200;
}
var result = addSomeNumber(100);
console.log(result)//300
声明了两个同名函数,而结果则是后面的函数覆盖了前面的函数。以上代码实际
上与下面的代码没有什么区别。
var addSomeNumber = function (num) {
return num + 100;
};
addSomeNumber = function (num) {
return num +200;
};
var result = addSomeNumber(100);
console.log(result) //300
在创建第二个函数时,实际上覆盖了引用第一个函数的变量 addSomeNumber。
函数声明与函数表达式
alert(sum(10,10));
function sum(num1,num2) {
return num1 + num2;
} //ok
alert(sum(10,10));
var sum = function(num1,num2) {
return num1 + num2;
}//报错Uncaught TypeError:
作为值的函数
因为ECMAScript中的函数名本身就是变量,所以函数也可以作为值来使用。也就是说,不仅可以像传递参数一样把一个函数传递给另一个函数,而且可以将一个函数作为另一个函数的结果返回
function callSomeFunction(someFunction,someArgument) {
return someFunction(someArgument);
}
这个函数接收两个参数。第一个参数是一个函数,第二个参数是要传递给该函数的一个值,例:
function add10(num) {
return num + 10;
}
var results1 = callSomeFunction(add10,10);
alert(result1); //20
function getGreeting(name) {
return "Hello," + name;
}
var result2 = callSomeFunction(getGreeting, 'Nicholas');
alert(result2) //"Hello,Nicholas"
这里的callSomeFunction()函数是通用的,即无论第一个参数中传递进来的是什么函数,它都会返回执行第一个参数后的结果。还记得吧,要访问函数的指针而不执行函数的话,必须去掉函数名后面的那对圆括号。因此上边例子中传递给callSomeFunction()的是add10 和getGreeting,而不是执行它们之后的结果。
当然,可以从一个函数中返回另一个函数,这是一种极为有用的技术。例如,假如有一个对象数组(数组对象),我们想要根据某个对象属性对数组进行排序。而传递给数组sort()方法的比较函数要接收两个参数,即要比较的值。可是,我们需要一种方式来指明按照哪个属性来排序。要解决这个问题,可以定义一个函数,它接收一个属性名,然后根据这个属性名来创建一个比较函数,下面就是这个函数的定义。
function createComparisonFunction(propertyName) {
return function (object1, object2) {
var value1 = object1[propertyName];
var value2 = object2[propertyName];
if (value1 < value2) {
return -1;
} else if (value1 > value2) {
return 1;
} else {
return 0;
}
};
}
这个函数定义看起来有点复杂,但实际上无非就是在一个函数中嵌套了另一个函数,而且内部函数前面加了一个 return 操作符。在内部函数接收到 propertyName 参数后,它会使用方括号表示法来取得给定属性的值。取得了想要的属性值之后,定义比较函数就非常简单了。上面这个函数可以像在下面例子中这样使用。
var data = [{ name: "Zachary", age: 28 }, { name: "Nicholas", age: 29 }];
data.sort(createComparisonFunction("name"));
alert(data[0].name); //Nicholas
data.sort(createComparisonFunction("age"));
alert(data[0].name); //Zachary
这里,我们创建了一个包含两个对象的数组 data。其中,每个对象都包含一个 name 属性和一个age 属性。在默认情况下,sort()方法会调用每个对象的 toString()方法以确定它们的次序;但得到的结果往往并不符合人类的思维习惯。因此,我们调用createComparisonFunction(“name”)方法创建了一个比较函数,以便按照每个对象的 name 属性值进行排序。而结果排在前面的第一项是 name为"Nicholas",age 是 29 的对象。然后,我们又使用了 createComparisonFunction(“age”)返回的比较函数,这次是按照对象的 age 属性排序。得到的结果是 name 值为"Zachary",age 值是 28 的对象排在了第一位。
函数内部属性
在函数内部,有两个特殊的对象:arguments和this。其中,arguments是一个类数组对象,包含着传入函数中的所有参数。arguments的主要用途是保存函数参数,还有一个名叫callee的属性,该属性是一个指针,指向拥有这个arguments对象的函数,例如下面的阶乘函数。
function factorial(num) {
if (num <= 1) {
return 1;
} else {
return num * factorial(num - 1)
}
}
定义阶乘函数一般都要用到递归算法;如上面的代码所示,在函数有名字,而且名字以后也不会变得情况下,这样定义没有问题。但问题是这个函数的执行与函数名factorical紧紧耦合在了一起。为了消除这种紧密耦合的现象,可以像下面这样使用arguments.callee。
if (num <= 1) {
return 1;
} else {
return num * arguments.callee(num - 1)
}
}
函数的名字仅仅是一个包含指针的变量而已
ECMAScript 5 也规范化了另一个函数对象的属性:caller。这个属性中保存着调用当前函数的函数的引用,如果是在全局作用域中调用当前函数,它的值为 null。例如:
function outer() {
inner();
}
function inner() {
alert(inner.caller); //ƒ outer() {inner();}
}
outer();
以上代码会导致警告框中显示 outer()函数的源代码。因为 outer()调用了 inter(),所以inner.caller 就指向 outer()。为了实现更松散的耦合,也可以通过 arguments.callee.caller来访问相同的信息。
function outer() {
inner();
}
function inner() {
alert(arguments.callee.caller);
}
outer();
函数属性和方法
ECMAScript中的函数是对象,因此函数也有属性和方法。每个函数都包含两个属性:length和prototype。其中,length属性表示函数希望接收的命名参数的个数,如下面的例子所示。
function sayName(name) {
alert(name);
}
function sum(num1, num2) {
return num1 + num2;
}
function sayHi() {
alert('hi');
}
alert(sayName.length); //1
alert(sum.length); //2
alert(sayHi.length); //0
每个函数都包含两个非继承而来的方法:apply()和call()。这两个方法的用途都是在特定的作用域中调动函数,实际上等于设置函数体内this对象的值。首先,apply()方法接收两个参数:一个是在其中运行函数的作用域,另一个是参数数组。其中,第二个参数可以是Array的实例,也可以是arguments对象。例如:
function sum(num1, num2) {
return num1 + num2;
}
function callSum1(a, b) {
return sum.apply(this, arguments);
}
function callSum2(c, d) {
return sum.apply(this, [c, d])
}
alert(callSum1(10, 10));
alert(callSum2(10, 20));
在上面这个例子中,callSum1()在执行sum()函数时传入了this作为this值(因为是在全局作用域中调用的恶,所以传入的就是window对象)和arguments对象。
call()方法与apply()方法的作用相同,区别在意接收参数的方式不同。call()方法第一个参数是this值没有变化,变化的是其余参数都直接传递给函数。换句话说,在使用call()方法时,传递给函数的参数必须逐个列举出来,例如:
function sum(num1,num2) {
return num1 + num2;
}
function callSum(a,b) {
return sum.call(this,a,b);
}
alert(callSum(10,10)); //20
事实上,传递参数并非apply()和call()真正的用武之地;它们真正强大的地方是能够扩充函数赖以运行的作用域。例如:
window.color = "red";
var o = {color: 'blue'};
function sayColor(){
alert(this.color);
}
sayColor(); //red
sayColor.call(this); //red
sayColor.call(window); //red
sayColor.call(o); //blue
sayColor.call(this)和 sayColor.call(window),则是两
种显式地在全局作用域中调用函数的方式,结果当然都会显示"red"。当运行 sayColor.call(o)时,函数的执行环境就不一样了,因为此时函数体内的 this 对象指向了 o,于是结果显示的是"blue"。就是在o中调用sayColor函数或者是先将 sayColor()函数放到了对象 o 中,然后再通过 o 来调用它的
bind()方法这个方法会创建一个函数的实例,其this值会被绑定到传给bind()函数的值。例如:
window.color = "red";
var o = {color: 'blue'};
function sayColor() {
alert(this.color);
}
var objectSayColor = sayColor.bind(o);
objectSayColor(); //blue
sayColor()调用 bind()并传入对象 o,创建了 objectSayColor()函数。objectSayColor()函数的 this 值等于 o,因此即使是在全局作用域中调用这个函数,也会看到"blue"。
基本包装类型
略
单体内置对象
略
总结
对象在 JavaScript 中被称为引用类型的值,而且有一些内置的引用类型可以用来创建特定的对象,现简要总结如下:
- 引用类型与传统面向对象程序设计中的类相似,但实现不同;
- Object 是一个基础类型,其他所有类型都从 Object 继承了基本的行为;
- Array 类型是一组值的有序列表,同时还提供了操作和转换这些值的功能;
- Date 类型提供了有关日期和时间的信息,包括当前日期和时间以及相关的计算功能;
- RegExp 类型是 ECMAScript 支持正则表达式的一个接口,提供了最基本的和一些高级的正则表达式功能。
函数实际上是 Function 类型的实例,因此函数也是对象;而这一点正是 JavaScript 最有特色的地方。由于函数是对象,所以函数也拥有方法,可以用来增强其为。
因为有了基本包装类型,所以 JavaScript 中的基本类型值可以被当作对象来访问。三种基本包装类型分别是:Boolean、Number 和 String。以下是它们共同的特征:
- 每个包装类型都映射到同名的基本类型;
- 在读取模式下访问基本类型值时,就会创建对应的基本包装类型的一个对象,从而方便了数据操作;
- 操作基本类型值的语句一经执行完毕,就会立即销毁新创建的包装对象。