第05章 - 引用类型

5.1 Object

有两种方法创建 Object 实例,一种是 new ,一种是字面量方法。

new 方法:

var person = new Object();
person.name = "Nicholas";
person.age = 29;

字面量方法:注意,用字面量创建对象不会调用 Object 的构造函数

var person = {
    name : "Nicholas",
    age : 29
};

属性名也可以用字符串,也可以是数字,如果用数字将会自动被转换为字符串类型

var person = {
    "name" : "Nicholas",
    "age" : 29,
    5: true
};

属性除了可以用点号访问外,还可以用方括号访问

alert(person["name"]); //"Nicholas"
alert(person.name); //"Nicholas"

使用方括号访问属性的好处是:可以使用变量,同时可以访问不规范的属性名

//使用变量
var propertyName = "name";
alert(person[propertyName]); //"Nicholas"

//不规范的属性名
person["first name"] = "Nicholas";

5.2 Array 类型

创建 Array

Array 也有两种创建方法,一种是 new 方法,一种是字面量方法

new 方法如下

//创建空数组
var colors = new Array();

//创建指定长度的数组
var colors = new Array(20);

//创建包含三个元素的数组
var colors = new Array("red", "blue", "green");

如果只传给构造函数一个参数,则分为两种情况:

  • 参数为数字时,创建指定长度数组
  • 参数为其它类型时,创建包含一个元素的数组
//创建长度为 3 的数组
var colors = new Array(3); 

//创建包含一个元素的数组
var names = new Array("Greg"); 

也可以省略 new 操作符

//创建长度为 3 的数组
var colors = Array(3); 

//创建包含一个元素的数组
var names = Array("Greg"); 

字面量创建数组的方法如下

var colors = ["red", "blue", "green"]; 
var names = [];

处理逗号的时候要小心!

下面的代码在 IE 浏览器下包含 3 个元素,在其它浏览器下只包含 2 个元素

var values = [1,2,];

同样,下面的代码在 IE 浏览器下包含 6 个 undefined ,而在其它浏览器下只有 5 个 undefined

var options = [,,,,,];

length 属性

length 属性保存 Array 的长度

var colors = ["red", "blue", "green"]; 
var names = []; 

alert(colors.length); //3
alert(names.length); //0

length 是可以设置的,缩小数组后,一部分元素将被丢弃

var colors = ["red", "blue", "green"]; 

//缩小数组
colors.length = 2;

//最后一个元素被丢弃
alert(colors[2]); //undefined

增大数组后,新增的元素都是 undefined

var colors = ["red", "blue", "green"]; 


var colors = ["red", "blue", "green"]; 

//扩大数组
colors.length = 4;

//新增元素为 undefined
alert(colors[3]); //undefined

可以利用 length 属性方便的在数组尾部添加元素

var colors = ["red", "blue", "green"]; 

//在位置 3 添加元素
colors[colors.length] = "black"; //add a color (position 3)

//在位置 4 添加元素
colors[colors.length] = "brown"; //add another color (position 4)

访问 Array 中的元素

用索引访问 Array 中的元素

var colors = ["red", "blue", "green"]; 

alert(colors[0]); 
colors[2] = "black"; 
colors[3] = "brown";

读取越界的索引将会返回 undefined ,设置越界元素将会扩大数组


var colors = ["red", "blue", "green"]; 

colors[99] = "black"; 
alert(colors.length); //100

5.2.1 检测数组

Array.isArray 方法检测一个对象是不是数组

if (Array.isArray(value)){
    //do something on the array
}

5.2.2 转换方法

有四种转换方法:

  • toString:返回数组的字符串形式,每个元素之间用逗号分隔。会调用每个元素的 toString 方法
  • toLocaleString:返回数组的字符串形式,每个元素之间用逗号分隔。会调用每个元素的 toLocaleString 方法
  • valueOf:原样返回数组
  • join:指定一个分隔符号
> arr=[1,2,3]
[ 1, 2, 3 ]

> arr.toString()
'1,2,3'

> arr.toLocaleString()
'1,2,3'

> arr.valueOf()
[ 1, 2, 3 ]

> arr.join('-')
'1-2-3'

可以重写对象的 toString()toLocaleString() 方法


var person1 = {
    toLocaleString : function () {
        return "Nikolaos";
    },

    toString : function() {
        return "Nicholas";
    }
};

var person2 = {
    toLocaleString : function () {
        return "Grigorios";
    },

    toString : function() {
        return "Greg";
    }
};

var people = [person1, person2];
alert(people); //Nicholas,Greg
alert(people.toString()); //Nicholas,Greg
alert(people.toLocaleString()); //Nikolaos,Grigorios

5.2.3 栈方法

有两个栈方法

  • push:将数据压入数组尾部,返回当前数组的长度。可以一次压入多个数据。
  • pop:弹出数组尾部的最后一个数据

结合 push 和 pop 可以模拟一个"尾部进,尾部出"栈结构

var colors = new Array(); //create an array

var count = colors.push("red", "green"); //push two items
alert(count); //2

count = colors.push("black"); //push another item on
alert(count); //3

var item = colors.pop(); //get the last item
alert(item); //"black"

alert(colors.length); //2

可以结合数组的 length 属性,以及数组自动扩大的性质使用栈方法

var colors = ["red", "blue"];

colors.push("brown"); //add another item

colors[3] = "black"; //add an item
alert(colors.length); //4

var item = colors.pop(); //get the last item
alert(item); //"black"

5.2.4 队列方法

shift 方法从数组的头部取出一个元素。

结合 push 方法和 shift 方法,可以模拟一个"尾部进,头部出"的队列结构

var colors = new Array(); //create an array

var count = colors.push("red", "green"); //push two items
alert(count); //2

count = colors.push("black"); //push another item on
alert(count); //3

var item = colors.shift(); //get the first item
alert(item); //"red"

alert(colors.length); //2

unshift 方法将数据从头部压入数组。结合 unshiftpop 方法可以模拟一个"头部进,尾部出"的反向队列

var colors = new Array(); //create an array

var count = colors.unshift("red", "green"); //push two items
alert(count); //2

count = colors.unshift("black"); //push another item on
alert(count); //3

var item = colors.pop(); //get the first item
alert(item); //"green"
alert(colors.length); //2

5.2.5 排序

reverse 反转数组的顺序

var values = [1, 2, 3, 4, 5];

values.reverse();
alert(values); //5,4,3,2,1

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

下面是按数值比较大小,降序排列

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

提示:sort 方法和 reverse 方法会改变原来的数组,同时也返回改变后的数组

> arr === arr.reverse()
true

5.2.6 操作方法

concat 方法合并数组,他会返回一个新数组

  • 如果没有参数,则创建原来数组的副本,并返回
  • 如果有一个或者多个参数,则将这些数据合并到原来的数组的副本中,并返回
var colors = ["red", "green", "blue"];

var colors2 = colors.concat("yellow", ["black", "brown"]);

alert(colors); //red,green,blue
alert(colors2); //red,green,blue,yellow,black,brown

slice 方法切分数组,他也会返回一个新数组

  • 如果只有一个参数,则返回从该索引处,到最后一个元素的切片
  • 如果有两个参数,则返回从第一个索引处,到第二个索引处之间的切片(不包括第二个索引位置)
var colors = ["red", "green", "blue", "yellow", "purple"];

var colors2 = colors.slice(1);
var colors3 = colors.slice(1,4);

alert(colors2); //green,blue,yellow,purple
alert(colors3); //green,blue,yellow

splice 方法可以删除数组中的元素,也可以向数组的任意位置插入元素。注意:该方法返回被删除的切片,同时原始数组被删除或插入元素

  • 当接受两个参数时,第一个参数是删除开始的索引位置,第二个参数是删除的个数
  • 当接收多个参数时,从第三个参数开始,都是要插入的数据
var colors = ["red", "green", "blue"];

var removed = colors.splice(0,1); //remove the first item
alert(colors); //green,blue
alert(removed); //red - one item array


removed = colors.splice(1, 0, "yellow", "orange"); //insert two items at position 1
alert(colors); //green,yellow,orange,blue
alert(removed); //empty array

removed = colors.splice(1, 1, "red", "purple"); //insert two values, remove one
alert(colors); //green,red,purple,orange,blue
alert(removed); //yellow - one item array

5.2.7 位置方法

位置方法可以查找元素在数组中的位置,如果没找到就返回 -1 ;可以从头开始查找,也可以从尾部开始查找

  • indexOf 从头部开始查找
  • lastIndexOf 从尾部开始查找
  • 如果给出第二个参数,则从指定位置开始查找
  • 位置方法使用全等来确定是不是要查找的元素
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

5.2.8 迭代方法

有五种迭代方法

  • every 接收一个函数,测试每一个元素,如果所有元素都返回 true ,则 every 返回 true
  • some 接收一个函数,测试每一个元素,只要有一个元素返回 true ,则 some 返回 true
  • map 接收一个函数,应用到所有元素上,返回值组成一个新的数组返回
  • filter 接收一个函数,应用到所有元素上,将能返回 true 的元素组成一个新数组返回
  • forEach 接收一个函数,应用到所有元素上,只接受该原始数组,forEach 自己没有返回值

下面是 everysome 的例子

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

//------------------------

var someResult = numbers.some(function(item, index, array){
    return (item > 2);
});
alert(someResult); //true

下面是 filter 的例子

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 的例子

var numbers = [1,2,3,4,5,4,3,2,1];

numbers.forEach(function(item, index, array){
    //do something here
});

5.2.9 归约函数

有两个归约函数

  • reduce 从左向右归约
  • reduceRight 从右向左归约

这两个函数都接受两个参数,第一个参数是一个函数;第二个参数可以设置一个初值,可以省略

var values = [1,2,3,4,5];

var sum = values.reduce(function(prev, cur, index, array){
    return prev + cur;
});

alert(sum); //15

从右向左只是方向相反而已,其他都是一样的

var values = [1,2,3,4,5];

var sum = values.reduceRight(function(prev, cur, index, array){
    return prev + cur;
});

alert(sum); //15

5.3 Date 类型

可以不传递任何参数,直接创建一个"当前时间"的日期对象

> new Date()
Sat Jan 02 2016 20:25:20 GMT+0800 (中国标准时间)

也可以传入一个从 1970 年 1 月 1 日到指定时间的毫秒数,从而创建一个"指定时间"的日期对象

获得"指定时间"的毫秒数

Date.parse 和 Date.UTC 这两个方法可以返回指定时间的毫秒数

Date.parse 函数

Date.parse 方法接收一个时间字符串,格式规定如下:

  • "月/日/年" (6/13/2004)
  • "英文月名 日, 年" (January 12, 2004)
  • "英文星期名 英文月名 日 年 小时:分钟:秒 时区" (Tue May 25 2004 00:00:00 GMT-0700)
  • "YYYY-MM-DDTHH:mm:ss.sssZ" (2004-05-25T00:00:00)
> Date.parse("May 25, 2004")
1085414400000

> new Date(Date.parse("May 25, 2004"))
Tue May 25 2004 00:00:00 GMT+0800 (中国标准时间)

也可以直接像 Date 构造函数传递时间字符串,省略获取毫秒数这一步

> new Date("May 25, 2004")
Tue May 25 2004 00:00:00 GMT+0800 (中国标准时间)

Date.UTC 函数

Date.UTC 函数接收七个参数,只有前两个参数是必需的,其它参数如果省略则都为 0

  • 月(0 到 11)
  • 日(1 到 31)
  • 小时(0 到 23)
  • 分钟
  • 毫秒
> Date.UTC(2000,0)
946684800000

> new Date(Date.UTC(2000,0))
Sat Jan 01 2000 08:00:00 GMT+0800 (中国标准时间)

同样可以省略获取毫秒数这一步,直接将参数传给 Date 构造函数

> new Date(2000,0)
Sat Jan 01 2000 00:00:00 GMT+0800 (中国标准时间)

获取当前时间的毫秒数

Date.now 获取当前时间的毫秒数

> Date.now()
1451739376823

可以利用这个函数计算程序的运算时间

//get start time
var start = Date.now();

//call a function
doSomething();

//get stop time
var stop = Date.now(),

result = stop – start;

5.3.1 继承的方法

Date 对象重写了 toString toLocaleStringvalueOf 这三个方法

toStringtoLocaleString 方法返回时间字符串,但是各个浏览器返回值不一样,因此用途不大。

valueOf 返回毫秒数,当两个时间对象比较大小时,会调用这个方法

var date1 = new Date(2007, 0, 1); //"January 1, 2007"
var date2 = new Date(2007, 1, 1); //"February 1, 2007"

alert(date1 < date2); //true
alert(date1 > date2); //false

5.3.2 日期格式化方法

有好几个日期格式化方法,但是各个浏览器都不兼容,标准推荐使用 toUTCString 方法

  • toDateString
  • toTimeString
  • toLocaleDateString
  • toLocaleTimeString
  • toUTCString

5.3.3 其他方法

方法 说明
getTime() 与 valueOf 返回值相同
setTime(milliseconds) 重新设置毫秒数,会改变时间对象
年 ------------------
getFullYear() 4位数年份
getUTCFullYear() 4位数年份
setFullYear(year) 4位数年份
setUTCFullYear(year) 4位数年份
月 ------------------
getMonth() 从 0 到 11
getUTCMonth()
setMonth(month) 超过 11 则增加年份
setUTCMonth(month)
日 ------------------
getDate() 从 1 到 31
getUTCDate()
setDate(date) 超过月份的最大天数则增加月份
setUTCDate(date)
星期 ------------------
getDay() 从 0 到 6 ,0 表示星期日
getUTCDay()
小时 ------------------
getHours() 从 0 到 23
getUTCHours()
setHours(hours) 超过 23 则增加日期
setUTCHours(hours)
分钟 ------------------
getMinutes() 从 0 到 59
getUTCMinutes()
setMinutes(minutes) 超过 59 则增加小时数
setUTCMinutes(minutes)
秒数 ------------------
getSeconds() 从 0 到 59
getUTCSeconds()
setSeconds(seconds) 超过 59 则增加分钟数
setUTCSeconds(seconds)
毫秒 ------------------
getMilliseconds()
getUTCMilliseconds()
setMilliseconds(milliseconds)
setUTCMilliseconds(milliseconds)
时区 ------------------
getTimezoneOffset() 返回与标准时间相差的分钟数,东八区返回 -480

5.4 RegExp 类型

正则表达式包括模式部分和标志部分。下面使用字面量的方法创建模式

var expression = /pattern/flags;

有三种标志

  • i:忽略大小写
  • g:全局模式,即不是遇到第一个匹配就停止
  • m:多行模式
//匹配所有的 at
var pattern1 = /at/g;

//匹配第一个 bat 或者 cat,不区分大小写
var pattern2 = /[bc]at/i;

//匹配所有的以 at 结尾的三字符组合,不区分大小写
var pattern3 = /.at/gi;

模式中有些符号需要转义
( [ { \ ^ $ | ) ] } ? * + .

//匹配第一个 bat 或 cat,不区分大小写
var pattern1 = /[bc]at/i;

//匹配第一个 [bc]at,不区分大小写
var pattern2 = /\[bc\]at/i;

//匹配所有的以 at 结尾的三字符组合,不区分大小写
var pattern3 = /.at/gi;

//匹配所有的 .at ,不区分大小写
var pattern4 = /\.at/gi;

下面使用 RexExp 构造函数来创建模式。RexExp 构造函数接收两个字符串参数,第一个是模式,第二个是标志。

//匹配第一个 bat 或者 cat,不区分大小写
var pattern1 = /[bc]at/i;

//使用构造函数创建模式,与上面的字面量模式相同
var pattern2 = new RegExp("[bc]at", "i");

传给构造函数的的模式参数中,有些不能直接出现在字符串中的字符也需要转义

字面量模式 字符串模式
/\[bc\]at/ "\\[bc\\]at"
/\.at/ "\\.at"
/name\/age/ "name\\/age"
/\d.\d{1,2}/ "\\d.\\d{1,2}"
/\w\\hello\\123/ "\\w\\\\hello\\\\123"

在老版本浏览器中,相同的字面量模式会共享同一个实例,而相同的构造函数模式会创建不同的实例

var re = null,
    i;

//共享一个实例。因此,第一次查找后,第二次查找将从第一次的位置之后开始
for (i=0; i < 10; i++){
    re = /cat/g;
    re.test("catastrophe");
}

//每次循环都是一个新实例,因此每次都从头开始查找
for (i=0; i < 10; i++){
    re = new RegExp("cat", "g");
    re.test("catastrophe");
}

新版浏览器已经改进了这个问题,字面量模式每次也会创建新的实例

5.4.1 RegExp 的属性

通过 RegExp 对象的属性可以了解正则表达式的一些信息

  • global — 布尔值,是否设置了全局标志
  • ignoreCase — 布尔值,是否设置了忽略大小写标志
  • lastIndex — 下一次搜索开始的位置,从 0 开始
  • multiline — 布尔值,是否设置了多行标志
  • source — 返回规范的字面量正则表达式
var pattern1 = /\[bc\]at/i;

alert(pattern1.global); //false
alert(pattern1.ignoreCase); //true
alert(pattern1.multiline); //false
alert(pattern1.lastIndex); //0
alert(pattern1.source); //"\[bc\]at"


var pattern2 = new RegExp("\\[bc\\]at", "i");

alert(pattern2.global); //false
alert(pattern2.ignoreCase); //true
alert(pattern2.multiline); //false
alert(pattern2.lastIndex); //0
alert(pattern2.source); //"\[bc\]at"

从上面的代码可以看到,字面量模式和构造函数模式最后返回的 source 属性是相同的范字面量正则表达式

5.4.2 RegExp 的方法

exec 方法

exec 方法执行匹配动作,接收一个文本参数

  • 如果没有任何匹配项则返回 null
  • 如果有匹配项则返回一个数组

返回数组有两个额外属性

  • index:匹配项在文本中的位置
  • input:被搜索的文本

返回数组中

  • 第一个元素是整个模式的匹配字符串
  • 其他元素则是组匹配
  • 如果没有组匹配,则数组只包含一个元素
var text = "mom and dad and baby";
var pattern = /mom( and dad( and baby)?)?/gi;

var matches = pattern.exec(text);

alert(matches.index); //0
alert(matches.input); //"mom and dad and baby"
alert(matches[0]); //"mom and dad and baby"
alert(matches[1]); //" and dad and baby"
alert(matches[2]); //" and baby"

exec 方法可以执行多次

  • 如果没有使用全局标志,则每次都返回第一个匹配项
  • 如果使用了全局标志,则每次返回下一个匹配项
var text = "cat, bat, sat, fat";

//没有使用全局标志
var pattern1 = /.at/;

var matches = pattern1.exec(text);
alert(matches.index); //0
alert(matches[0]); //cat
alert(pattern1.lastIndex); //0

matches = pattern1.exec(text);
alert(matches.index); //0
alert(matches[0]); //cat
alert(pattern1.lastIndex); //0

//使用全局标志
var pattern2 = /.at/g;

var matches = pattern2.exec(text);
alert(matches.index); //0
alert(matches[0]); //cat
alert(pattern2.lastIndex); //0

matches = pattern2.exec(text);
alert(matches.index); //5
alert(matches[0]); //bat
alert(pattern2.lastIndex); //8

test 方法

test 方法仅仅测试是否能够匹配,返回布尔值

var text = "000-00-0000";
var pattern = /\d{3}-\d{2}-\d{4}/;

if (pattern.test(text)){
    alert("The pattern was matched.");
}

toString 和 toLocaleString 方法

都返回字面量模式

var pattern = new RegExp("\\[bc\\]at", "gi");

alert(pattern.toString()); // /\[bc\]at/gi
alert(pattern.toLocaleString()); // /\[bc\]at/gi

valueOf

返回模式对象自己

5.4.3 RegExp 静态属性

RegExp 中包含一些静态属性,保存最近一次运行时的一些信息

属性名 短属性名 说明
input $_ 最近一次要匹配的字符串
lastMatch $& 最近一次的匹配项
lastParen $+ 最近一次的匹配组
leftContext $` input 字符串中 lastMatch 之前的文本
multiline $* 是否所有表达时都使用多行模式
rightContext $’ input 字符串中 lastMatch 之后的文本

下面是使用长属性名的例子

var text = "this has been a short summer";
var pattern = /(.)hort/g;

/*
* 注意:这些属性老旧浏览器都不支持
*/
if (pattern.test(text)){
    alert(RegExp.input); //this has been a short summer
    alert(RegExp.leftContext); //this has been a
    alert(RegExp.rightContext); // summer
    alert(RegExp.lastMatch); //short
    alert(RegExp.lastParen); //s
    alert(RegExp.multiline); //false
}

下面是使用短属性名的例子

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["$`"]); //this has been a
    alert(RegExp["$’"]); // summer
    alert(RegExp["$&"]); //short
    alert(RegExp["$+"]); //s
    alert(RegExp["$*"]); //false
}

还有多达 9 个属性用来存储捕获组

var text = "this has been a short summer";
var pattern = /(..)or(.)/g;

if (pattern.test(text)){
    alert(RegExp.$1); //sh
    alert(RegExp.$2); //t
}

5.5.4 模式的局限性

Javascript 相比其他语言的正则表达式,有一定的局限性,但是也很强大,完全够用。

5.5 Function 类型

函数本质上是个对象,有三种方法定义函数:

  • 常规定义
function sum (num1, num2) {
    return num1 + num2;
}
  • 表达式定义
var sum = function(num1, num2){
    return num1 + num2;
};
  • 构造函数定义:前面的参数是函数参数签名,最后一个参数是函数体。这种方法不常用。
var sum = new Function("num1", "num2", "return num1 + num2"); //not recommended

函数的名字仅仅是个指针,因此一个函数对象可以有多个名字

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

5.5.1 没有重载(深入理解)

因为函数名就是个指针,因此一旦重载函数,函数名就会指向新的函数对象,所以无法像其他语言那样重载函数名。

function addSomeNumber(num){
    return num + 100;
}

function addSomeNumber(num) {
    return num + 200;
}

var result = addSomeNumber(100); //300

5.5.2 函数声明与函数表达式

Javascript 解析器首先会扫描所有的函数声明,然后再解析其他表达式,因此函数声明会被提升,即可以先使用后声明。

alert(sum(10,10));

function sum(num1, num2){
    return num1 + num2;
}

而用表达式定义的函数不能这样使用


//会报错~~~~
alert(sum(10,10));

var sum = function(num1, num2){
    return num1 + num2;
};

5.5.3 作为值的函数

函数名字可以作为函数的参数传入


//第一个参数传入一个函数
function callSomeFunction(someFunction, someArgument){
    return someFunction(someArgument);
}

//===================================

function add10(num){
    return num + 10;
}

var result1 = callSomeFunction(add10, 10);
alert(result1); //20

//===================================

function getGreeting(name){
    return "Hello, " + name;
}

var result2 = callSomeFunction(getGreeting, "Nicholas");
alert(result2);  //"Hello, Nicholas"

函数也可以作为返回值返回

//根据参数不同,返回的排序函数也会有所不同
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;
        }
    };
}

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

5.5.4 函数内部属性

argumnets.callee 属性

因为在 Javascript 中函数名仅仅是一个指针,因此,如果在递归中使用函数名可能会导致问题。


// 求阶乘的函数:使用函数名递归调用自身
function factorial(num){
    if (num <= 1) {
        return 1;
    } else {
        return num * factorial(num-1)
    }
}


//用另外一个名字指向递归函数
var trueFactorial = factorial;

//原先的名字变成另外一个函数
factorial = function(){
    return 0;
};

//调用会发生错误
console.info(trueFactorial(5)); //0

argumnets.callee 属性则代表函数对象自身,使用这个属性可以避免上述问题的发生

// 求阶乘的函数:使用 arguments.callee 属性递归调用自身
function factorial(num){
    if (num <= 1) {
        return 1;
    } else {
        return num * arguments.callee(num-1)
    }
}


//用另外一个名字指向递归函数
var trueFactorial = factorial;

//原先的名字变成另外一个函数
factorial = function(){
    return 0;
};

//这次不会发生错误
console.info(trueFactorial(5)); //120

函数内部的 this 对象

在函数内部还有一个 this 对象,这个对象代表调用函数的那个对象,他是动态可变的,定义函数的时候不需要知道他是谁。


//这个函数里面的 this 指向谁现在并不清楚
function sayColor(){
    alert(this.color);
}


window.color = "red";
var o = { color: "blue" };

//全局调用,this 指向 window
sayColor(); //"red"

//这里 this 指向 o
o.sayColor = sayColor;
o.sayColor(); //"blue"

函数的 caller 属性

caller 属性指向调用当前函数的外部函数


function outer(){
    inner();
}

//打印外部函数
function inner(){
    alert(inner.caller);
}

outer();

更好的方法是不使用函数自己的名字,而是用 arguments.callee


function outer(){
    inner();
}

//打印外部函数
function inner(){
    alert(arguments.callee.caller);
}

outer();

5.5.5 函数的属性和方法

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 方法

apply 和 call 方法配合 this 指针,可以动态为函数指定所属对象。两个方法的区别不大,唯一的区别是参数列表组织方式不一样。


window.color = "red";

var o = { color: "blue" };

//现在 this 没有指定
function sayColor(){
    alert(this.color);
}

sayColor(); //red

//this 指向 window
sayColor.call(this); //red
sayColor.call(window); //red

//this 指向 o
sayColor.call(o); //blue

bind 方法

bind 方法将函数的 this 指针绑定到一个明确的对象,然会返回这个新函数函数

window.color = "red";
var o = { color: "blue" };

function sayColor(){
    alert(this.color);
}

//返回绑定到对象 o 的函数
var objectSayColor = sayColor.bind(o);
objectSayColor(); //blue

5.6 基本包装类型

有三种包装器类型,一般不直接使用,只在后台供解析器使用。

  • Boolean
  • Number
  • String

每当代码调用"原生类型"的对象方法或属性时,解析器都会在后台创建对应的包装器对象,代替原生类型,这会让我们觉得在直接使用原生类型的属性和方法。当属性和方法使用完毕后,包装器对象会被销毁。

下面的语句

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

第二条语句实际上被转换成

//转换成包装器对象
var temp = new String("some text");

//使用其中的方法
var s2 = s1.substring(2);

//销毁包装器对象
temp = null;

无法为原生类型添加属性:因为包装器对象是临时的,使用完毕后会被销毁,为其添加的属性同样会销毁

var s1 = "some text";
s1.color = "red";
alert(s1.color); //undefined

将原生类型数据传给 Object 构造器当做参数,会到的对应的包装器对象

var obj = new Object("some text");
alert(obj instanceof String); //true

除非必须的情况下,一般不直接使用包装器对象,因为包装器对象的 typeof 运算总是返回 object ,而原生类型会返回明确的信息,二者在逻辑上有冲突。

var value = "25";

var number = Number(value); //转型函数
alert(typeof number); //"number"

var obj = new Number(value); //包装对象构造器
alert(typeof obj); //"object"

5.6.1 Boolean 类型

永远不要使用 Boolean 包装器,因为所有对象的真值为都是 True ,即使逻辑上是一个 False 值

var falseObject = new Boolean(false);
var result = falseObject && true;
alert(result); //true

var falseValue = false;
result = falseValue && true;
alert(result); //false

而且 typeof 运算和 instanceof 运算返回的值也不一样

alert(typeof falseObject); //object
alert(typeof falseValue); //boolean

alert(falseObject instanceof Boolean); //true
alert(falseValue instanceof Boolean); //false

5.6.2 Number 类型

Number 类型重写了几个常见的方法:

  • valueOf:返回原生数据
  • toString:返回字符串形式
  • toLocaleString:返回字符串形式

toFixed 方法:指定小数位数

var num = 10;
alert(num.toFixed(2)); //"10.00"

var num = 10.005;
alert(num.toFixed(2)); //"10.01"

toExponential:科学计数法表示

var num = 10;
alert(num.toExponential(1)); //"1.0e+1"

toPrecision:设置显示精度

var num = 99;

alert(num.toPrecision(1)); //"1e+2"
alert(num.toPrecision(2)); //"99"
alert(num.toPrecision(3)); //"99.0"

一般情况下也不应使用 Number 包装器,原因也是 typeof 测试和 instanceof 测试会出现问题

var numberObject = new Number(10);
var numberValue = 10;

alert(typeof numberObject); //"object"
alert(typeof numberValue); //"number"

alert(numberObject instanceof Number); //true
alert(numberValue instanceof Number); //false

5.6.3 String 类型

String 对象的 valueOf 、toString、toLocaleString 这三个方法都返回字符串原生数据

length 属性

返回字符串的长度

var stringValue = "hello world";
alert(stringValue.length); //"11"

获取指定位置的字符

  • charAt:获取指定位置的字符
  • charCodeAt:获取指定位置的字符编码
var stringValue = "hello world";
alert(stringValue.charAt(1)); //"e"

var stringValue = "hello world";
alert(stringValue.charCodeAt(1)); //outputs "101"

也可以将字符串看做是一个字符数组,直接索引字符

var stringValue = "hello world";
alert(stringValue[1]); //"e"

拼接字符串

concat 方法用来拼接字符串

var stringValue = "hello ";
var result = stringValue.concat("world");

alert(result); //"hello world"
alert(stringValue); //"hello"

concat 方法可以接受多个参数,一块拼接

var stringValue = "hello ";
var result = stringValue.concat("world", "!");


alert(result); //"hello world!"
alert(stringValue); //"hello"

更常见的用法还是使用加号

> "hello" + "world" + "!"
'helloworld!'

切分字符串

  • slice:返回两个位置之间的字符串,第二个位置必须在第一个位置右边,则返回空字符串。两个位置都可以使用负数索引。
> s.slice(5,7)
'56'
> s.slice(7,5)
''
> s.slice(5,-3)
'56'
> s.slice(-3,5)
''
> s.slice(-5,-3)
'56'
> s.slice(-3,-5)
''
  • substring:返回两个位置参数之间的字符串。负数索引都被转换为 0
> s.substring(5,7)
'56'
> s.substring(7,5)
'56'
> s.substring(5,-3)
'01234'
> s.substring(-3,5)
'01234'
> s.substring(-3,-5)
''
  • substr:第一个参数指定切分位置,第二个参数指定切分数量,第一个参数接受负数,第二个参数输入负数会被转换为 0
> s.substr(5,2)
'56'
> s.substr(2,5)
'23456'
> s.substr(5,-2)
''
> s.substr(-5,2)
'56'
> s.substr(-5,-2)
''

索引和定位某个字符

使用 indexOf 和 lastIndexOf 这两个方法

var stringValue = "hello world";

alert(stringValue.indexOf("o")); //4
alert(stringValue.lastIndexOf("o")); //7

第二个参数可以指定搜索起始位置

var stringValue = "hello world";

alert(stringValue.indexOf("o", 6)); //7
alert(stringValue.lastIndexOf("o", 6)); //4

多次调用,并且每次改变第二个参数的,可以找到所有指定字符的位置

var stringValue = "Lorem ipsum dolor sit amet, consectetur adipisicing elit";
var positions = new Array();

var pos = stringValue.indexOf("e");

while(pos > -1){
    positions.push(pos);
    pos = stringValue.indexOf("e", pos + 1);
}

alert(positions); //"3,24,32,35,52"

删除前缀及后缀空格

使用 trim trimLeft trimRight 这三个方法,返回新字符串,原是字符串不变

var stringValue = " hello world ";

var trimmedStringValue = stringValue.trim();

alert(stringValue); //" hello world "
alert(trimmedStringValue); //"hello world"

大小写转换

使用 toUpperCase、toLocaleUpperCase、toLowerCase、toLocaleLowerCase 这几个方法

var stringValue = "hello world";

alert(stringValue.toLocaleUpperCase()); //"HELLO WORLD"
alert(stringValue.toUpperCase()); //"HELLO WORLD"
alert(stringValue.toLocaleLowerCase()); //"hello world"
alert(stringValue.toLowerCase()); //"hello world

正则匹配

var text = "cat, bat, sat, fat";
var pattern = /.at/;

//same as pattern.exec(text)
var matches = text.match(pattern);

alert(matches.index); //0
alert(matches[0]); //"cat"
alert(pattern.lastIndex); //0

正则搜索

返回搜索字符串的位置,如果没有找到则返回 -1

var text = "cat, bat, sat, fat";
var pos = text.search(/at/);
alert(pos); //1

正则替换

replace 方法返回一个新字符串,源字符串不变

var text = "cat, bat, sat, fat";

var result = text.replace("at", "ond");
alert(result); //"cond, bat, sat, fat"

//第一个参数是个模式
result = text.replace(/at/g, "ond");
alert(result); //"cond, bond, sond, fond"

在第二个参数里可以使用美元符号,让替换更灵活

var text = "cat, bat, sat, fat";

result = text.replace(/(.at)/g, "word ($1)");
alert(result); //word (cat), word (bat), word (sat), word (fat)
属性名 短属性名 说明
input $_ 最近一次要匹配的字符串
lastMatch $& 最近一次的匹配项
lastParen $+ 最近一次的匹配组
leftContext $` input 字符串中 lastMatch 之前的文本
multiline $* 是否所有表达时都使用多行模式
rightContext $’ input 字符串中 lastMatch 之后的文本

第二个参数可以是一个函数,函数接受三个参数

  • 第一个参数:匹配字符串
  • 第二个参数:匹配位置
  • 第三个参数:原始字符串

如果模式中有组存在,则从第二个参数开始到倒数第三个参数,传入的都是组匹配,最后两个参数不变

function htmlEscape(text){
    return text.replace(/[<>"&]/g, function(match, pos, originalText){
        switch(match){
            case "<":
            return "<";
            case ">":
            return ">";
            case "&":
            return "&";
            case "\"":
            return """;
        }
    });
}

alert(htmlEscape("<p class=\"greeting\">Hello world!</p>"));
//"<p class="greeting">Hello world!</p&gt";

切分

  • 第一个参数可以是字符串,也可以是正则表达式
  • 第二个参数可以设置返回的切分数量
var colorText = "red,blue,green,yellow";

var colors1 = colorText.split(","); //["red", "blue", "green", "yellow"]
var colors2 = colorText.split(",", 2); //["red", "blue"]
var colors3 = colorText.split(/[^\,]+/); //["", ",", ",", ",", ""]

比较字符串大小 localeCompare

var stringValue = “yellow”;
alert(stringValue.localeCompare(“brick”)); //1
alert(stringValue.localeCompare(“yellow”)); //0
alert(stringValue.localeCompare(“zoo”)); //-1

用编码组成字符串 fromCharCode

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

HTML 方法

METHOD OUTPUT
anchor(name) <a name=“name”>string</a>
big() <big>string</big>
bold() <b>string</b>
fixed() <tt>string</tt>
fontcolor(color) <font color=“color”>string</font>
fontsize(size) <font size=“size”>string</font>
italics() <i>string</i>
link(url) <a href=“url”>string</a>
small() <small>string</small>
strike() <strike>string</strike>
sub() <sub>string</sub>
sup() <sup>string</sup>

5.7 单体内置对象

5.7.1 Global 对象

全局属性和全局方法其实都是 Global 的属性和方法,例如 isNaN isFinite 等等,除此之外还有另外几个方法

URI 编码方法

  • encodeURI 方法:不会对本身属于 URI 的字符进行编码,比如斜杠、冒号、问号、井号之类的。因此这个方法可以对 URI 串进行整体编码
  • encodeURIComponent 方法:会对任何非标准字符串进行编码,因此只能对 URI 的部分串进行编码
var uri = “http://www.wrox.com/illegal value.htm#start”;

//”http://www.wrox.com/illegal%20value.htm#start”
alert(encodeURI(uri));

//”http%3A%2F%2Fwww.wrox.com%2Fillegal%20value.htm%23start”
alert(encodeURIComponent(uri));

解码是相反的过程

var uri = “http%3A%2F%2Fwww.wrox.com%2Fillegal%20value.htm%23start”;

//http%3A%2F%2Fwww.wrox.com%2Fillegal value.htm%23start
alert(decodeURI(uri));

//http://www.wrox.com/illegal value.htm#start
alert(decodeURIComponent(uri));

eval 方法

eval 方法对字符串进行求值操作

eval(“alert(‘hi’)”);
//相当于
alert(“hi”);

eval 方法中的代码与当前环境有相同的作用域链

var msg = “hello world”;
eval(“alert(msg)”); //”hello world”

同理,在 eval 中定义的对象在当前环境中也能访问(严格模式下不行)

var msg = “hello world”;
eval(“alert(msg)”); //”hello world”

Global 的属性

PROPERTY DESCRIPTION
undefined The special value undefined
NaN The special value NaN
Infinity The special value Infinity
Object Constructor for Object
Array Constructor for Array
Function Constructor for Function
Boolean Constructor for Boolean
String Constructor for String
Number Constructor for Number
Date Constructor for Date
RegExp Constructor for RegExp
Error Constructor for Error
EvalError Constructor for EvalError
RangeError Constructor for RangeError
ReferenceError Constructor for ReferenceError
SyntaxError Constructor for SyntaxError
TypeError Constructor for TypeError
URIError Constructor for URIError

window 对象

window 对象就是浏览器中的 Global 对象

var color = “red”;

function sayColor(){
    alert(window.color);
}

window.sayColor(); //”red”

如果不是在浏览器环境下,可以用下面的代码犬的 Global 对象

var global = function(){
    return this;
}();

5.7.2 Math 对象

下面是 Math 对象中一些常用的常数

PROPERTY DESCRIPTION
Math.E The value of e, the base of the natural logarithms
Math.LN10
Math.LN2
Math.LOG2E
Math.LOG10E
Math.PI
Math.SQRT1_2
Math.SQRT2

求最大最小数

var max = Math.max(3, 54, 32, 16);
alert(max); //54
var min = Math.min(3, 54, 32, 16);
alert(min); //3

如果师数组,可以用 apply 方法

var values = [1, 2, 3, 4, 5, 6, 7, 8];
var max = Math.max.apply(Math, values);

舍入方法

  • ceil :向上靠近
  • floor:向下靠近
  • round:四舍五入
alert(Math.ceil(25.9)); //26
alert(Math.ceil(25.5)); //26
alert(Math.ceil(25.1)); //26

alert(Math.round(25.9)); //26
alert(Math.round(25.5)); //26
alert(Math.round(25.1)); //25

alert(Math.floor(25.9)); //25
alert(Math.floor(25.5)); //25
alert(Math.floor(25.1)); //25

随机数

返回某个范围内的随机数


number = Math.floor(Math.random() * 可能值的总数 + 第一个数)

//返回 1 到 10 之间的随机数
var num = Math.floor(Math.random() * 10 + 1);

//返回 2 到 9 之间的随机
var num = Math.floor(Math.random() * 9 + 2);

可以将这个算法写成一个函数


function selectFrom(lowerValue, upperValue) {
    var choices = upperValue - lowerValue + 1;
    return Math.floor(Math.random() * choices + lowerValue);
}

var num = selectFrom(2,10);
alert(num); //返回 1 到 10 之间的随机数

其他方法

METHOD DESCRIPTION
Math.abs(num)
Math.exp(num)
Math.log(num)
Math.pow(num, power)
Math.sqrt(num)
Math.acos(x)
Math.asin(x)
Math.atan(x)
Math.atan2(y, x)
Math.cos(x)
Math.sin(x)
Math.tan(x)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,817评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,329评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,354评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,498评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,600评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,829评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,979评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,722评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,189评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,519评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,654评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,329评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,940评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,762评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,993评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,382评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,543评论 2 349

推荐阅读更多精彩内容

  • 第5章 引用类型(返回首页) 本章内容 使用对象 创建并操作数组 理解基本的JavaScript类型 使用基本类型...
    大学一百阅读 3,216评论 0 4
  • 我希望自己是一个有点像流动的人,给人一种很舒适的感觉,放在哪里都很合适,不会带给别人不舒服的感觉 包括对未来,我...
    冉伊沐阅读 172评论 0 0
  • 今天很不想睡觉,想和微信里面一个陌生的朋友聊天。转念一想,时间也不早了,这一聊估计要聊到凌晨了。所以,我和自己对话...
    飞入寻常百姓家阅读 222评论 0 0
  • 文|北苏 1 一个妹妹的朋友前几天失恋了,这个小妹妹很崩溃. 她跑来找我说,姐你说他有什么理由和我分手,每个月我工...
    北苏阅读 1,227评论 34 46
  • 常用注释说明 @RestController和@RequestMapping @EnableAutoConfigu...
    早餐午餐晚餐阅读 185评论 0 1