5.3、Date类型
在调用 Date 构造函数而不传递参数的情况下,新创建的对象自动获得当前日期和时间。
var now = new Date();
console.log(now); // chrome => Fri Jan 04 2019 20:44:15 GMT+0800 (中国标准时间)
// firefox => Date 2019-01-04T12:54:58.617Z
ES中提供了两个方法来获取日期的毫秒数:Date.parse();Date.UTC()。
(1)Date.parse() 接收一个表示日期的字符串参数,然后尝试根据这个字符串返回相应日期的毫秒数。
注意:如果传入的字符串不能表示日期,那么它会返回 NaN。
// Date.parse('May 25, 2004')返回的是毫秒数
var someDate = new Date(Date.parse('May 25, 2004'));
// Date()默认会后台调用 Date.parse()方法
var someDate1 = new Date('May 25, 2004');
(2)Date.UTC() 同样返回表示日期的毫秒数,其参数分别为:
年份(必须);
月:基于0的月份(一月是0,必须);
日:月中的哪一天(1-31);
时:小时数(0-23);
分:分钟数;
秒;
毫秒。
// GMT时间2000年1月1日午夜零时
var y2k = new Date(Date.UTC(2000, 0));
console.log(y2k);
// GMT时间2005年5月5日下午5:55:55
var allFives = new Date(Date.UTC(2005, 4, 5, 17, 55, 55));
console.log(allFives);
在ES5中新增 Date.now() 方法返回表示调用这个方法时的日期和时间的毫秒数。
注意:在不支持该方法的浏览器中,使用 + 操作符获取 Date 对象的时间戳,也可以达到同样的目的。
var start = Date.now(); // 等同于 var start = +new Date();
console.log(start);
5.3.1、继承的方法
Date 类型也重写了 toLocaleString()、toString()、valueOf()方法。
Date 类型的 toLoacleString() 方法会按照与浏览器设置的地区相适应的格式返回日期和时间。
而 toString() 方法则通常返回带有时区信息的日期和时间。
Date 类型的 valueOf() 方法返回日期的毫秒数。
// 在chrome中测试
var date1 = new Date();
console.log(date1.toLocaleString()); // 2019/1/4 下午9:10:05
console.log(date1.toString()); // Fri Jan 04 2019 21:10:05 GMT+0800 (中国标准时间)
console.log(date1.valueOf()); // 1546607405111
5.3.2、日期格式化方法
将日期格式化为字符串的方法,如下所示:
- toDateString()——以特定于实现的格式显示星期几、月、日和年;
- toTimeString()——以特定于实现的格式显示时、分、秒和时区;
- toLocaleDateString()——以特定于地区的格式显示星期几、月、日和年;
- toLocaleTimeString()——以特定于地区的格式显示时、分、秒;
- toUTCString()——以特定于实现的格式完整的UTC日期。
以上这些字符串格式方法的输出会因浏览器而异。
5.4、RegExp类型
正则表达式的匹配模式支持下列3个标志:
- g:表示全局(global)模式,即模式将被应用于所有字符串,而非在发现第一个匹配项时立即停止;
- i:表示不区分大小写模式,即在确定匹配项时忽略模式与字符串的大小写;
- m:表示多行模式,即在到达一行文本末尾时还会继续查找下一行中是否存在与模式匹配的项。
在js中有两种方式创建正则表达式方法:(1)字面量;(2)RegExp 构造函数。
(1)用字面量创建正则表达式
// 用字面量创建正则表达式如下所示
var expression = /pattern/flags;
(2)通过 RegExp 构造函数创建正则表达式,它接收两个两个参数:一个是要匹配的字符串模式,另一个是可选的标志字符串。
注意:由于传递给 RegExg 构造函数的两个参数都是字符串,因此所有正则表达式中的所有元字符都必须双重转义,那些已经转义过的也是如此。
// 匹配第一个"bac"或"cat",不区分大小写
var pattern1 = /[bc]at/i;
// 与pattern1相同,只不过是使用构造函数创建的
var pattern2 = new RegExp("[bc]at","i");
在 ES3 中正则表达式字面量始终会共享同一个 RegExg 实例,而使用构造函数创建的每一个新 RegExp 实例都是一个新实例。
在 ES5 中规定,使用正则表达式字面量必须像直接调用 RegExp 构造函数一样,每次都创建新的 RegExp 实例。
5.4.1、RegExp实例属性
RegExp 的每个实例都具有下列属性:
- global:布尔值,表示是否设置了 g 标志。
- ignoreCase:布尔值,表示是否设置了 i 标志。
- lastIndex:整数,表示开始搜索下一个匹配项的字符位置,从0算起。
- multiline:布尔值,表示是否设置了 m 标志。
- source:正则表达式的字符串表示,按照字面量形式而非传入构造函数中的字符串模式返回。
var pattern1 = /\[bc\]at/i;
console.log(pattern1.global); // false;
console.log(pattern1.ignoreCase); // true
console.log(pattern1.lastIndex); // 0
console.log(pattern1.multiline); // false
console.log(pattern1.source); // /\[bc\]at/i
var pattern2 = new RegExp('\\[bc\\]at', 'i');
console.log(pattern2.global); // false
console.log(pattern2.ignoreCase); // true
console.log(pattern2.lastIndex); // 0
console.log(pattern2.multiline); // false
console.log(pattern2.source); // /\[bc\]at/i,按照正则表达式字面量返回
5.4.2、RegExp实例方法
5.4.2.1、exec() 方法
exec() 方法接受一个参数,即要应用模式的字符串,然后返回包含第一个匹配项信息的数组;或则没有匹配项的情况下返回null。返回的数组虽然是 Array 实例,但包含两个额外的属性:index 和 input。其中,index 表示匹配项在字符串中的位置,而 input 表示应用正则表达式的字符串。
var text = 'mom and dad and baby';
var pattern = /mom( and dad( and baby)?)?/gi;
var matches = pattern.exec(text);
console.log(matches.index); // 0
console.log(matches.input); // 'mom and dad and baby'
console.log(matches[0]); // "mom and dad and baby"
console.log(matches[1]); // " and dad and baby"
console.log(matches[2]); // " and baby"
注意:对于 exec() 方法而言,即使在模式中设置了全局表示(g),它每次也只会返回一个匹配项。在不设置全局标志的情况下,在同一个字符串上多次调用 exec() 将始终返回第一个匹配项的信息。而在设置全局标志的情况下,每次调用 exec() 则都会在字符串中继续查找新匹配项。
var text = 'cat, bat, sat, fat';
var pattern1 = /.at/;
var matches = pattern1.exec(text);
console.log(matches.index); // 0
console.log(matches[0]); // cat
console.log(pattern1.lastIndex); // 0
var matches = pattern1.exec(text);
console.log(matches.index); // 0
console.log(matches[0]); // cat
console.log(pattern1.lastIndex); // 0
var pattern2 = /.at/g;
var matches = pattern2.exec(text);
console.log(matches.index); // 0
console.log(matches[0]); // cat
console.log(pattern2.lastIndex); // 3
var matches = pattern2.exec(text);
console.log(matches.index); // 5
console.log(matches[0]); // bat
console.log(pattern2.lastIndex); // 8
5.4.2.2、test() 方法
test() 方法接受一个字符串参数。在模式与该参数匹配的情况下返回 true;否则,返回 false。在只想知道目标字符串与某个模式是否匹配,但不需要知道其文本内容的情况下,使用这个方法非常不错。
var text = '000-000-0000';
var pattern = /\d{3}-\d{3}-\d{4}/;
if (pattern.test(text)) {
console.log('The pattern was matched.');
}
RegExp 实例继承的 toLoacleString() 和 toString() 方法都会返回正则表达式的字面量。valueOf() 方法则返回正则表达式本身(对象)。
var pattern= new RegExp('\\[bc\\]at', 'gi');
console.log(pattern.toLocaleString()); // /\[bc\]at/gi
console.log(pattern.toString()); // /\[bc\]at/gi
console.log(pattern.valueOf()); // 返回的是对象
5.4.3、RegExp 构造函数属性
RegExp 构造函数的各个属性返回了下列值:
- input 属性返回了原始字符串
- leftContext 属性返回了单词 short 之前的字符串,而 rightContext 属性则返回了 short 之后的字符串;
- lastMatch 属性返回最近一次与整个正则表达式匹配的字符串,即 short;
- lastParen 属性返回最近一次匹配的捕获组,即例子中的 s。
var text = 'this has been a short summer';
var pattern = /(.)hort/g;
if (pattern.test(text)) {
console.log(RegExp.input); // 'this has been a short summer'
console.log(RegExp.leftContext); // 'this has been a '
console.log(RegExp.rightContext); // ' summer'
console.log(RegExp.lastMatch); // 'short'
console.log(RegExp.lastParen); // 's'
console.log(pattern.multiline); // false
}
5.4.4、正则表达式学习参考文献
[1] https://www.cnblogs.com/Jochebed/p/6293595.html
[2] http://www.zjmainstay.cn/regexp-one
[3] https://www.zhihu.com/question/20197998
5.5、Function类型
函数实际上是对象,因此函数名实际上也是一个指向函数对象的指针。
定义函数有三种方法:(1)函数声明;(2)函数表达式;(3)使用 Function 构造函数。
// (1)函数声明
function sum(num1, num2) {
return num1+num2;
}
// (2)函数表达式
var sum1 = function(sum1, sum2) {
return sum1+sum2;
}
// (3)使用 Function 构造函数
var sum2 = new Function('num1', 'num2', 'return num1+num2');
console.log(sum(10, 20)); // 30
console.log(sum1(10, 30)); // 40
console.log(sum2(10, 40)); // 50
注意:使用不带圆括号的函数名是访问函数指针,而给调用函数。且在js函数没有重载。
5.5.1、函数声明与函数表达式
解析器对函数声明与函数表达式的解析顺序是不一样的:解析器会率先读取函数声明,并使其在执行任何代码之前可用(可以访问)——函数声明提升;至于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解释执行。
console.log(sum(10, 10)); // 可以提前访问
// 函数声明
function sum(num1, num2) {
return num1+num2;
}
console.log(sum1(10, 10)); // 错误
// 函数表达式只有执行到函数表达式所在行的代码时才会被解析
var sum1 = function(num1, num2) {
return num1+num2;
};
5.5.2、作为值的函数
ES中的函数名本身就是变量,所以函数也可以作为值来使用。
function callSomeFunction(someFunction, someArgument) {
return someFunction(someArgument);
}
function add10(num) {
return num + 10;
}
var result1 = callSomeFunction(add10, 10);
console.log(result1); // 20
function getGreeting(name) {
return 'Hello, ' + name;
}
var result2 = callSomeFunction(getGreeting, 'Nicholas');
alert(result2); // 'Hello, Nicholas'
// FunctionReturningFunctionExample01.html
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'));
console.log(data[0].name); // 'Nicholas'
data.sort(createComparisonFunction('age'));
console.log(data[0].name); // 'Zachary'
5.5.3、函数的内部属性
函数内部有两个特殊的对象:(1)arguments;(2)this;。
(1)arguments 是一个类数组对象,包含传入函数中的所有参数。该对象拥有一个名叫 callee 的属性,该属性是一个指针,指向拥有这个 arguments 对象的函数。
function factorial(num) {
if (num <= 1) {
return 1;
}
else {
return num * factorial(num - 1);
}
}
function factorial2(num) {
if (num <= 1) {
return 1;
}
else {
return num * arguments.callee(num - 1);
}
}
var trueFactorial = factorial;
var trueFactorial2 = factorial2;
factorial = function() {
return 0;
};
/*
* trueFactorial(5)的执行步骤
* 步骤一:调用 factorial()阶乘函数,在该函数内部执行到 return num * factorial(num - 1)时;
* 步骤二:fcatorial(num - 1)会调用 factorial = function() {return 0;};
* 步骤三:结果为 0
*/
alert(trueFactorial(5)); // 0
alert(trueFactorial2(5)); // 120
(2)this 引用的是函数执行环境的对象——或者也可以说是 this 值(当在网页的全局作用域中调用函数时,this 对象引用的是 window)。
window.color = 'red';
var o = {color: 'blue'};
// 全局函数
function sayColor() {
alert(this.color);
}
// 在全局作用域中调用函数
// 那么 this 引用的对象就是 window
sayColor(); // 'red'
o.sayColor = sayColor;
// this 引用的对象是 o
o.sayColor(); // "blue"
ES5中 规范化了另一个函数对象的属性 caller。该属性中保存着调用当前函数的函数的引用,如果是在全局作用域中调用当前函数,它的值为 null。
function outer() {
console.log(outer.caller);
}
outer(); // null
function outer1() {
inner();
}
function inner() {
console.log(inner.caller);
}
outer1(); // outer1函数的引用
注意:在严格模式下访问 arguments.callee 会导致错误。ES5 还定义了 arguments.caller 属性,但在严格模式下访问它也会导致错误,而在非严格模式下这个属性始终是 undefined。严格模式还有一个限制:不能为函数的 caller 属性赋值,否则会导致错误。
5.5.4、函数属性和方法
每个函数都包含两个属性:(1)length;(2)prototype。
(1)length 属性表示函数希望接收的命名参数的个数。
function sayName(name) {
console.log(name);
}
function sum(num1, num2) {
return num1 + num2;
}
function sayHi() {
alert("hi");
}
console.log(sayName.length); // 1
console.log(sum.length); // 2
console.log(sayHi.length); // 0
(2)对于 ES 中的引用类型而言,prototype 是保存它们所有实例方法的真正所在。
注意:在 ES5 中 prototype 属性是不可枚举的,因此使用 for-in 无法发现。
每个函数都包含两个非继承而来的方法:(1)apply();(2)call()。用途:在特定的作用域中调用函数,实际上等于设置函数体内 this 对象的值。
(1)apply() 方法接收两个参数:一个是在其中运行函数的作用域,另一个是参数数组。其中,第二个参数可以是 Array 的实例,也可以是 arguments 对象。
function sum(num1, num2) {
return num1+num2;
}
function callSum0(num1, num2) {
return sum(num1, num2);
}
function callSum1(num1, num2) {
return sum.apply(this, arguments);
}
function callSum2(num1, num2) {
return sum.apply(this, [num1, num2]);
}
console.log(callSum0(10, 20)); // 30
console.log(callSum1(10, 20)); // 30
console.log(callSum2(10, 20)); // 30
call() 方法与 apple() 方法的唯一区别为:传递给函数的参数必须逐个列举出来。
function sum(num1, num2) {
return num1 + num2;
}
function callSum(num1, num2) {
return sum.call(this, num1, num2);
}
alert(callSum(10, 10));
注意:apple() 方法 和 call() 方法真正强大的地方是能够扩充函数赖以运行的作用域,且对象不需要与方法有任何耦合关系。
window.color = 'red';
var o = {color: 'blue'};
function sayColor() {
alert(this.color);
}
sayColor(); // red
// 在全局作用域中调用,this = window
sayColor.call(this); // red
sayColor.call(window); // red
sayColor.call(o); // blue
在 ES5 中还定义了一个方法:bind() 方法会创建一个函数的实例,其 this 值会被绑定到传给 bind() 函数的值。
window.color = 'red';
var o = {color: 'blue'};
function sayColor() {
console.log(this.color);
}
sayColor(); // 'red'
var objectSayColor = sayColor.bind(o);
objectSayColor(); // 'blue'
sayColor(); // 'red'
参考文献
[1] 《JavaScript高级程序设计(第3版)》