JS原生Date类型方法的一些冷知识,简单粗暴地理解

前端基础进阶(三):变量对象详解

2017/02/21 · 基础技术 ·
变量对象

原文出处: 波同学   

图片 1

开年之后工作热情一直不是很高,这几天一直处于消极怠工状态。早上不想起床,起床了不想上班。明明放假之前工作热情还一直很高,一直心心念念的想把小程序项目怼出来,结果休假回来之后画风完全不一样了。我感觉自己得了严重了节后综合征。还好撸了几篇文章,勉强表示这一周的时间没有完全浪费。这篇文章要给大家介绍的是变量对象。

在JavaScript中,我们肯定不可避免的需要声明变量和函数,可是JS解析器是如何找到这些变量的呢?我们还得对执行上下文有一个进一步的了解。

在上一篇文章中,我们已经知道,当调用一个函数时(激活),一个新的执行上下文就会被创建。而一个执行上下文的生命周期可以分为两个阶段。

  • 创建阶段
    在这个阶段中,执行上下文会分别创建变量对象,建立作用域链,以及确定this的指向
  • 代码执行阶段
    创建完成之后,就会开始执行代码,这个时候,会完成变量赋值,函数引用,以及执行其他代码。

图片 2

执行上下文生命周期

从这里我们就可以看出详细了解执行上下文极为重要,因为其中涉及到了变量对象,作用域链,this等很多人没有怎么弄明白,但是却极为重要的概念,因此它关系到我们能不能真正理解JavaScript。在后面的文章中我们会一一详细总结,这里我们先重点了解变量对象。

JS原生Date类型方法的一些冷知识

2015/09/07 · JavaScript
· Date

原文出处:
chitanda   

一个多月没更新了-
-偷懒中。这个东西其实很早之前就在整理了,不过后来发现自己不少地方没弄明白,然后就一直卡那边了(其实就是不想写吧),想了下反正是给自己熟悉js的原生API而已,所以也没必要太钻牛角尖,也不一定要多完整,因此就当是Date()函数的一个冷门知识点小补充吧。这篇文章主要讲Date()的字符串与时间戳转换以及用户时间本地化,可能内容上比较乱(不然也不会卡我一个月时间了),见谅

ps:由于 Date() 是js原生函数,不同浏览器的解析器对其实现方式并不同,所以返回值也会有所区别。本文测试未特别申明浏览器的情况下,均是指
win7 x64+chrome 44.0.2403.155
(正式版本) m (32
位) 版本

简单粗暴地理解 JS 原型链

2016/05/07 · JavaScript
· 1 评论 ·
原型链

原文出处:
茄果   

原型链理解起来有点绕了,网上资料也是很多,每次晚上睡不着的时候总喜欢在网上找点原型链和闭包的文章看,效果极好。

不要纠结于那一堆术语了,那除了让你脑筋拧成麻花,真的不能帮你什么。简单粗暴点看原型链吧,想点与代码无关的事,比如人、妖以及人妖。

1)人是人他妈生的,妖是妖他妈生的。人和妖都是对象实例,而人他妈和妖他妈就是原型。原型也是对象,叫原型对象。

图片 3

2)人他妈和人他爸啪啪啪能生出一堆人宝宝、妖他妈和妖他爸啪啪啪能生出一堆妖宝宝,啪啪啪就是构造函数,俗称造人。

图片 4

3)人他妈会记录啪啪啪的信息,所以可以通过人他妈找到啪啪啪的信息,也就是说能通过原型对象找到构造函数。

4)人他妈可以生很多宝宝,但这些宝宝只有一个妈妈,这就是原型的唯一性。

5)人他妈也是由人他妈他妈生的,通过人他妈找到人他妈他妈,再通过人他妈他妈找到人他妈他妈……,这个关系叫做原型链。

图片 5

6)原型链并不是无限的,当你通过人他妈一直往上找,最后发现你会发现人他妈他妈他妈……的他妈都不是人,也就是原型链最终指向null。

7)人他妈生的人会有人的样子,妖他妈生的妖会有妖的丑陋,这叫继承。

图片 6

8)你继承了你妈的肤色,你妈继承了你妈他妈的肤色,你妈他妈……,这就是原型链的继承。

9)你谈对象了,她妈让你带上房产证去提货,你若没有,那她妈会问你妈有没有,你妈没有那她妈会问你妈她妈有没有……这就是原型链的向上搜索。

10)你会继承你妈的样子,但是你也可以去染发洗剪吹,就是说对象的属性可以自定义,会覆盖继承得到的属性。

图片 7

11)虽然你洗剪吹了染成黄毛了,但你不能改变你妈的样子,你妈生的弟弟妹妹跟你的黄毛洗剪吹没一点关系,就是说对象实例不能改动原型的属性。

12)但是你家被你玩火烧了的话,那就是说你家你妈家你弟们家都被烧了,这就是原型属性的共享。

13)你妈外号阿珍,邻居大娘都叫你阿珍儿,但你妈头发从飘柔做成了金毛狮王后,隔壁大婶都改口叫你包租仔,这叫原型的动态性。

图片 8

14)你妈爱美,又跑到韩国整形,整到你妈他妈都认不出来,即使你妈头发换回飘柔了,但隔壁邻居还是叫你金毛狮王子。因为没人认出你妈,整形后的你妈已经回炉再造了,这就是原型的整体重写。

图片 9

尼玛!你特么也是够了! Don’t BB! Show me the code!

function Person (name) { this.name = name; } function Mother () { }
Mother.prototype = { //Mother的原型 age: 18, home: [‘Beijing’,
‘Shanghai’] }; Person.prototype = new Mother(); //Person的原型为Mother
//用chrome调试工具查看,提供了__proto__接口查看原型,这里有两层原型,各位还是直接看chrome好一点。
var p1 = new Person(‘Jack’); //p1:’Jack’;
__proto__:{__proto__:18,[‘Beijing’,’Shanghai’]} var p2 = new
Person(‘Mark’); //p2:’Mark’;
__proto__:{__proto__:18,[‘Beijing’,’Shanghai’]} p1.age = 20;
/* 实例不能改变原型的基本值属性,正如你洗剪吹染黄毛跟你妈无关 *
在p1实例下增加一个age属性的普通操作,与原型无关。跟var o={};
o.age=20一样。 * p1:下面多了个属性age,而__proto__跟
Mother.prototype一样,age=18。 * p2:只有属性name,__proto__跟
Mother.prototype一样 */ p1.home[0] = ‘Shenzhen’; /*
原型中引用类型属性的共享,正如你烧了你家,就是烧了你全家的家 *
这个先过,下文再仔细唠叨一下可好? * p1:’Jack’,20;
__proto__:{__proto__:18,[‘Shenzhen’,’Shanghai’]} *
p2:’Mark’; __proto__:{__proto__:18,[‘Shenzhen’,’Shanghai’]}
*/ p1.home = [‘Hangzhou’, ‘Guangzhou’]; /*
其实跟p1.age=20一样的操作。换成这个理解: var o={};
o.home=[‘big’,’house’] * p1:’Jack’,20,[‘Hangzhou’,’Guangzhou’];
__proto__:{__proto__:18,[‘Shenzhen’,’Shanghai’]} *
p2:’Mark’; __proto__:{__proto__:18,[‘Shenzhen’,’Shanghai’]}
*/ delete p1.age; /*
删除实例的属性之后,原本被覆盖的原型值就重见天日了。正如你剃了光头,遗传的迷人小卷发就长出来了。
*
这就是向上搜索机制,先搜你,然后你妈,再你妈他妈,所以你妈的改动会动态影响你。
* p1:’Jack’,[‘Hangzhou’,’Guangzhou’];
__proto__:{__proto__:18,[‘Shenzhen’,’Shanghai’]} *
p2:’Mark’; __proto__:{__proto__:18,[‘Shenzhen’,’Shanghai’]}
*/ Person.prototype.lastName = ‘Jin’; /*
改写原型,动态反应到实例中。正如你妈变新潮了,邻居提起你都说你妈真潮。
*
注意,这里我们改写的是Person的原型,就是往Mother里加一个lastName属性,等同于Mother.lastName=’Jin’
*
这里并不是改Mother.prototype,改动不同的层次,效果往往会有很大的差异。
* p1:’Jack’,[‘Hangzhou’,’Guangzhou’];
__proto__:{‘jin’,__proto__:18,[‘Shenzhen’,’Shanghai’]} *
p2:’Mark’;
__proto__:{‘jin’,__proto__:18,[‘Shenzhen’,’Shanghai’]} */
Person.prototype = { age: 28, address: { country: ‘USA’, city:
‘Washington’ } }; var p3 = new Person(‘Obama’); /*
重写原型!这个时候Person的原型已经完全变成一个新的对象了,也就是说Person换了个妈,叫后妈。
* 换成这样理解:var a=10; b=a; a=20;
c=a。所以b不变,变得是c,所以p3跟着后妈变化,与亲妈无关。 *
p1:’Jack’,[‘Hangzhou’,’Guangzhou’];
__proto__:{‘jin’,__proto__:18,[‘Shenzhen’,’Shanghai’]} *
p2:’Mark’;
__proto__:{‘jin’,__proto__:18,[‘Shenzhen’,’Shanghai’]} *
p3:’Obama’;__proto__: 28 {country: ‘USA’, city: ‘Washington’} */
Mother.prototype.no = 9527; /*
改写原型的原型,动态反应到实例中。正如你妈他妈变新潮了,邻居提起你都说你丫外婆真潮。
*
注意,这里我们改写的是Mother.prototype,p1p2会变,但上面p3跟亲妈已经了无瓜葛了,不影响他。
* p1:’Jack’,[‘Hangzhou’,’Guangzhou’];
__proto__:{‘jin’,__proto__:18,[‘Shenzhen’,’Shanghai’],9527} *
p2:’Mark’;
__proto__:{‘jin’,__proto__:18,[‘Shenzhen’,’Shanghai’],9527} *
p3:’Obama’;__proto__: 28 {country: ‘USA’, city: ‘Washington’} */
Mother.prototype = { car: 2, hobby: [‘run’,’walk’] }; var p4 = new
Person(‘Tony’); /*
重写原型的原型!这个时候Mother的原型已经完全变成一个新的对象了!人他妈换了个后妈!
*
由于上面Person与Mother已经断开联系了,这时候Mother怎么变已经不影响Person了。
* p4:’Tony’;__proto__: 28 {country: ‘USA’, city: ‘Washington’} */
Person.prototype = new Mother(); //再次绑定 var p5 = new
Person(‘Luffy’); //
这个时候如果需要应用这些改动的话,那就要重新将Person的原型绑到mother上了
// p5:’Luffy’;__proto__:{__proto__: 2, [‘run’,’walk’]}
p1.__proto__.__proto__.__proto__.__proto__
//null,你说原型链的终点不是null?
Mother.__proto__.__proto__.__proto__
//null,你说原型链的终点不是null?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
function Person (name) { this.name = name; }
function Mother () { }
Mother.prototype = {    //Mother的原型
    age: 18,
    home: [‘Beijing’, ‘Shanghai’]
};
Person.prototype = new Mother(); //Person的原型为Mother
 
//用chrome调试工具查看,提供了__proto__接口查看原型,这里有两层原型,各位还是直接看chrome好一点。
var p1 = new Person(‘Jack’); //p1:’Jack’; __proto__:{__proto__:18,[‘Beijing’,’Shanghai’]}
var p2 = new Person(‘Mark’); //p2:’Mark’; __proto__:{__proto__:18,[‘Beijing’,’Shanghai’]}
 
p1.age = 20;  
/* 实例不能改变原型的基本值属性,正如你洗剪吹染黄毛跟你妈无关
* 在p1实例下增加一个age属性的普通操作,与原型无关。跟var o={}; o.age=20一样。
* p1:下面多了个属性age,而__proto__跟 Mother.prototype一样,age=18。
* p2:只有属性name,__proto__跟 Mother.prototype一样
*/
 
p1.home[0] = ‘Shenzhen’;
/* 原型中引用类型属性的共享,正如你烧了你家,就是烧了你全家的家
* 这个先过,下文再仔细唠叨一下可好?
* p1:’Jack’,20; __proto__:{__proto__:18,[‘Shenzhen’,’Shanghai’]}
* p2:’Mark’;    __proto__:{__proto__:18,[‘Shenzhen’,’Shanghai’]}
*/
 
p1.home = [‘Hangzhou’, ‘Guangzhou’];
/* 其实跟p1.age=20一样的操作。换成这个理解: var o={}; o.home=[‘big’,’house’]
* p1:’Jack’,20,[‘Hangzhou’,’Guangzhou’]; __proto__:{__proto__:18,[‘Shenzhen’,’Shanghai’]}
* p2:’Mark’;                             __proto__:{__proto__:18,[‘Shenzhen’,’Shanghai’]}
*/
 
delete p1.age;    
/* 删除实例的属性之后,原本被覆盖的原型值就重见天日了。正如你剃了光头,遗传的迷人小卷发就长出来了。
* 这就是向上搜索机制,先搜你,然后你妈,再你妈他妈,所以你妈的改动会动态影响你。
* p1:’Jack’,[‘Hangzhou’,’Guangzhou’]; __proto__:{__proto__:18,[‘Shenzhen’,’Shanghai’]}
* p2:’Mark’;                          __proto__:{__proto__:18,[‘Shenzhen’,’Shanghai’]}
*/
 
 
Person.prototype.lastName = ‘Jin’;
/* 改写原型,动态反应到实例中。正如你妈变新潮了,邻居提起你都说你妈真潮。
* 注意,这里我们改写的是Person的原型,就是往Mother里加一个lastName属性,等同于Mother.lastName=’Jin’
* 这里并不是改Mother.prototype,改动不同的层次,效果往往会有很大的差异。
* p1:’Jack’,[‘Hangzhou’,’Guangzhou’]; __proto__:{‘jin’,__proto__:18,[‘Shenzhen’,’Shanghai’]}
* p2:’Mark’;                          __proto__:{‘jin’,__proto__:18,[‘Shenzhen’,’Shanghai’]}
*/
 
Person.prototype = {
    age: 28,
    address: { country: ‘USA’, city: ‘Washington’ }
};
var p3 = new Person(‘Obama’);
/* 重写原型!这个时候Person的原型已经完全变成一个新的对象了,也就是说Person换了个妈,叫后妈。
* 换成这样理解:var a=10; b=a; a=20; c=a。所以b不变,变得是c,所以p3跟着后妈变化,与亲妈无关。
* p1:’Jack’,[‘Hangzhou’,’Guangzhou’]; __proto__:{‘jin’,__proto__:18,[‘Shenzhen’,’Shanghai’]}
* p2:’Mark’;                          __proto__:{‘jin’,__proto__:18,[‘Shenzhen’,’Shanghai’]}
* p3:’Obama’;__proto__: 28 {country: ‘USA’, city: ‘Washington’}
*/
 
 
Mother.prototype.no = 9527;
/* 改写原型的原型,动态反应到实例中。正如你妈他妈变新潮了,邻居提起你都说你丫外婆真潮。
* 注意,这里我们改写的是Mother.prototype,p1p2会变,但上面p3跟亲妈已经了无瓜葛了,不影响他。
* p1:’Jack’,[‘Hangzhou’,’Guangzhou’]; __proto__:{‘jin’,__proto__:18,[‘Shenzhen’,’Shanghai’],9527}
* p2:’Mark’;                          __proto__:{‘jin’,__proto__:18,[‘Shenzhen’,’Shanghai’],9527}
* p3:’Obama’;__proto__: 28 {country: ‘USA’, city: ‘Washington’}
*/
 
Mother.prototype = {
    car: 2,
    hobby: [‘run’,’walk’]
};
var p4 = new Person(‘Tony’);
/* 重写原型的原型!这个时候Mother的原型已经完全变成一个新的对象了!人他妈换了个后妈!
* 由于上面Person与Mother已经断开联系了,这时候Mother怎么变已经不影响Person了。
* p4:’Tony’;__proto__: 28 {country: ‘USA’, city: ‘Washington’}
*/
Person.prototype = new Mother(); //再次绑定
var p5 = new Person(‘Luffy’);
// 这个时候如果需要应用这些改动的话,那就要重新将Person的原型绑到mother上了
// p5:’Luffy’;__proto__:{__proto__: 2, [‘run’,’walk’]}
 
p1.__proto__.__proto__.__proto__.__proto__ //null,你说原型链的终点不是null?
Mother.__proto__.__proto__.__proto__    //null,你说原型链的终点不是null?

看完基本能理解了吧?

现在再来说说 p1.age = 20、p1.home = [‘Hangzhou’, ‘Guangzhou’]
和  p1.home[0] = ‘Shenzhen’ 的区别。 p1.home[0] = ‘Shenzhen’;
 总结一下是 p1.object.method,p1.object.property 这样的形式。

p1.age = 20;  p1.home = [‘Hangzhou’,
‘Guangzhou’];这两句还是比较好理解的,先忘掉原型吧,想想我们是怎么为一个普通对象增加属性的:

var obj = new Object(); obj.name=’xxx’; obj.num = [100, 200];

1
2
3
var obj = new Object();
obj.name=’xxx’;
obj.num = [100, 200];

这样是不是就理解了呢?一样一样的呀。

那为什么 p1.home[0] = ‘Shenzhen’ 不会在 p1 下创建一个 home
数组属性,然后将其首位设为 ‘Shenzhen’呢?
我们还是先忘了这个,想想上面的obj对象,如果写成这样: var obj.name =
‘xxx’, obj.num = [100, 200],能得到你要的结果吗?
显然,除了报错你什么都得不到。因为obj还未定义,又怎么能往里面加入东西呢?同理,p1.home[0]中的
home 在 p1 下并未被定义,所以也不能直接一步定义 home[0]
了。如果要在p1下创建一个 home 数组,当然是这么写了:

p1.home = []; p1.home[0] = ‘Shenzhen’;

1
2
p1.home = [];
p1.home[0] = ‘Shenzhen’;

这不就是我们最常用的办法吗?

而之所以 p1.home[0] =
‘Shenzhen’ 不直接报错,是因为在原型链中有一个搜索机制。当我们输入
p1.object
的时候,原型链的搜索机制是先在实例中搜索相应的值,找不到就在原型中找,还找不到就再往上一级原型中搜索……一直到了原型链的终点,就是到null还没找到的话,就返回一个
undefined。当我们输入 p1.home[0] 的时候,也是同样的搜索机制,先搜索 p1
看有没有名为 home
的属性和方法,然后逐级向上查找。最后我们在Mother的原型里面找到了,所以修改他就相当于修改了
Mother 的原型啊。

一句话概括:p1.home[0] = ‘Shenzhen’  等同于
 Mother.prototype.home[0] = ‘Shenzhen’。

由上面的分析可以知道,原型链继承的主要问题在于属性的共享,很多时候我们只想共享方法而并不想要共享属性,理想中每个实例应该有独立的属性。因此,原型继承就有了下面的两种改良方式:

变量对象(Variable Object)

变量对象的创建,依次经历了以下几个过程。

  1. 建立arguments对象。检查当前上下文中的参数,建立该对象下的属性与属性值。
  2. 检查当前上下文的函数声明,也就是使用function关键字声明的函数。在变量对象中以函数名建立一个属性,属性值为指向该函数所在内存地址的引用。如果函数名的属性已经存在,那么该属性将会被新的引用所覆盖。
  3. 检查当前上下文中的变量声明,每找到一个变量声明,就在变量对象中以变量名建立一个属性,属性值为undefined。如果该变量名的属性已经存在,为了防止同名的函数被修改为undefined,则会直接跳过,原属性值不会被修改。

图片 10

我知道有的人不喜欢看文字

根据这个规则,理解变量提升就变得十分简单了。在很多文章中虽然提到了变量提升,但是具体是怎么回事还真的很多人都说不出来,以后在面试中用变量对象的创建过程跟面试官解释变量提升,保证瞬间提升逼格。

在上面的规则中我们看出,function声明会比var声明优先级更高一点。为了帮助大家更好的理解变量对象,我们结合一些简单的例子来进行探讨。

JavaScript

// demo01 function test() { console.log(a); console.log(foo()); var a =
1; function foo() { return 2; } } test();

1
2
3
4
5
6
7
8
9
10
11
12
// demo01
function test() {
    console.log(a);
    console.log(foo());
 
    var a = 1;
    function foo() {
        return 2;
    }
}
 
test();

在上例中,我们直接从test()的执行上下文开始理解。全局作用域中运行test()时,test()的执行上下文开始创建。为了便于理解,我们用如下的形式来表示

JavaScript

创建过程 testEC = { // 变量对象 VO: {}, scopeChain: {}, this: {} } //
因为本文暂时不详细解释作用域链和this,所以把变量对象专门提出来说明 // VO
为 Variable Object的缩写,即变量对象 VO = { arguments: {…},
//注:在浏览器的展示中,函数的参数可能并不是放在arguments对象中,这里为了方便理解,我做了这样的处理
foo: <foo reference> // 表示foo的地址引用 a: undefined }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
创建过程
testEC = {
    // 变量对象
    VO: {},
    scopeChain: {},
    this: {}
}
 
// 因为本文暂时不详细解释作用域链和this,所以把变量对象专门提出来说明
 
// VO 为 Variable Object的缩写,即变量对象
VO = {
    arguments: {…},  //注:在浏览器的展示中,函数的参数可能并不是放在arguments对象中,这里为了方便理解,我做了这样的处理
    foo: <foo reference>  // 表示foo的地址引用
    a: undefined
}

未进入执行阶段之前,变量对象中的属性都不能访问!但是进入执行阶段之后,变量对象转变为了活动对象,里面的属性都能被访问了,然后开始进行执行阶段的操作。

这样,如果再面试的时候被问到变量对象和活动对象有什么区别,就又可以自如的应答了,他们其实都是同一个对象,只是处于执行上下文的不同生命周期。

JavaScript

// 执行阶段 VO -> AO // Active Object AO = { arguments: {…}, foo:
<foo reference>, a: 1 }

1
2
3
4
5
6
7
// 执行阶段
VO ->  AO   // Active Object
AO = {
    arguments: {…},
    foo: <foo reference>,
    a: 1
}

因此,上面的例子demo1,执行顺序就变成了这样

JavaScript

function test() { function foo() { return 2; } var a; console.log(a);
console.log(foo()); a = 1; } test();

1
2
3
4
5
6
7
8
9
10
11
function test() {
    function foo() {
        return 2;
    }
    var a;
    console.log(a);
    console.log(foo());
    a = 1;
}
 
test();

再来一个例子,巩固一下我们的理解。

JavaScript

// demo2 function test() { console.log(foo); console.log(bar); var foo =
‘Hello’; console.log(foo); var bar = function () { return ‘world’; }
function foo() { return ‘hello’; } } test();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// demo2
function test() {
    console.log(foo);
    console.log(bar);
 
    var foo = ‘Hello’;
    console.log(foo);
    var bar = function () {
        return ‘world’;
    }
 
    function foo() {
        return ‘hello’;
    }
}
 
test();

JavaScript

// 创建阶段 VO = { arguments: {…}, foo: <foo reference>, bar:
undefined } //
这里有一个需要注意的地方,因为var声明的变量当遇到同名的属性时,会跳过而不会覆盖

1
2
3
4
5
6
7
// 创建阶段
VO = {
    arguments: {…},
    foo: <foo reference>,
    bar: undefined
}
// 这里有一个需要注意的地方,因为var声明的变量当遇到同名的属性时,会跳过而不会覆盖

JavaScript

// 执行阶段 VO -> AO VO = { arguments: {…}, foo: ‘Hello’, bar:
<bar reference> }

1
2
3
4
5
6
7
// 执行阶段
VO -> AO
VO = {
    arguments: {…},
    foo: ‘Hello’,
    bar: <bar reference>
}

需要结合上面的知识,仔细对比这个例子中变量对象从创建阶段到执行阶段的变化,如果你已经理解了,说明变量对象相关的东西都已经难不倒你了。

Date()与new Date()的区别

Date() 直接返回当前时间字符串,不管参数是number还是任何string

JavaScript

Date(); Date(‘sssss’); Date(1000); //Fri Aug 21 2015 15:46:21 GMT+0800
(中国标准时间)

1
2
3
4
Date();
Date(‘sssss’);
Date(1000);
//Fri Aug 21 2015 15:46:21 GMT+0800 (中国标准时间)

而 new Date() 则是会根据参数来返回对应的值,无参数的时候,返回当前时间的字符串形式;有参数的时候返回参数所对应时间的字符串。
new Date() 对参数不管是格式还是内容都要求,且只返回字符串,

JavaScript

new Date(); //Fri Aug 21 2015 15:51:55 GMT+0800 (中国标准时间) new
Date(1293879600000); new Date(‘2011-01-01T11:00:00’) new
Date(‘2011/01/01 11:00:00’) new Date(2011,0,1,11,0,0) new Date(‘jan 01
2011,11 11:00:00’) new Date(‘Sat Jan 01 2011 11:00:00’) //Sat Jan 01
2011 11:00:00 GMT+0800 (中国标准时间) new Date(‘sss’); new
Date(‘2011/01/01T11:00:00’); new Date(‘2011-01-01-11:00:00’) new
Date(‘1293879600000’); //Invalid Date new
Date(‘2011-01-01T11:00:00’)-new Date(‘1992/02/11 12:00:12’)
//596069988000

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
new Date();
//Fri Aug 21 2015 15:51:55 GMT+0800 (中国标准时间)
 
new Date(1293879600000);
new Date(‘2011-01-01T11:00:00’)
new Date(‘2011/01/01 11:00:00’)
new Date(2011,0,1,11,0,0)
new Date(‘jan 01 2011,11 11:00:00’)
new Date(‘Sat Jan 01 2011 11:00:00’)
//Sat Jan 01 2011 11:00:00 GMT+0800 (中国标准时间)
 
new Date(‘sss’);
new Date(‘2011/01/01T11:00:00’);
new Date(‘2011-01-01-11:00:00’)
new Date(‘1293879600000’);
//Invalid Date
 
new Date(‘2011-01-01T11:00:00’)-new Date(‘1992/02/11 12:00:12’)
//596069988000

从上面几个测试结果可以很容易发现

  1. new Date() 在参数正常的情况只会返回当前时间的字符串(且是当前时区的时间)
  2. new Date() 在解析一个具体的时间的时候,对参数有较严格的格式要求,格式不正确的时候会直接返回Invalid Date,比如将
    number 类的时间戳转换成 string 类的时候也会导致解析出错
  3. 虽然 new Date() 的返回值是字符串,然而两个
    new Date() 的结果字符串是可以直接相减的,结果为相差的毫秒数。

那么, new Date() 能接受的参数格式到底是什么标准呢?(相对于严格要求的多参数传值方法。非严格的单参数(数字日期表示格式)更常用且更容易出错,所以下文只考虑单参数数字时间字符串转换的情况)

表示格式)更常用且更容易出错,所以下文只考虑单参数数字时间字符串转换的情况)


1)组合继承

function Mother (age) { this.age = age; this.hobby =
[‘running’,’football’] } Mother.prototype.showAge = function () {
console.log(this.age); }; function Person (name, age) {
Mother.call(this, age);  //第二次执行 this.name = name; }
Person.prototype = new Mother();  //第一次执行
Person.prototype.constructor = Person; Person.prototype.showName =
function () { console.log(this.name); } var p1 = new Person(‘Jack’, 20);
p1.hobby.push(‘basketball’); //p1:’Jack’;
__proto__:20,[‘running’,’football’] var p2 = new Person(‘Mark’,
18); //p2:’Mark’; __proto__:18,[‘running’,’football’]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function Mother (age) {
    this.age = age;
    this.hobby = [‘running’,’football’]
}
Mother.prototype.showAge = function () {
    console.log(this.age);
};
 
function Person (name, age) {
    Mother.call(this, age);  //第二次执行
    this.name = name;
}
Person.prototype = new Mother();  //第一次执行
Person.prototype.constructor = Person;
Person.prototype.showName = function () {
    console.log(this.name);
}
 
var p1 = new Person(‘Jack’, 20);
p1.hobby.push(‘basketball’);  //p1:’Jack’; __proto__:20,[‘running’,’football’]
var p2 = new Person(‘Mark’, 18);  //p2:’Mark’; __proto__:18,[‘running’,’football’]

结果是酱紫的:

图片 11  图片 12

这里第一次执行的时候,得到 Person.prototype.age =
undefined, Person.prototype.hobby =
[‘running’,’football’],第二次执行也就是 var p1 = new Person(‘Jack’,
20) 的时候,得到 p1.age =20, p1.hobby =
[‘running’,’football’],push后就变成了 p1.hobby =
[‘running’,’football’, ‘basketball’]。其实分辨好 this
的变化,理解起来也是比较简单的,把 this 简单替换一下就能得到这个结果了。
如果感觉理解起来比较绕的话,试着把脑子里面的概念扔掉吧,把自己当浏览器从上到下执行一遍代码,结果是不是就出来了呢?

通过第二次执行原型的构造函数
Mother(),我们在对象实例中复制了一份原型的属性,这样就做到了与原型属性的分离独立。细心的你会发现,我们第一次调用
Mother(),好像什么用都没有呢,能不调用他吗?可以,就有了下面的寄生组合式继承。

全局上下文的变量对象

以浏览器中为例,全局对象为window。
全局上下文有一个特殊的地方,它的变量对象,就是window对象。而这个特殊,在this指向上也同样适用,this也是指向window。

JavaScript

// 以浏览器中为例,全局对象为window // 全局上下文 windowEC = { VO:
window, scopeChain: {}, this: window }

1
2
3
4
5
6
7
// 以浏览器中为例,全局对象为window
// 全局上下文
windowEC = {
    VO: window,
    scopeChain: {},
    this: window
}

除此之外,全局上下文的生命周期,与程序的生命周期一致,只要程序运行不结束,比如关掉浏览器窗口,全局上下文就会一直存在。其他所有的上下文环境,都能直接访问全局上下文的属性。

前端基础进阶系列目录

前端基础进阶系列我会持续更新,欢迎大家关注我公众号isreact,新的文章更新了我会在公众号里第一时间通知大家。也欢迎大家来简书关注我。

1 赞 3 收藏
评论

图片 13

new Date()解析所支持的参数格式标准

2)寄生组合式继承

function object(o){ function F(){} F.prototype = o; return new F(); }
function inheritPrototype(Person, Mother){ var prototype =
object(Mother.prototype); prototype.constructor = Person;
Person.prototype = prototype; } function Mother (age) { this.age = age;
this.hobby = [‘running’,’football’] } Mother.prototype.showAge =
function () { console.log(this.age); }; function Person (name, age) {
Mother.call(this, age); this.name = name; } inheritPrototype(Person,
Mother); Person.prototype.showName = function () {
console.log(this.name); } var p1 = new Person(‘Jack’, 20);
p1.hobby.push(‘basketball’);//p1:’Jack’;
__proto__:20,[‘running’,’football’] var p2 = new Person(‘Mark’,
18); //p2:’Mark’; __proto__:18,[‘running’,’football’]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function object(o){
    function F(){}
    F.prototype = o;
    return new F();
}
 
function inheritPrototype(Person, Mother){
    var prototype = object(Mother.prototype);
    prototype.constructor = Person;    
    Person.prototype = prototype;    
}
                        
function Mother (age) {
    this.age = age;
    this.hobby = [‘running’,’football’]
}
Mother.prototype.showAge = function () {
    console.log(this.age);
};
 
function Person (name, age) {
    Mother.call(this, age);
    this.name = name;
}
 
inheritPrototype(Person, Mother);
 
Person.prototype.showName = function () {
    console.log(this.name);
}
 
var p1 = new Person(‘Jack’, 20);
p1.hobby.push(‘basketball’);//p1:’Jack’; __proto__:20,[‘running’,’football’]
var p2 = new Person(‘Mark’, 18); //p2:’Mark’; __proto__:18,[‘running’,’football’]

结果是酱紫的:

图片 14 图片 15

原型中不再有 age 和 hobby 属性了,只有两个方法,正是我们想要的结果!

关键点在于 object(o) 里面,这里借用了一个临时对象来巧妙避免了调用new
Mother(),然后将原型为 o
的新对象实例返回,从而完成了原型链的设置。很绕,对吧,那是因为我们不能直接设置
Person.prototype = Mother.prototype 啊。

时间戳格式

这个是最简单的也是最不容易出错的。当然唯一的缺点大概就是对开发者不直观,无法一眼看出具体日期。
需要注意的以下两点:

  1. js内的时间戳指的是当前时间到1970年1月1日00:00:00 UTC对应的毫秒数,和unix时间戳不是一个概念,后者表示秒数,差了1000倍
  2. class=”crayon-syntax crayon-syntax-inline crayon-theme-github crayon-theme-github-inline crayon-font-monaco”
    style=”font-size: 13px !important; line-height: 15px !important;font-size: 13px !important;”> class=”crayon-pre crayon-code”
    style=”font-size: 13px !important; line-height: 15px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;”> class=”crayon-r”>new class=”crayon-r”>Date( class=”crayon-v”>timestamp class=”crayon-sy”>) 中的时间戳必须是number格式,
    class=”crayon-syntax crayon-syntax-inline crayon-theme-github crayon-theme-github-inline crayon-font-monaco”
    style=”font-size: 13px !important; line-height: 15px !important;font-size: 13px !important;”> class=”crayon-pre crayon-code”
    style=”font-size: 13px !important; line-height: 15px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;”> class=”crayon-t”>string 会返回Invalid Date。所以比如new Date('11111111')这种写法是错的

小结


说了这么多,其实核心只有一个:属性共享和独立的控制,当你的对象实例需要独立的属性,所有做法的本质都是在对象实例里面创建属性。若不考虑太多,你大可以在Person里面直接定义你所需要独立的属性来覆盖掉原型的属性。总之,使用原型继承的时候,要对于原型中的属性要特别注意,因为他们都是牵一发而动全身的存在。

下面简单罗列下js中创建对象的各种方法,现在最常用的方法是组合模式,熟悉的同学可以跳过到文章末尾点赞了。

时间数字字符串格式

不大清楚这种该怎么描述,就是类似YYYY/MM/DD HH:mm:SS这种。下文以dateString代指。
new Date(dateString)所支持的字符串格式需要满足RFC2822标准或者ISO
8601标准
这两种标准对应的格式分别如下:

  1. RFC2822 标准日期字符串
JavaScript

YYYY/MM/DD HH:MM:SS ± timezon(时区用4位数字表示) // eg 1992/02/12
12:23:22+0800

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f6c187675a314957670-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6c187675a314957670-2">
2
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f6c187675a314957670-1" class="crayon-line">
YYYY/MM/DD HH:MM:SS ± timezon(时区用4位数字表示)
</div>
<div id="crayon-5b8f6c187675a314957670-2" class="crayon-line crayon-striped-line">
// eg 1992/02/12 12:23:22+0800
</div>
</div></td>
</tr>
</tbody>
</table>

>  RFC2822还有别的格式,不过上面这个是比较常用的(另外这标准太难啃了,实在没耐心啃完,所以也就没太深入)。RFC2822标准本身还有其他的非数字日期表达方式,不过不在这个话题讨论范围内了,略过
  1. ISO 8601标准日期字符串
JavaScript

YYYY-MM-DDThh:mm:ss ± timezone(时区用HH:MM表示) 1997-07-16T08:20:30Z
//
“Z”表示UTC标准时区,即"00:00",所以这里表示零时区的\`1997年7月16日08时20分30秒\`
//转换成位于东八区的北京时间则为\`1997年7月17日16时20分30秒\`
1997-07-16T19:20:30+01:00 //
表示东一区的1997年7月16日19时20秒30分,转换成UTC标准时间的话是1997-07-16T18:20:30Z

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f6c187675d765819674-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6c187675d765819674-2">
2
</div>
<div class="crayon-num" data-line="crayon-5b8f6c187675d765819674-3">
3
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6c187675d765819674-4">
4
</div>
<div class="crayon-num" data-line="crayon-5b8f6c187675d765819674-5">
5
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6c187675d765819674-6">
6
</div>
<div class="crayon-num" data-line="crayon-5b8f6c187675d765819674-7">
7
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6c187675d765819674-8">
8
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f6c187675d765819674-1" class="crayon-line">
 YYYY-MM-DDThh:mm:ss ± timezone(时区用HH:MM表示)
</div>
<div id="crayon-5b8f6c187675d765819674-2" class="crayon-line crayon-striped-line">
 
</div>
<div id="crayon-5b8f6c187675d765819674-3" class="crayon-line">
 1997-07-16T08:20:30Z
</div>
<div id="crayon-5b8f6c187675d765819674-4" class="crayon-line crayon-striped-line">
 // “Z”表示UTC标准时区,即&quot;00:00&quot;,所以这里表示零时区的`1997年7月16日08时20分30秒`
</div>
<div id="crayon-5b8f6c187675d765819674-5" class="crayon-line">
 //转换成位于东八区的北京时间则为`1997年7月17日16时20分30秒`
</div>
<div id="crayon-5b8f6c187675d765819674-6" class="crayon-line crayon-striped-line">
 
</div>
<div id="crayon-5b8f6c187675d765819674-7" class="crayon-line">
 1997-07-16T19:20:30+01:00
</div>
<div id="crayon-5b8f6c187675d765819674-8" class="crayon-line crayon-striped-line">
 // 表示东一区的1997年7月16日19时20秒30分,转换成UTC标准时间的话是1997-07-16T18:20:30Z
</div>
</div></td>
</tr>
</tbody>
</table>
  1. 日期和时间中间的T不可以被省略,一省略就出错。
  2. 虽然在chrome浏览器上时区也可以用+0100这种RFC2822的形式来表示,然而IE上不支持这种混搭写法,所以用ISO8601标准形式表示的时候时区要用+HH:MM

单单从格式上来说,两者的区别主要在于分隔符的不同。不过需要注意的是,ISO
8601标准的兼容性比RFC2822差得多(比如IE8和iOS均不支持前者。我知道IE8很多人会无视,不过iOS也有这个坑的话,各位或多或少会谨慎点了吧?),所以一般情况下建议用RFC 2822格式的。
不过需要注意的是,在未指定时区的前提下,对于只精确到day的日期字符串,RFC 2822返回结果是以当前时区的零点为准,而ISO8601返回结果则会以UTC时间的零点为标准进行解析。
例如:

JavaScript

//RFC2822: new Date(‘1992/02/13’) //Thu Feb 13 1992 00:00:00 GMT+0800
(中国标准时间) //ISO8601: new Date(‘1992-02-13’) //Thu Feb 13 1992
08:00:00 GMT+0800 (中国标准时间)

1
2
3
4
//RFC2822:
new Date(‘1992/02/13’) //Thu Feb 13 1992 00:00:00 GMT+0800 (中国标准时间)
//ISO8601:
new Date(‘1992-02-13’) //Thu Feb 13 1992 08:00:00 GMT+0800 (中国标准时间)

 

然而上面这个只是ES5的标准而已,在ES6里这两种形式都会变成当前时区的零点为基准1
*不管你们崩溃没,反正我是已经想死了*
关于跨浏览器的dataString解析情况,还可以参考这个页面:
JavaScript and Dates, What a
Mess!

所以对于时间字符串对象,个人意见是要么用RFC2822形式,要么自己写个解析函数然后随便你传啥格式进来。


1)原始模式

//1.原始模式,对象字面量方式 var person = { name: ‘Jack’, age: 18,
sayName: function () { alert(this.name); } };
//1.原始模式,Object构造函数方式 var person = new Object(); person.name
= ‘Jack’; person.age = 18; person.sayName = function () {
alert(this.name); };

1
2
3
4
5
6
7
8
9
10
11
12
13
//1.原始模式,对象字面量方式
var person = {
    name: ‘Jack’,
    age: 18,
    sayName: function () { alert(this.name); }
};
//1.原始模式,Object构造函数方式
var person = new Object();
person.name = ‘Jack’;
person.age = 18;
person.sayName = function () {
    alert(this.name);
};

显然,当我们要创建批量的person1、person2……时,每次都要敲很多代码,资深copypaster都吃不消!然后就有了批量生产的工厂模式。

时间格式化函数的效率

这里的时间格式化值得是将时间字符串转换成毫秒数的过程。js原生的时间格式化函数有Date.parseDate.prototype.valueOfDate.prototype.getTimeNumber(Date)+Date(还有个Date.UTC方法,然而对参数要求严格,不能直接解析日期字符串,所以略过)
这5个函数从功能上来说一模一样,但是具体的效率如何呢?我写了个检测页面,诸位也可以自己测试下。
点击预览

2)工厂模式

//2.工厂模式,定义一个函数创建对象 function creatPerson (name, age) {
var person = new Object(); person.name = name; person.age = age;
person.sayName = function () { alert(this.name); }; return person; }

1
2
3
4
5
6
7
8
9
10
//2.工厂模式,定义一个函数创建对象
function creatPerson (name, age) {
    var person = new Object();
    person.name = name;
    person.age = age;
    person.sayName = function () {
        alert(this.name);
    };
    return person;
}

工厂模式就是批量化生产,简单调用就可以进入造人模式(图片 16啪啪啪……)。指定姓名年龄就可以造一堆小宝宝啦,解放双手。但是由于是工厂暗箱操作的,所以你不能识别这个对象到底是什么类型、是人还是狗傻傻分不清(instanceof
测试为
Object),另外每次造人时都要创建一个独立的temp对象,代码臃肿,雅蠛蝶啊。

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注