0%

概念

(略)

方法

1
2
3
4
5
6
7
8
keys *
redis-cli
set key value
get key
del key
hget key fieldname
hget all
flushall

redis作为认证token数据库

ticket=(accountid, token)

centos安装Redis

参考:Linux 平台将 Redis 设置为服务并开机自启动

服务部署好之后,若无法远程访问

首先检查防火墙设置

1
2
3
systemctl status firewalld # 查看防火墙状态
firewall-cmd --zone= public --query-port=6379/tcp # 查询防火墙端口状态
firewall-cmd --zone=public --add-port=6379/tcp --permanent # 永久开发端口及协议

确认是否绑定服务主机ip 参考 处理CentOS 7.2 x64端口不通的问题

远程访问

修改/etc/redis/redis.conf

1
2
# 注释掉或将其改为服务器静态ip
# bind 127.0.0.1 ::1

DENIED Redis is running in protected mode because protected mode is enabled, no bind address was specified, no authentication password is requested to clients…..

受保护的模式,改为no

1
protected-mode no

ControlValueAccessor接口

定义一个接口,该接口充当 Angular 表单 API 和 DOM 中的原生元素之间的桥梁。 实现此接口以创建与 Angular 表单集成的自定义表单控件指令, from Angular Doc

删除线小注:见下文“关于原生控件”

1
2
3
4
5
6
7
// angular2/packages/forms/src/directives/control_value_accessor.ts 
export interface ControlValueAccessor {
writeValue(obj: any): void;
registerOnChange(fn: any): void;
registerOnTouched(fn: any): void;
setDisabledState?(isDisabled: boolean): void;
}

  • writeValue(obj: any):该方法用于将宿主传入的新值写入自定义组件的视图或 DOM 属性。
  • registerOnChange(fn: any):设置当控件接收到来自宿主的 change 事件后,调用的函数 一般将其对接到input控件的ngOnChange响应事件上
  • registerOnTouched(fn: any):设置当控件接收到 touched 事件后,调用的函数
  • setDisabledState?(isDisabled: boolean):当控件状态变成 DISABLED 或从 DISABLED 状态变化成 ENABLE 状态时,会调用该函数。该函数会根据参数值,启用或禁用指定的 DOM 元素。

自定义的组件实现ControlValueAccessor接口,可以像原生表单控件一样使用ngModel绑定值
stackbliz demo

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
import {Component, OnInit, forwardRef, Input} from '@angular/core';
import {ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR} from '@angular/forms';

@Component({
selector: 'app-custom-input',
templateUrl: './custom-input.component.html',
styleUrls: ['./custom-input.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CustomInputComponent),
multi: true
}
]
})
export class CustomInputComponent implements ControlValueAccessor {
onChange: any = () => {}
onTouch: any = () => {}
registerOnChange(fn: any): void {
this.onChange = fn;
}
registerOnTouched(fn: any): void {
this.onTouch = fn;
}
input: string;
writeValue(input: string) {
this.input = input;
}
}

关于原生控件
不直接操作自定义组件 而在组件内逻辑赋值绑定值input 控件会响应 但是自定义组件的ngOnChange不会响应 即父组件绑定ngModel的值并不更新, 即逻辑赋值input还需要手动调用registerOnChange注入的外部响应事件,也就是上述范例代码的onChange变量

参考ng-zorro的select控件源码发现 在实现ControlValueAccessor的自定义组件模板中原生组件是不必要的,自定义组件可以自行管理对外暴露的ngModel的值,没有必要响应原生组件的特性

OnChange接口

A lifecycle hook that is called when any data-bound property of a directive changes. Define an ngOnChanges() method to handle the changes.当指令的任何一个可绑定属性发生变化时调用。 定义一个 ngOnChanges() 方法来处理这些变更。

实际上是自定义组件继承OnChange接口,实现ngOnChange方法,当自定义组件@Input的变量更新时,该方法响应,传入SimpleChanges类型参数,该参数是发生更新的变量的哈希表, 即

1
2
3
4
5
6
7
8
9
10
11
@Component({selector: 'my-cmp', template: `...`})
class MyComponent implements OnChanges {
// TODO(issue/24571): remove '!'.
@Input() prop!: number;
@Input() data:any

ngOnChanges(changes: SimpleChanges) {
const {prop, data} = changes
// prop and data is an SimpleChange boject which contains the old and the new value...
}
}

SimpleChanges是SimpleChange类型的map
1
2
3
4
5
6
7
8
9
10
export declare class SimpleChange {
previousValue: any;
currentValue: any;
firstChange: boolean;
constructor(previousValue: any, currentValue: any, firstChange: boolean);
/**
* Check whether the new value is the first value assigned.
*/
isFirstChange(): boolean;
}

Caution! ngOnChange的底层逻辑是使用‘===’判断前后值的,因此引用类型的属性变化不会触发ngOnChange,workaround是将此类输入属性拆成基本值类型变量,或者在宿主更新该输入属性时赋予新的引用地址,亦或使用ngDoCheck

1
2
3
4
<app-voter *ngFor="let voter of voters"
[name]="voter"
(voted)="onVoted($event)">
</app-voter>

@Input()

@Input()可以带set、get属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { Component, Input } from '@angular/core';

@Component({
selector: 'app-voter',
template: '<h3>"{{name}}"</h3>'
})
export class VoterComponent {
private _name = '';

@Input()
set name(name: string) {
this._name = (name && name.trim()) || '<no name set>';
}

get name(): string { return this._name; }
}

在set、get时进行格式化等操作。

ngOnChanges()生命周期钩子是专门用来监听@Input入参的,非传入参数不会触发ngOnChanges()方法

@Output()

子组件发射事件

1
2
3
4
5
@Output() voted = new EventEmitter<boolean>();

vote(agreed: boolean) {
this.voted.emit(agreed);
}

模板标记子组件

1
2
3
4
5
6
7
8
<h3>Countdown to Liftoff (via local variable)</h3>

<button (click)="timer.start()">Start</button>
<button (click)="timer.stop()">Stop</button>

<div class="seconds">{{timer.seconds}}</div>

<app-countdown-timer #timer></app-countdown-timer>

timer 作为父组件的变量,标记在子组件标签中,用以代表子组件,即可使父组件获取子组件的引用,进而可以调用子组件公用方法

该方法有局限性在于,父组件-子组件的连接必须全部在父组件的模板中进行。父组件本身的代码对子组件没有访问权。

@ViewChild()

1
2
@ViewChild(CountdownTimerComponent)
private timerComponent: CountdownTimerComponent;

使用@ViewChild()装饰器将CountdownTimerComponent实例注入到本组件,记为私有变量timerComponent

  • 特别注意
    被注入的组件只有在 Angular 显示了父组件视图之后才能访问
    下面的语法很关键

    1
    2
    3
    4
    5
    ngAfterViewInit() {
    // wait a tick first to avoid one-time devMode
    // unidirectional-data-flow-violation error
    setTimeout(() => this.timerComponent.init(), 0);
    }

    父组件要及时调用子组件方法进行初始化,须实现AfterViewInit生命周期钩子,且用定时器异步操作。
    否则报异常

    1
    ExpressionChangedAfterItHasBeenCheckedError

    引入第三者通信

    这类方法包括注入服务,借助相同父组件以及借助发布订阅可观察对象等,此处略。

SCSS 是 Sass 3 引入新的语法,其语法完全兼容 CSS3,并且继承了 Sass 的强大功能。

将Angular项目样式由css改为scss

安装 node-sass sass-loader

修改angular.json

1
2
3
4
5
6
"styles": [
"src/styles.scss"
],
"default": {
"styleExt":"scss"
},

上面的修改也就看看,不做实操指导,新建ng项目时可以选择样式类型,当时选scss便可,免得多事

SCSS嵌套结构样式优先级高于非嵌套结构样式,因此可以用某元素父元素嵌套的写法覆盖该元素样式

常用新语法

map类型
1
2
3
4
$pie:(
width:125px
height:140px
)
1
width:map-get($pie, width)
@import @mixin

定义一个Mixin模块

1
2
3
4
5
@mixin button{
font-size:1em;
padding:0.5em;
color:#fff
}

调用
1
2
3
4
.button-green{
@include button;
back-ground:green
}

@extend

引用已定义的样式

1
2
3
4
.button-green-mini{
@extend .button-green;
width:2em
}

循环语句创建样式
1
2
3
4
5
6
7
8
9
$lvlcolors:(
1:$color-danger
2:$color-orange
3:$color-warning
4:$color-blue
)
@for $lvl from 1 through 4{
.lvl#{$lvl} {background: map-get($lvlcolors, $lvl)}
}

each:

1
2
3
4
5
6
7
8
9
$icons: ("eye": "\f112", "start": "\f12e", "stop": "\f12f");

@each $name, $glyph in $icons {
.icon-#{$name}:before {
display: inline-block;
font-family: "Icon Font";
content: $glyph;
}
}

issue: scss variables are not working in calc
1
2
3
4
5
.main {
width: 100%;
height: calc(100% - #{$header-height});
background: #313030;
}

伪类 icon
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$icon:(
add:"%3Csvg%20width%3D%2218%22%20height%3D%2218%22%20viewBox...",
edit:"%3Csvg%20width%3D%2230%22%20height%3D%2230%22%20viewBox...",
delete:"%3Csvg%20width%3D%2230%22%20height%3D%2230%22%20viewBox...",
refresh:"%3Csvg%20width%3D%2230%22%20height%3D%2230%22%20viewBox..."
)
@each $key, $val in $icon{
.qqs-design-icon.icon-#{$key}{
position: relative;
padding-left: 22px;
margin-left: 4px;
&::before{
content: '';
position: absolute;
left: 0px;
bottom: calc(50% - 10px);
background: url("data:image/svg+xml," + $val + "") no-repeat top left;
width: 25px;
height: 25px;
}
}
}

拼接url

1
2
3
4
5
6
7
$sites: ("twitter.com", "facebook.com", "linkedin.com");

@each $site in $sites {
a[href*="#{$site}"] {
background-image: url("/images/" + $site + ".png");
}
}

总结一下就是#{}这个符号用于将变量拼接在css选择器上,包括class名,属性名等,在样式的值中,字符串与变量的拼接可以直接用“+”连接

issues

jenkins build fail

1
npm i -g node-sass

以下理解未必完全正确,但包含了若干方面的可能因素,可日后进一步探究(QQs:不太会探究):jenkins 在打包angular过程中为webpack的sass-loader安装所需包node-sass,但是缺少node-gyp,python等工具链的调用权限,因而build失败,至于npm install为什么会build,electron编译过程中也遇到过,编译对象是package中调用的c++库。对此的解决方案之一是在jenkins所在的物理机上全局安装node-sass,当下的默认版本是5.0.0,曾尝试在项目package.json中将node-sass更新为5.0.0,然而angular9中的sass-loader似乎是支持node-sass^4.0.0,因此出现“Node Sass version 5.0.0 is incompatible with ^4.0.0”的报错,应在全局重装npm i -g node-sass@4

参考
node-sass troubleshooting#Running with sudo or as root
stackoverflow:Error: Node Sass version 5.0.0 is incompatible with ^4.0.0
node-sass issues#941

Cannot download “https://github.com/sass/node-sass/releases/download/v4.13.1/win32-x64-83_binding.node”
package-lock指定了node-sass@4.13.1, 关于node-sass的release版本没有win32-x64-83_binding.node, ‘-83’为node 14的支持模块,而4.13的node-sass不支持node 14,见Node version support policy. 即此问题是由于编译环境升级到node14造成的,解决方法是安装支持node14的4.14+

参考

Worker

多线程 worker.js

  • 同源限制: worker线程运行的脚本 必须与主线程脚本文件同源(QQs按:这里应该指的是同域)
  • 无法访问dom 不能调用阻塞主线程的alert confirm等
  • 通过postmessage/onmessage与主线程通讯
  • 无法访问文件系统file:// Worker可用的Web Api

单线程和事件循环

js是单线程的 但是‘同时’能做很多事情 如js逻辑执行不影响交互事件的响应,可以设置定时器而不是阻塞在等待时间上

我们可以将整个事件循环机制拆解为几个核心部分来理解:

1. 核心组件

  • 调用栈:一个后进先出的数据结构,用于追踪当前正在执行的函数。所有同步代码都在这里执行。
  • :用于存储对象、数组等非结构化数据的内存区域。
  • Web APIs (或 Node.js APIs):由浏览器或 Node.js 环境提供的线程,用于处理异步操作(如 setTimeout、DOM 事件、网络请求)。它们不属于 JS 引擎本身
  • 任务队列:这是一个总称,它至少包含两种不同优先级的队列:
    • Macrotask Queue (宏任务队列):有时也被称为 Task QueueCallback Queue。这是我们通常所说的“事件队列”。
      • 来源setTimeoutsetIntervalI/O (如文件读取、网络请求 fetch)、UI 渲染、DOM 事件(如点击、键盘事件)。
    • Microtask Queue (微任务队列):一个优先级更高的队列。
      • 来源Promise.then() / Promise.catch() / Promise.finally()queueMicrotask()MutationObserver

2. 事件循环的运行规则

第一步:执行同步代码

  • JS 引擎从全局代码开始执行,将所有同步函数调用压入 调用栈 中,依次执行。
  • 在执行过程中,如果遇到异步操作(如 setTimeoutPromise.resolve().then()),JS 引擎会将其交给 Web APIs 处理,然后立即继续执行调用栈中的下一条同步代码,不会等待。

第二步:Web APIs 处理异步任务

  • Web APIs 在后台执行相应的操作。例如:
    • setTimeout 会启动一个计时器。
    • fetch 会发起网络请求。
  • 当异步操作完成时(例如,计时器到期、网络数据返回),Web APIs 不会立即将回调函数放入任务队列。它会将对应的回调函数推入到相应的任务队列中。
    • setTimeout 的回调进入 Macrotask Queue
    • Promise.then() 的回调进入 Microtask Queue

第三步:主线程空闲,开始事件循环

  • 调用栈 为空时(意味着所有同步代码都已执行完毕),事件循环 开始工作。

第四步:执行 Microtask Queue(关键!)

  • 事件循环会首先检查 Microtask Queue。
  • 如果 Microtask Queue 中有任务,事件循环会一次性执行完所有存在的微任务,直到微任务队列为空。
  • 在执行微任务的过程中,如果产生了新的微任务,它们会被添加到微任务队列的末尾,并在当前循环中被继续执行。这可能导致微任务的无限循环(需谨慎)。
  • 只有当 Microtask Queue 完全清空后,事件循环才会进行下一步。

第五步:执行 Macrotask Queue

  • 事件循环检查 Macrotask Queue
  • 如果 Macrotask Queue 中有任务,事件循环会只取出队列中的第一个任务,将其放入调用栈中执行。
  • 注意:每次循环只执行一个宏任务。

第六步:循环往复

  • 当这一个宏任务执行完毕,调用栈再次为空。事件循环会重新回到第四步,再次检查并清空 Microtask Queue。
  • 这个“执行一个宏任务 -> 执行所有微任务”的循环会不断重复,构成了完整的事件循环。

总结

核心要点修正:

  1. 不是两个队列,而是至少两个(宏任务和微任务)。
  2. 微任务队列优先级远高于宏任务队列。
  3. 每次事件循环只执行一个宏任务,但会执行所有可用的微任务。
  4. Promise.then 等产生微任务,setTimeout 等产生宏任务。
    代码示例验证:
1
2
3
4
5
6
7
8
9
10
console.log('1. 同步代码开始');
setTimeout(() => {
console.log('4. 宏任务: setTimeout');
}, 0);
Promise.resolve().then(() => {
console.log('2. 微任务: Promise.then 1');
}).then(() => {
console.log('3. 微任务: Promise.then 2');
});
console.log('1. 同步代码结束');

执行顺序分析:

  1. 同步代码console.log('1. 同步代码开始')console.log('1. 同步代码结束') 执行。输出 1. 同步代码开始1. 同步代码结束
  2. 遇到异步
    • setTimeout 交给 Web APIs,其回调被推入 Macrotask Queue
    • Promise.resolve().then() 的两个回调被推入 Microtask Queue
  3. 调用栈为空,事件循环开始
    • 检查 Microtask Queue:发现有两个微任务。
    • 执行第一个:console.log('2. 微任务: Promise.then 1')。输出 2
    • 执行第二个:console.log('3. 微任务: Promise.then 2')。输出 3
    • Microtask Queue 现在为空。
  4. 检查 Macrotask Queue
    • 发现一个宏任务 (setTimeout 的回调)。
    • 取出并执行:console.log('4. 宏任务: setTimeout')。输出 4
  5. 本次循环结束,等待下一次循环。
    最终输出:
1
2
3
4
5
1. 同步代码开始
1. 同步代码结束
2. 微任务: Promise.then 1
3. 微任务: Promise.then 2
4. 宏任务: setTimeout

这个结果清晰地展示了微任务在当前宏任务结束后、下一个宏任务开始前被全部执行的特性。

与typescript集成

定义一个common module

1
2
3
4
5
6
7
8
9
// worker-loader.d.ts

declare module "worker-loader!*" {
class WebpackWorker extends Worker {
constructor();
}

export default WebpackWorker;
}

worker

1
2
3
4
5
// MyWorker.ts
const worker :Worker = self as any;
worker.postMessage({ foo: "foo"});

worker.addEventListener("message", (event)=>console.log(evnet));

调用worker

1
2
3
4
5
6
7
// index.ts
import Worker from "worker-loader!./Worker";
const worker = new Worker();

worker.postMessage({ a: 1 });
worker.onmessage = (event) => {};
worker.addEventListener("message", (event) => {})

web服务器

1
2
3
4
5
<VirtualHost *:80>
DocumentRoot "E:/wamp/www"
ServerName localhost
ServerAlias localhost
</VirtualHost>

访问权限

浏览器报You don’t have permission to access /index.html on this server.

1
2
3
4
5
6
7
<Directory />         
Options FollowSymLinks
AllowOverride All
Order deny,allow
Deny from all
Satisfy all
</Directory>

上述配置是没毛病的,然而只是Apache22语法,Apache24不支持
服务器启动报Invalid command Order等

Apache2.4版本中,提供了由mod_authz_host支持的新的访问控制配置语法。而2.2版本中的Order、Allow等命令在新版本中也可以得到兼容,实现这个兼容功能的模块就是mod_access_compat。所以Load这个模块后,apache2.4就能识别这些语句了。

还有,一般情况下,http.conf中可能有其他关于访问权限的配置,如默认有

1
2
AllowOverride none
Require all denied

解决跨域

P类问题(Polynomial Problem)

简单的认为,P问题就是可以在多项式时间被图灵机判定的语言类。这里又涉及到图灵机,那么我们可以简单的认为,如果一个算法可以在多项式时间内求解,那么就可以认为它是P类问题。这样你就会感觉好多算法都是P类问题,对!没错!如何证明一个问题是否是P类问题呢?只要它满足以下两个条(证明它在多项式时间内完成)

  1. 运行步骤数要有多项式上届时间
  2. 每一步都要保证它可以由合理的确定模型在多项式时间内完成,其实就是每一步的求解过程也是多项式时间

这样步骤是多项式时间的,而每一步也是多项式时间,整合起来整个算法还是多项式时间的。

NP类问题(Non-deterministic Polynomial Problem)

NP问题指的是,这个算法可以在多项式时间内可验证,什么意思呢?我们知道对于P类问题,可以在多项式时间内求解出来,但是NP问题不行。可以这样理解,NP虽然不能在多项式时间内被求解,但是如果给出这个问题的某个解,那么我们可以在多项式时间内验证这个解是不是这个问题的解。
P是属于NP的,但是NP是否等于P是著名的千禧年七大难题之一 (有人说证明NP=P,相当于论证,世间万事皆有捷径)

NPC(Non-deterministic Polynomial Complete Problem)

方法 描述
getDate() 从 Date 对象返回一个月中的某一天 (1 ~ 31)。
getDay() 从 Date 对象返回一周中的某一天 即星期几 (0 ~ 6)。
getMonth() 从 Date 对象返回月份 (0 ~ 11)。
getFullYear() 从 Date 对象以四位数字返回年份。
getHours() 返回 Date 对象的小时 (0 ~ 23)。
getMinutes() 返回 Date 对象的分钟 (0 ~ 59)。
getSeconds() 返回 Date 对象的秒数 (0 ~ 59)。
getMilliseconds() 返回 Date 对象的毫秒(0 ~ 999)。
getTime() 返回 1970 年 1 月 1 日至今的毫秒数。
getTimezoneOffset() 返回本地时间与格林威治标准时间 (GMT) 的分钟差。
getUTCDate() 根据世界时从 Date 对象返回月中的一天 (1 ~ 31)。
getUTCDay() 根据世界时从 Date 对象返回周中的一天 (0 ~ 6)。
getUTCMonth() 根据世界时从 Date 对象返回月份 (0 ~ 11)。
getUTCFullYear() 根据世界时从 Date 对象返回四位数的年份。
getUTCHours() 根据世界时返回 Date 对象的小时 (0 ~ 23)。
getUTCMinutes() 根据世界时返回 Date 对象的分钟 (0 ~ 59)。
getUTCSeconds() 根据世界时返回 Date 对象的秒钟 (0 ~ 59)。
getUTCMilliseconds() 根据世界时返回 Date 对象的毫秒(0 ~ 999)。
parse() 返回1970年1月1日午夜到指定日期(字符串)的毫秒数。
setDate() 设置 Date 对象中月的某一天 (1 ~ 31)。
setMonth() 设置 Date 对象中月份 (0 ~ 11)。
setFullYear() 设置 Date 对象中的年份(四位数字)。
setYear() 请使用 setFullYear() 方法代替。
setHours() 设置 Date 对象中的小时 (0 ~ 23)。
setMinutes() 设置 Date 对象中的分钟 (0 ~ 59)。
setSeconds() 设置 Date 对象中的秒钟 (0 ~ 59)。
setMilliseconds() 设置 Date 对象中的毫秒 (0 ~ 999)。
setTime() 以毫秒设置 Date 对象。
setUTCDate() 根据世界时设置 Date 对象中月份的一天 (1 ~ 31)。
setUTCMonth() 根据世界时设置 Date 对象中的月份 (0 ~ 11)。
setUTCFullYear() 根据世界时设置 Date 对象中的年份(四位数字)。
setUTCHours() 根据世界时设置 Date 对象中的小时 (0 ~ 23)。
setUTCMinutes() 根据世界时设置 Date 对象中的分钟 (0 ~ 59)。
setUTCSeconds() 根据世界时设置 Date 对象中的秒钟 (0 ~ 59)。
setUTCMilliseconds() 根据世界时设置 Date 对象中的毫秒 (0 ~ 999)。
toSource() 返回该对象的源代码。
toString() 把 Date 对象转换为字符串。
toTimeString() 把 Date 对象的时间部分转换为字符串。格式如:09:58:59 GMT+0800 (China Standard Time)
toDateString() 把 Date 对象的日期部分转换为字符串。格式如:Wed Nov 10 2021
~~toGMTString() 请使用 toUTCString() 方法代替。~~
toUTCString() 根据世界时,把 Date 对象转换为字符串。Wed, 10 Nov 2021 02:00:48 GMT
toLocaleString() 根据本地时间格式,把 Date 对象转换为字符串。11/10/2021, 10:01:19 AM
toLocaleTimeString() 根据本地时间格式,把 Date 对象的时间部分转换为字符串。10:01:19 AM
toLocaleDateString() 根据本地时间格式,把 Date 对象的日期部分转换为字符串。11/10/2021
UTC() 根据世界时返回 1970 年 1 月 1 日 到指定日期的毫秒数。
valueOf() 返回 Date 对象的原始值。

月份的英文缩写

  • 使用数组
    [‘Jan’, ‘Feb’, ‘Mar’, ‘Apr’, ‘May’, ‘June’, ‘July’, ‘Aug’, ‘Sept’, ‘Oct’, ‘Nov’, ‘Dec’][(new Date()).getMonth()]
  • 使用格式化字符串
    (new Date()).toDateString().split(“ “)[1]

星期同理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Date.property.format=function(format){
// eg: format="yyyy-MM-dd hh:mm:ssS"
var o = {
"M+": this.getMonth() + 1,
"d+": this.getDate(),
"h+": this.getHours(),
"m+": this.getMinutes(),
"s+": this.getSeconds(),
"q+": Math.floor((this.getMonth() + 3)/3), //quarter
"S": this.getMilliseconds()
};
if(/(y+)/.test(format)){
format = format.replace(RegExp.$1,(this.getFullYear) + "").substr(4 - RegExp.$1.length)
}
for(var k in o){
if(new RegExp("(" + k + ")").test(format)){
format = format.replace(RegExp.$1, RegExp.$1.length == 1?o[k]:("00" + o[k]).substr(("" + o[k]).length));
}
}
return format;
}