0%

以前央视新闻有条微博说7成网友赞成数学退出高考,下边一片叫好声。我有个朋友淡淡回了句:“数学就是用来把这7成人筛出去的。”这句话我永远都记得,所有被千夫所指的困难,都是为了淘汰掉懦夫,仅此而已。
做前端开发我们都不得不使用float浮动属性,但是如果使用了浮动,由于浮动本身的特性,难免会遇到父级元素塌陷的情况,不能自适应高度。 假设了有三个盒子对象,一个父级里包含了两个子级,父级元素没有设置固定高度,子级一个使用了float:left(左浮动)属性,另外一个子级使用float:right(右浮动)属性,这种情况的话父级元素的高度就必定不能被子内容而撑开。 ### 场景 三个盒子对象:
1
2
3
4
5
<div class="content">
<div class="col-1">布局1</div>
<div class="col-2">布局2</div>
内容
</div>
设置了浮动之后,父级元素不能被撑开,如图: ![](/images/014.jpg) 我们所希望的效果: ![](/images/015.jpg) 这种情况在我们的工作中特别常见,我们可以通过四种方法清除浮动。 #### 方法一 使用一个空标签
1
2
3
4
5
6
<div class="content">
<div class="col-1">布局1</div>
<div class="col-2">布局2</div>
内容
<div class="clear"></div>
</div>
clear的 CSS样式如下:
1
2
3
.clear {
clear: both;
}

方法二

给父元素设置display:inline-block属性:

1
2
3
.content {
display: inline-block
}

方法三

给父元素设置overflow:auto或hidden属性:

1
2
3
.content {
overflow: auto; /* 设置 hidden 也可以 */
}

方法四

人不能为了尊严连钱都不要了。
### 预备知识—程序的内存分配 一个由C/C++编译的程序占用的内存分为以下几个部分: - 1、栈区(stack)— 由编译器自动分配释放,存放函数的参数值,局部变量的值等。其 操作方式类似于数据结构中的栈。 - 2、堆区(heap) — 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回 收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。 - 3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的 全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另 一块区域。 - 程序结束后由系统释放。 - 4、文字常量区 —常量字符串就是放在这里的。程序结束后由系统释放 - 5、程序代码区—存放函数体的二进制代码。

例子程序

这是一个前辈写的,非常详细

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//main.cpp    
int a = 0; 全局初始化区
char *p1; 全局未初始化区
main()
{
int b; //栈
char s[] = "abc"; //栈
char *p2; //栈
char *p3 = "123456"; //123456/0在常量区,p3在栈上。
static int c =0//全局(静态)初始化区
p1 = (char *)malloc(10);
p2 = (char *)malloc(20);
//分配得来得10和20字节的区域就在堆区。
strcpy(p1, "123456"); //123456/0放在常量区,编译器可能会将它与p3所指向的"123456" 优化成一个地方。
}

堆和栈的理论知识

申请方式

stack:
由系统自动分配。例如,声明在函数中一个局部变量int b;系统自动在栈中为b开辟空间
heap:
需要程序员自己申请,并指明大小,在c中malloc函数
如p1 = (char *)malloc(10);
在C++中用new运算符
如p2 = new char[10];
但是注意p1、p2本身是在栈中的。

申请后系统的响应

栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢
出。
堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,
会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表
中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的
首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。
另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部
分重新放入空闲链表中。

申请大小的限制

栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意
思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有
的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将
提示overflow。因此,能从栈获得的空间较小。
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储
的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小
受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。

申请效率的比较:

栈由系统自动分配,速度较快。但程序员是无法控制的。
堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.
另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈是
直接在进程的地址空间中保留一块内存,虽然用起来最不方便。但是速度快,也最灵活。

堆和栈中的存储内容

栈: 在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可
执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈
的,然后是函数中的局部变量。注意静态变量是不入栈的。
当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地
址,也就是主函数中的下一条指令,程序由该点继续运行。
堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容由程序员安排。

存取效率的比较

1
2
char   s1[]   =   "aaaaaaaaaaaaaaa";    
char *s2 = "bbbbbbbbbbbbbbbbb";

aaaaaaaaaaa是在运行时刻赋值的;
而bbbbbbbbbbb是在编译时就确定的;
但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。
比如:

1
2
3
4
5
6
7
8
9
10
#include    
void main()
{
char a = 1;
char c[] = "1234567890";
char *p ="1234567890";
a = c[1];
a = p[1];
return;
}

对应的汇编代码

1
2
3
4
5
6
7
10:   a   =   c[1];    
00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]
0040106A 88 4D FC mov byte ptr [ebp-4],cl
11: a = p[1];
0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]
00401070 8A 42 01 mov al,byte ptr [edx+1]
00401073 88 45 FC mov byte ptr [ebp-4],al

第一种在读取时直接就把字符串中的元素读到寄存器cl中,而第二种则要先把指针值读到
edx中,再根据edx读取字符,显然慢了。

http://blog.csdn.net/hairetz/article/details/4141043

我们走得太快,灵魂都跟不上了 ...

前几天在机场,我和一个老同学擦肩。老同学见面无非就是问问现状,聊聊过往。

想到那时,我们几个同学初到北京,几个刚毕业的年轻人在大城市打拼,生活动不动就拮据困顿。

我们一面抱怨贫穷,一面又有些心安理得,不断的安慰自己,年轻时穷一点没什么的,将来一定会好起来的,我们又不是什么富二代,官二代,年轻时穷一点不是理所当然的嘛。

何况再看看那些成天把钱挂在嘴边的人,要么是在手套厂做会计的小气阿姨,要么是带着大金链子的土肥圆小老板,多么庸俗市侩啊。

那么多美好的事情等待着我去探索,股票和基金那些枯燥的数字还是留给我35岁以后的人生吧。

二十几岁的人生大多如此,我们讨厌贫穷,却又心安理得的贫穷着。

我知道我们都可以找出无数个理由可以说服别人,自己是穷得多么有理有据,理所当然,但是乔克叔叔今天却想说,不管你是20岁,还是30岁,都不要穷的心安理得,年轻人一定要趁早学会挣钱。

1现实从来不是想象中那样岁月静好

年轻时总觉得,管他的金钱和房子,做自己喜欢的事,尽情的月光、追星、梦想诗和远方,等过了三十岁自然而然地会关注股票和基金,自然而然的就会有钱买车买房,虽然不至于富甲一方,但也会有个两室两厅的小房子,全家人围在一起吃饭,小孩在旁边吵吵闹闹。

然而事实是,你过了三十岁,依然一无所有,没有合适的人可以结婚,房子依然离你很遥远,这把年纪再说诗与远方你自己都想吐……

网上流传着一个故事,一个女人去粥店吃早餐,因为皮蛋瘦肉粥里没有看到瘦肉而和老板争吵起来。

老板解释说,瘦肉在熬粥的过程中熬化了。女人不信,两人越吵越激动,女人突然大哭起来。

旁边的人纷纷安慰“为了一碗粥而已,不至于啦”。

女人哭着说:“我哭的不是这个,我难过的是,为什么我30多岁了还因为这些鸡毛蒜皮跟人争吵,这不是我要的人生啊!”

是啊,年轻时谁曾想过将来的人生会这样的尴尬不体面,可是眼下,又有多少三四十岁的人那样尴尬不体面的活着。

现实从来没有我们想象中那样岁月静好,人生也绝不是从青年无产阶级自然过渡到中年中产阶级。

20多岁不努力,30多岁只会让你成为一个老去10岁的穷人。

年轻时,穷不是罪过,罪过的是穷的心安理得,不安于现状,却没有努力改变现状。

2努力赚钱,才有条件去接触更美好的事

前几天有个23岁的小朋友在后台跟我聊天:“乔克叔叔,我可能得抑郁症了”,然后巴拉巴拉说了一堆。

听他倾诉完,我说:“你这不是抑郁,你这是想要的太多,拥有的太少,说白了就是没钱太闲综合症”。

年轻时,最缺的就是钱,最充足的就是时间,于是总能衍生出很多烦恼。

离家太远,房东人太烂,同事太难相处,日子太无聊,追个剧广告都越来越长…….

有人说,生活中90%的烦恼都能用钱解决。

年轻时的烦恼更是如此,你有理想,心怀诗和远方,想和世界上所有美好不期而遇。这很好,与挣钱、与世俗意义上的丰裕生活并不冲突。

追求钱并不庸俗,经济学、投资学和哲学、诗歌一样浪漫伟大,钱给你节省时间,给你的人生更多选择权,可以更没顾虑的去做想做的事情。

你可以学想学很久的国画,去帮助更多需要帮助的人,可以去带父母环游世界,可以投资给你认同的事业并为之摇旗呐喊,可以不用在爱情来临时,因为没有房,而和爱情失之交臂……

3

把“挣钱”当做一种技能去学

罗伯特·清崎在《穷爸爸,富爸爸》中说,之所以世界上绝大多数的人,为了财富奋斗终生而不可得,其主要原因在于,虽然他们都曾在各种学校学习多年,却从未真正学习到关于金钱的知识。

努力挣钱不是不择手段,甚至出卖自己灵魂的挣钱。

而趁早关注房产、股票等各种投资理财渠道,学习基本的经济学知识,认真审视自己,学习将自己的优势转化成钞票的能力。

否则,中年以后,你只能每天为生活疲于奔命,整个余生都在和不喜欢的人斗争,做自己不喜欢做的事。

写到这里,乔克叔叔真的希望能有一条万能准则告诉你们,让看到这篇文章的朋友都能实现财务自由。

然而事实是,每个人的情况都不一样,也没有这样一条准则可以放之四海皆准。

我和从事理财专业的朋友聊了聊,然后结合我自己浅薄经验总结一些小Tips,希望对你们有所裨益。

1学会存钱

根据自己每月的固定收入,按照合适的比例固定存入一些。

2养成记账的习惯

分析自己每月的支出报表,找出每月能省掉的和必须花钱的地方。

3如工资是收入唯一来源,请做好职业规划

如果你每月的收入,唯一来自固定的薪水,就要好好规划一下职业发展,怎么完善自己可以有效快速的实现加薪。

4努力让自己的收入变多元

丰富自己的收入途径,比如你擅长写文章,写段子,或者画插画,从各种方面把这些优势变现。

5学习投资理财

当有一定的存款之后,就开始学习怎么用“钱”赚钱,可以从最安全的货币基金开始,然后再尝试更多。

 你必须很努力,才能看上去毫不费力

事件冒泡

点击一个div, 最终弹出 div

1
2
3
4
5
6
7
8
9
10
<div>
<p>点我吧</p>
</div>

<script>
let div = document.querySelector("div");
div.onclick=function () {
alert('div');
}
</script>

在div里的p元素同样也绑定一个点击事件, 那么会先弹出p, 然后再弹出div

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div>
<p>点我吧</p>
</div>

<script>
let div = document.querySelector("div");
div.onclick=function () {
alert('div');
};

let p = document.querySelector("p");
p.onclick=function () {
alert('p');
}
</script>

微软提出了名为事件冒泡(event bubbling)的事件流。事件冒泡可以形象地比喻为把一颗石头投入水中,泡泡会一直从水底冒出水面。也就是说,事件会从最内层的元素开始发生,一直向上传播,直到document对象。

因此上面的例子在事件冒泡的概念下发生click事件的顺序应该是p -> div -> body -> html -> document

阻止事件冒泡

jquery

这样就不再弹出div了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script src="https://cdn.bootcss.com/jquery/1.9.1/jquery.min.js"></script>
<div>
<p>点我吧</p>
</div>
<script>
let div = document.querySelector("div");
div.onclick=function (event) {
alert('div');
};

let p = document.querySelector("p");
p.onclick=function () {
alert('p');
event.stopPropagation();
}
</script>

js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div>
<p>点我吧</p>
</div>
<script>
let div = document.querySelector("div");
div.onclick=function (event) {
alert('div');
};

let p = document.querySelector("p");
p.onclick=function (e) {
alert('p');
if ( e && e.stopPropagation )
e.stopPropagation();
else
window.event.cancelBubble = true;
}
</script>

事件捕获

网景提出另一种事件流名为事件捕获(event capturing)。与事件冒泡相反,事件会从最外层开始发生,直到最具体的元素。

上面的例子在事件捕获的概念下发生click事件的顺序应该是document -> html -> body -> div -> p

addEventListener的第三个参数
第一个参数是需要绑定的事件,第二个参数是触发事件后要执行的函数。
而第三个参数默认值是false,表示在事件冒泡的阶段调用事件处理函数,如果参数为true,则表示在事件捕获阶段调用处理函数。

1
element.addEventListener(event, function, useCapture)

拓展浏览器的默认事件

下面这段代码我们点击后超链链是会发送跳转的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div>
<a href="http://www.google.com">点我吧</a>
</div>
<script>
let div = document.querySelector("div");
div.onclick=function (event) {
alert('div');
};

let p = document.querySelector("p");
p.onclick=function (e) {
alert('p');
if ( e && e.stopPropagation )
e.stopPropagation();
else
window.event.cancelBubble = true;
}
</script>

如果我们仅仅想点击,而不想让发送跳转呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<div>
<a href="http://www.google.com">点我吧</a>
</div>
<script>
let div = document.querySelector("div");
div.onclick=function (event) {
alert('div');
};

let a = document.querySelector("a");
a.onclick=function (e) {
alert('a');
if ( e && e.preventDefault )
e.preventDefault();
else
window.event.returnValue = false;
return false;
}
</script>
</body>

你看了一千个正确答案,还是要一秒一秒的等时间

任务超时

任务可以运行的最大秒数可以使用 Artisan 命令行上的 --timeout 开关指定:

1
php artisan queue:work --timeout=30

这个选项指定了 Laravel 队列处理器最多执行多长时间后就应该被关闭掉。有时候一个队列的子进程会因为很多原因僵死,比如一个外部的 HTTP 请求没有响应。这个 –timeout 选项会移除超出指定事件限制的僵死进程。

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php

namespace App\Jobs;

class ProcessPodcast implements ShouldQueue
{
/**
* 任务运行的超时时间。
*
* @var int
*/
public $timeout = 120;
}

任务过期

config/queue.php 配置文件里,每一个队列连接都定义了一个 retry_after 选项。这个选项指定了任务最多处理多少秒后就被当做失败重试了。比如说,如果这个选项设置为 90,那么当这个任务持续执行了 90 秒而没有被删除,那么它将被释放回队列。通常情况下,你应该把 retry_after 设置为最长耗时的任务所对应的时间。

二者区别

retry_after 配置选项和 --timeout 命令行选项是不一样的,但是可以同时工作来保证任务不会丢失并且不会重复执行。
--timeout应该永远都要比retry_after短至少几秒钟的时间。这样就能保证任务进程总能在失败重试前就被杀死了。如果你的--timeout选项大于retry_after 配置选项,你的任务可能被执行两次。

queue:listen 和 queue:work

queue:work在启动后,代码修改,queue:work不会再 Load 上下文,但是 queue:listen仍然会重新 Load 新代码。

队列监听

1
php artisan queue:work --daemon --quiet --queue=default --delay=3 --sleep=3 --tries=3
  • –quiet

不输出任何内容

  • –delay=3

一个任务失败后,延迟多长时间后再重试,单位是秒。这个值的设定我个人建议不要太短,因为一个任务失败(比如网络原因),重试时间太短可能会出现连续失败的情况。

  • –sleep=3

去 Redis 中拿任务的时候,发现没有任务,休息多长时间,单位是秒。这个值的设定要看你的任务是否紧急,如果是那种非常紧急的任务,不能等待太长时间。

  • –tries=3

定义失败任务最多重试次数。这个值的设定根据任务的重要程度来确定,一般 3 次比较适合。

  • –daemon

以守护进程的方式运行,在 supervisor 中一般要加这个 option,可以节省 CPU 使用。

Supervisor

使用 Supervisor 监听 Laravel 队列任务,其中 Supervisor 的配置如下

1
2
3
4
5
6
7
8
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/xxx.cn/artisan queue:work --queue=sendfile --tries=3 --daemon
autostart=true
autorestart=true
numprocs=8
redirect_stderr=true
stdout_logfile=/var/www/xxx.cn/worker.log

注意 numprocs = 8,代表开启 8 个进程来执行 command 中的命令, 这样就可以大大加快队列的执行速度,如果仅仅是一个进程, 队列就相当于顺序执行。

https://laravel-china.org/docs/laravel/5.5/queues#744c5b

Yesterday you said tomorrow.

嵌入Ajax调用

最简单的办法,就是在返回给客户端的HTML代码中,嵌入Ajax调用,或者,嵌入一个img标签,src指向要执行的耗时脚本。
这种方法最简单,也最快。服务器端不用做任何的调用。
但是缺点是,一般来说Ajax都应该在onLoad以后触发,也就是说,用户点开页面后,就关闭,那就不会触发我们的后台脚本了。
而使用img标签的话,这种方式不能称为严格意义上的异步执行。用户浏览器会长时间等待php脚本的执行完成,也就是用户浏览器的状态栏一直显示还在load。
当然,还可以使用其他的类似原理的方法,比如script标签等等。

popen()

1
2
resource popen ( string command, string mode );
//打开一个指向进程的管道,该进程由派生给定的 command 命令执行而产生。打开一个指向进程的管道,该进程由派生给定的 command 命令执行而产生。

所以可以通过调用它,但忽略它的输出。

1
pclose(popen("/home/xinchen/backend.php &", 'r'));

这个方法避免了第一个方法的缺点,并且也很快。但是问题是,这种方法不能通过HTTP协议请求另外的一个WebService,只能执行本地的脚本文件。并且只能单向打开,无法穿大量参数给被调用脚本。
并且如果,访问量很高的时候,会产生大量的进程。如果使用到了外部资源,还要自己考虑竞争。

使用CURL

这个方法,设置CUROPT_TIMEOUT为1(最小为1,郁闷)。也就是说,客户端至少必须等待1秒钟。

1
2
3
4
5
6
7
8
$ch = curl_init();

$curl_opt = array(CURLOPT_URL, 'http://www.example.com/backend.php',
CURLOPT_RETURNTRANSFER, 1,
CURLOPT_TIMEOUT, 1,);
curl_setopt_array($ch, $curl_opt);
curl_exec($ch);
curl_close($ch);

使用fsockopen

这个方法应该是最完美的,但是缺点是,你需要自己拼出HTTP的header部分。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$fp = fsockopen("www.example.com", 80, $errno, $errstr, 30);
if (!$fp) {
echo "$errstr ($errno)<br />\n";
} else {
$out = "GET /backend.php / HTTP/1.1\r\n";
$out .= "Host: www.example.com\r\n";
$out .= "Connection: Close\r\n\r\n";

fwrite($fp, $out);
/*忽略执行结果
while (!feof($fp)) {
echo fgets($fp, 128);
}*/
fclose($fp);
}

http://www.laruence.com/2008/04/14/318.html

如果不曾遇见过你,我本可以忍受孤独.
### 关于大O符号 我所能想到的大O符号最好的例子就是做算术。拿两个数字(123456和789012)举例。我们在学校里学到的基本算术操作是: 加法; 减法; 乘法; 除法。 它们中每一个都是一次操作或一个问题。为它们(加法,减法,乘法,除法)求解的方法就被叫做算法(algorithm)。

加法

加法是最简单的了。你把加数排成行,按列加上每个数字,把所加得的数的末位数字写到结果里。所加得的数的十位及其以上的数字转入下一列的计算中。

1
2
3
4
5
   123456
+
789012
------------
912468 我们一共操作了6

让我们假设在算法中,加上这些数是计算开销最大的操作。合乎情理的说,为了把这两个数加起来我们必须要加6次数字(并且可能进位到第7次)。如果我们把两个100位数相加,我们必须做100次加法操作。如果我们把两个10,000位数相加,我们必须做10,000次加法操作。
6位数相加就必须要加6次数字(并且可能进位到第7次)—最坏的情况
复杂度(complexity,就是操作的数量),对于加法中较大数的数字个数n,是直接成比例的。我们称这为O(n)或者线性复杂度(linear complexity)。

乘法

乘法就不同了。你把乘数排成行,取放在下面的乘数的第1个数字,把它逆序乘以上面乘数的每一个数字。下面乘数的其余数字也这样做。所以为了乘我们的两个6位数乘数,我们必须做36次乘法操作。我们还需要做10或11次列的加法操作来得到最终结果。

1
2
3
4
5
6
7
8
9
10
  23
x
34
-------
2
9
9
6
782
// 4次 乘法操作, 3次加法操作

如果我们有两个100位数相乘,我们需要做10,000次乘法操作和200次加法操作。两个100万位数相乘,我们需要做1万亿(1012)次乘法操作和200万次加法操作。
作为n平方的算法衡量尺度,这就是O(n2),即平方复杂度(quadratic complexity)。现在是时候介绍另一个重要概念了:

我们只关心复杂度最重要的部分。

敏锐的人可能已意识到,我们可以把操作次数表示为:n2 + 2n。但正如你所看到的,我们的两个100万位数相乘的例子,第二个 2n 无关紧要(在那个阶段,2n只占操作总量的0.0002%)。

有人注意到我们在这里假设场景为最坏的情况。当我们做6位数乘法时,如果其中一个是4位数另一个是6位数,那么我们只需做24次乘法操作。然而,对于那个’n’,我们仍然计算最坏情况,即乘数都是6位数的情况。因此,大O符号是关于一个算法的最坏情况的。

电话簿

我所能想到的下一个最棒的例子就是电话簿,通常叫做白页电话簿或者其它类似名字,因国而异。但我要谈论的是这种电话薄,这种电话薄把人按这样的顺序排列:姓、缩写或名、地址、然后是电话号码。

现在,如果你要指示计算机在一个包含1,000,000个名字的电话簿中查找”John Smith”的电话号码,你会怎么做?忽略也许你能猜测出S从电话簿哪里开始的事实(假设你不能猜测),你会怎么做?

一种典型的实现也许是,打开电话簿的正中间,取第500,000条记录,把它和”Smith”进行比较。如果这恰好就是”Smith,John”,那我们真幸运。然而,”John Smith”更有可能在其前面或后面。如果在后面,那么我们把电话簿后面一半从中间划分开,然后重复之前的过程;如果在前面,那么我们把第一半从中间划分开,然后重复之前的过程。以此类推。

这种算法叫做二分搜索(binary search)

因此,如果你想要在包含100万名字的电话簿中查找一个名字,事实上,通过这种算法,最多20次,你能找到任何名字。在比较搜索算法中,我们决定把比较操作作为我们的’n’。

对于有3个名字的电话簿,最多需2次比较。
对于有7个名字的电话簿,最多需3次比较。
对于有15个名字的电话簿,最多需4次比较。

对于有1,000,000个名字的电话簿,最多需20次比较。
这简直好得难以置信,不是吗?

用大O术语就是O(log n),即对数复杂度(logarithmic complexity)。现在问题中的对数可以是ln(底数为e),log10,log2 或者以其它为底数,这无关紧要,它仍然是O(log n),正如O(2n2) 和 O(100n2) 都记为 O(n2)。

现在,值得花时间说明一下,对于算法,大O符号能够被用于决定3种情况:

最好情况(Best Case):在电话簿的搜索中,最好情况是我们比较了1次就找到了名字。这就是O(1),即常数复杂度(constant complexity);
期望情况(Expected Case):正如上面讨论过的,复杂度是O(log n);
最坏情况(Worst Case):也是O(log n)。
通常我们不关心最好情况。我们对期望和最坏情况感兴趣。有时,期望情况更重要,有时最坏情况更重要。

回到电话簿的例子上来。

如果你有一个电话号码,想要查找名字,要怎么做呢?警察有一个相反(按电话号码排列)的电话簿,但是对于一般公众,这样的查询会被拒绝,是吧?技术上,你能在普通电话簿中查找一个号码。要怎么做呢?

你从第一个名字开始比较号码。如果吻合,很棒,如果不吻合,你移到下一条记录。你必须这样做,因为电话簿是无序(unordered)的(电话号码的排列是无序的)。

因此,查找一个名字:

最好情况(Best Case):O(1);
期望情况(Expected Case):O(n)(对应500,000);
最坏情况(Worst Case):O(n)(对应1,000,000)。

http://blog.jobbole.com/55184/#article-comment
https://stackoverflow.com/questions/487258/what-is-a-plain-english-explanation-of-big-o-notation/487278#487278

哪个更痛苦,努力还是后悔?
### 时间戳 时间戳是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数 ### php获取
1
2
3
4
5
// pure php
time()

// Carbon\Carbon
Carbon::now()->timestamp

PHP Date() 函数

date(format,timestamp)

1
2
3
4
5
6
<?php
echo "今天是 " . date("Y/m/d") . "<br>";
echo "今天是 " . date("Y.m.d") . "<br>";
echo "今天是 " . date("Y-m-d") . "<br>";
echo "今天是 " . date("l");
?>

php 获取时间今天明天昨天时间戳

1
2
3
4
5
6
7
8
9
10
11
12
echo "今天:".date("Y-m-d")."<br>";     
echo "昨天:".date("Y-m-d",strtotime("-1 day")), "<br>";
echo "明天:".date("Y-m-d",strtotime("+1 day")). "<br>";
echo "一周后:".date("Y-m-d",strtotime("+1 week")). "<br>";
echo "一周零两天四小时两秒后:".date("Y-m-d G:H:s",strtotime("+1 week 2 days 4 hours 2 seconds")). "<br>";
echo "下个星期四:".date("Y-m-d",strtotime("next Thursday")). "<br>";
echo "上个周一:".date("Y-m-d",strtotime("last Monday"))."<br>";
echo "一个月前:".date("Y-m-d",strtotime("last month"))."<br>";
echo "一个月后:".date("Y-m-d",strtotime("+1 month"))."<br>";
echo "十年后:".date("Y-m-d",strtotime("+10 year"))."<br>";

//strtotime()函数的作用是将日期时间描述解析为 Unix 时间戳

python获取

1
2
import time
time.time()

小时候,向喜欢的姑娘展示快 长大后,向喜欢的姑娘展示慢
### 作用域 如果一个变量在函数体内部申明,则该变量的作用域为整个函数体,在函数体外不可引用该变量:
1
2
3
4
5
6
7
8
'use strict';

function foo() {
var x = 1;
x = x + 1;
}

x = x + 2; // ReferenceError! 无法在函数体外引用变量x
如果两个不同的函数各自申明了同一个变量,那么该变量只在各自的函数体内起作用。换句话说,不同函数内部的同名变量互相独立,互不影响:
1
2
3
4
5
6
7
8
9
10
11
'use strict';

function foo() {
var x = 1;
x = x + 1;
}

function bar() {
var x = 'A';
x = x + 'B';
}
由于JavaScript的函数可以嵌套,此时,内部函数可以访问外部函数定义的变量,反过来则不行:
1
2
3
4
5
6
7
8
9
'use strict';

function foo() {
var x = 1;
function bar() {
var y = x + 1; // bar可以访问foo的变量x!
}
var z = y + 1; // ReferenceError! foo不可以访问bar的变量y!
}
如果内部函数和外部函数的变量名重名怎么办? JavaScript的函数在查找变量时从自身函数定义开始,从“内”向“外”查找。如果内部函数定义了与外部函数重名的变量,则内部函数的变量将“屏蔽”外部函数的变量。
1
2
3
4
5
6
7
8
9
10
11
'use strict';

function foo() {
var x = 1;
function bar() {
var x = 'A';
alert('x in bar() = ' + x); // 'A'
}
alert('x in foo() = ' + x); // 1
bar();
}

不在任何函数内定义的变量就具有全局作用域。实际上,JavaScript默认有一个全局对象window,全局作用域的变量实际上被绑定到window的一个属性:

1
2
3
4
5
'use strict';

var course = 'Learn JavaScript';
alert(course); // 'Learn JavaScript'
alert(window.course); // 'Learn JavaScript'

由于函数定义有两种方式,以变量方式var foo = function () {}定义的函数实际上也是一个全局变量,因此,顶层函数的定义也被视为一个全局变量,并绑定到window对象:

1
2
3
4
5
6
7
8
'use strict';

function foo() {
alert('foo');
}

foo(); // 直接调用foo()
window.foo(); // 通过window.foo()调用

进一步大胆地猜测,我们每次直接调用的alert()函数其实也是window的一个变量:

变量提升

JavaScript的函数定义有个特点,它会先扫描整个函数体的语句,把所有申明的变量“提升”到函数顶部,JavaScript引擎自动提升了变量y的声明,但不会提升变量y的赋值。

考虑下面的代码:

1
2
var myvar = 'my value';
alert(myvar); // my value

Okay, 当然,弹出的结果肯定是”my value”,但是,跟着我,让我下面创建个方法,弹出相同的值:

1
2
3
4
5
var myvar = 'my value';

(function() {
alert(myvar); // my value
})();

好吧,好吧,仍然很明显,我知道。现在,让我们加点猛的调料,在匿名函数内部创建一个同名的局部变量。

1
2
3
4
5
6
var myvar = 'my value';

(function() {
alert(myvar); // undefined
var myvar = 'local value';
})();

在当前的作用域内,无论在哪里变量声明,在幕后,其都在顶部被“预解析”了。不过,仅声明被“预解析”。该变量即使初始化,其当前的值,在作用域的顶部,也会被设置成undefined。
恩,现在让我们好好的破译下这个“声明”和“初始化”,以var joe = ‘plumber’;为模特吧。
声明(Declaration)

1
var joe; // the declaration

初始化(Initialization)

1
joe = 'plumber'; // the initialization 

现在,我们知道了这些术语的意思,就可以更好的理解到底背地里都干了些什么勾当,请看下面的伪函数:

1
2
3
4
5
6
7
8
(function() {
var a = 'a';
// 一行代码
var b = 'b';
// 更多行的代码
var c= 'c'; // antipattern
// 最后一行脚本
})();

需注意,上面的这做法是不太好的。但是,先不管这个,在程序的背后,这个变量声明无论在函数作用域的什么地方,都被置顶解析了,就像下面这样:

1
2
3
4
5
6
7
8
9
(function() {
var a, b, c; // variables declared
a = 'a';
// 一行代码
b = 'b'; // initialized
// 更多行的代码
c= 'c'; // initialized
// 最后一行脚本
})();

http://www.zhangxinxu.com/wordpress/2010/10/%E7%BF%BB%E8%AF%91-%E8%A7%A3%E9%87%8Ajavascript%E7%9A%84%E2%80%9C%E9%A2%84%E8%A7%A3%E6%9E%90%E7%BD%AE%E9%A1%B6%E8%A7%A3%E6%9E%90%E2%80%9D/
https://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000/0014344993159773a464f34e1724700a6d5dd9e235ceb7c000

人总是喜欢自己没有的

一个有趣的现象

let

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<ul>
<li>111</li>
<li>222</li>
<li>333</li>
<li>444</li>
<li>555</li>
</ul>
<script>
var lis = document.querySelectorAll("li");
for (let i=0; i< lis.length; i++) {
var li = document.querySelectorAll("li")[i];
li.onclick = function () {
alert(i);
}
}
// 依次点击5个元素, 分别弹出0,1,2,3,4,5
</script>

var

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<ul>
<li>111</li>
<li>222</li>
<li>333</li>
<li>444</li>
<li>555</li>
</ul>
<script>
var lis = document.querySelectorAll("li");
for (var i=0; i< lis.length; i++) {
var li = document.querySelectorAll("li")[i];
li.onclick = function () {
alert(i);
}
}
// 依次点击5个元素, 分别弹出5,5,5,5,5,5
</script>

问题

在不使用let的情况下, 如何使点击每个li弹出对应的序号
事件委托
事件委托,通俗地来讲,就是把一个元素响应事件(click、keydown……)的函数委托到另一个元素;
一般来讲,会把一个或者一组元素的事件委托到它的父层或者更外层元素上,真正绑定事件的是外层元素,当事件响应到需要绑定的元素上时,会通过事件冒泡机制从而触发它的外层元素的绑定事件上,然后在外层元素上去执行函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<ul id="list">
<li>111</li>
<li>222</li>
<li>333</li>
<li>444</li>
<li>555</li>
</ul>
<script>
document.getElementById('list').addEventListener('click', function (e) {
// 兼容性处理
var event = e || window.event;
var target = event.target || event.srcElement;
// 判断是否匹配目标元素
if (target.nodeName.toLocaleLowerCase() === 'li') {
console.log('the content is: ', target.innerHTML);
}
});
</script>

let

let定义变量时和var有两个区别:块级作用域、不会变量提升和不能定义在块中已有标识符同名的变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function getValue(condition) {

if (condition) {
let value = "blue";

// other code

return value;
} else {

// value doesn't exist here

return null;
}

// value doesn't exist here
}

当用let定义value时,我们只能在if里面才能访问到value了,value变量也不会变量提升,从而我们在else里面不能访问到value。
let最常用的场景应该是for循环了:

1
2
for (let i = 0; i < len; i++) {
}

块级作用域
块级作用域就不用多说,就是用let定义的变量只在定义它的块中有效,出了这个块你就不能访问到它了。
变量提升
变量提升应该是在面试的时候会经常考到,例如:

1
2
3
4
5
6
7
8
9
<script>
function test () {
console.log(value);
var value = 'something';
console.log(value);
}
test();
// 结果: undefined something
</script>

我们用let重新定义上面的test()函数:

1
2
3
4
5
6
7
8
9
<script>
function test () {
console.log(value);
let value = 'something';
console.log(value);
}
test();
// 结果程序直接报错:Uncaught ReferenceError: value is not defined
</script>

同名变量
用var定义变量时,我们可以多次对它进行定义,例如:

1
2
3
var a = 1;
var a = 2;
var a = 3;

这样的代码是不会报错的,在let定义的相同块中定义同名变量时就会报错了,例如:

1
2
3
4
5
6
7
let a = 1;
let a = 2;

// or
var a = 1;
let a = 2;

要注意的是要与let定义时在相同的块中,下面的代码是不会出错的:

1
2
3
4
var a = 1;
if (something) {
let a = 2;
}

var

我们知道在ES6之前,是没有块级作用域这一说的,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function getValue(condition) {

if (condition) {
var value = "blue";

// other code

return value;
} else {

// value exists here with a value of undefined

return null;
}

// value exists here with a value of undefined
}

你可能会觉得在else里面无法访问到value变量,其实在js内部会造成变量提升,这意味着我们可以在else里面访问到value变量,只是它未初始化,所以其变量值为undefined。实际解析时代码可能像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function getValue(condition) {

var value;

if (condition) {
value = "blue";

// other code

return value;
} else {

return null;
}
}

var会造成变量提升

const

const除了具有let的块级作用域和不会变量提升外,还有就是它定义的是常量,在用const定义变量后,我们就不能修改它了,对变量的修改会默默的失败(在iojs中会抛出异常,在Chrome下会默默的失败)。例如:

1
2
3
4
const PI = 3.1415;
console.log(PI);
PI = 22;
console.log(PI);

http://cookfront.github.io/2015/05/28/es6-let-const/