如何用JavaScript获取图片的真实尺寸大小

网页页面上的图片尺寸似乎都千篇一律。我们最常见到的带有多图的文章页面中,图的大小通常是和页面的宽度一致,这样看起来,页面就是一个直筒形,这样的布局看多了就会觉得很单调。之所以形成这样的局面,我想很大程度上是因为老式浏览器的限制。但随着现代浏览器(火狐/谷歌/IE11)的普及,浏览器对页面设计的限制越来越少,Web程序员的想象能力能够得到极大的发挥。

比如,冷知识:你知道每个视窗都有的 [x] 是怎么来的吗?这篇文章中,很多图片超出了文本宽度的限制,给人一种参差错落的感觉,同时,让大图片以其真实的尺寸展示,给人以更震撼的感觉。
阅读全文

程序员的十大烦恼

每个程序员都有自己烦恼的事。不论这事指的是范围蠕变(scope creep),还是 指匈牙利变量命名 (Hungarian notation),还是有臭味的同事,我们都明白,这是我们有我们行业里的特定的烦恼。 下面要说的就是十大让程序员们烦恼的事情,这是我从最 近的在StackOverflow上的一个调查里整理出来的,并且掺杂了一些我个人的经验:

10. 注释 — 只解释了“how”却没有解释“why”

入门级的编程课程通常会教育学生们写代码前先写注释、而且要尽量多注释。 这种教育的出发点是“多注释肯定比少注释好、少注释肯定比没注释好”。 可不幸的是,很多的程序员把这当成了一种任务,对每一行代码都注释一下。 这就是为什么会经常看到像Jeff Atwood在他的博客文章Coding Without Comments提到的代码:

1 	r = n / 2; // 让 r 等于 n 除以 2
2 	
3 	// 当 r - (n/r) 大于 t 时进行循环
4 	while ( abs( r - (n/r) ) > t ) {
5 	 r = 0.5 * ( r + (n/r) ); // 设置 r 等于 r + (n/r) 的一半
6 	}

经过这样的注释,你否明白了这段代码是干什么的?的确,我也没明白。 问题就在于,虽然有大量的注释,可它们只是描述了代码是干什么了,却没有说明代码为什么要这样写。

现在,请看一下我们采用另外一种方式对同一段代码进行的注释:

1 	// 使用牛顿-Raphson算法求n的平方根近似值
2 	r = n / 2;
3 	
4 	while ( abs( r - (n/r) ) > t ) {
5 	 r = 0.5 * ( r + (n/r) );
6 	}

这就好多了!也许我们还是不能完全明白这段代码的作用,但至少是有了一点方向了。
阅读全文

kmp算法实现原理及简单示例

以前看过kmp算法,当时接触后总感觉好深奥啊,抱着数据结构的数啃了一中午,最终才大致看懂,后来提起kmp也只剩下“奥,它是做模式匹配的”这点干货。最近有空,翻出来算法导论看看,原来就是这么简单(下不说程序实现,思想很简单)。

模式匹配的经典应用:从一个字符串中找到模式字串的位置。如“abcdef”中“cde”出现在原串第三个位置。从基础看起

朴素的模式匹配算法

A:abcdefg  B:cde

首先B从A的第一位开始比较,B++==A++,如果全部成立,返回即可;如果不成立,跳出,从A的第二位开始比较,以此类推。

/*
 *侯凯,2014-9-16
 *功能:模式匹配
 */
#include<iostream>
#include <string>
using namespace std;

int index(char *a,char *b)
{
    int tarindex = 0;
    while(a[tarindex]!='\0')
    {
        int tarlen = tarindex;
        int patlen;
        for(patlen=0;b[patlen]!='\0';patlen++)
        {
            if(a[tarlen++]!=b[patlen])
            {
                break;
            }
        }
        if(b[patlen]=='\0')
        {
            return tarindex;
        }
        tarindex++;
    }
    return -1;
}
int main()
{
    char *a = "abcdef";
    char *b = "cdf";
    cout<<index(a,b)<<endl;
      system("Pause");
}

思路朴实无华,十分有效,但是时间复杂度是O(mn),m、n分别是字符串和模式串的长度。模式匹配是一个常见的应用问题,用的广了,就有人想法去优化了。Rabin-Karp算法、有限自动机等等,前仆后继,最终出现了KMP(Knuth-Morris-Pratt)算法。

kmp算法

a1

 

 

优化的地方:如果我们知道模式中a和后面的是不相等的,那么第一次比较后,发现后面的的4个字符均对应相等,可见a下次匹配的位置可以直接定位到f了。说明主串对应位置i的回溯是不必要的。这是kmp最基本最关键的思想和目标。
阅读全文

Python 代码性能优化技巧

选择了脚本语言就要忍受其速度,这句话在某种程度上说明了 python 作为脚本的一个不足之处,那就是执行效率和性能不够理想,特别是在 performance 较差的机器上,因此有必要进行一定的代码优化来提高程序的执行效率。如何进行 Python 性能优化,是本文探讨的主要问题。本文会涉及常见的代码优化方法,性能优化工具的使用以及如何诊断代码的性能瓶颈等内容,希望可以给 Python 开发人员一定的参考。

Python 代码优化常见技巧

代码优化能够让程序运行更快,它是在不改变程序运行结果的情况下使得程序的运行效率更高,根据 80/20 原则,实现程序的重构、优化、扩展以及文档相关的事情通常需要消耗 80% 的工作量。优化通常包含两方面的内容:减小代码的体积,提高代码的运行效率。
阅读全文

从MVC框架看MVC架构的设计

尽管MVC早已不是什么新鲜话题了,但是从近些年一些优秀MVC框架的设计上,我们还是会发现MVC在架构设计上的一些新亮点。本文将对传统MVC架构中的一些弊病进行解读,了解一些优秀MVC框架是如何化解这些问题的,揭示其中所折射出的设计思想与设计理念。

 

MVC回顾

 

作为一种经典到不能再经典的架构模式,MVC的成功有其必然的道理,这个道理不同的人会有不同的解读,笔者最认同的一种观点是:通过把职责、性质相近的成分归结在一起,不相近的进行隔离,MVC将系统分解为模型、视图、控制器三部分,每一部分都相对独立,职责单一,在实现过程中可以专注于自身的核心逻辑。MVC是对系统复杂性的一种合理的梳理与切分,它的思想实质就是“关注点分离”。至于MVC三元素的职责划分与相互关系,这里不再赘述,下图给出了非常细致的说明。

a1

 

图1:MVC组件的功能和关系

View与Controller的解耦:mediator+二次事件委派

 

笔者早年开发基于swing的GUI应用时,在架构MVC的实践过程中深刻体会到了view与controller之间的紧密耦合问题。在很多事件驱动的GUI框架里,如swing,用户对view的任何操作都会触发一个事件,然后在listener的响应方法里进行处理。如果让view自己注册成为事件的listener,则必须要在view中加入对controller的引用,这不是MVC希望看到的,因为这样view和controller就形成了紧密的耦合关系。若将controller注册为listener,则事件响应将由controller承担,这又会导致controller处理其不该涉及的展现逻辑,造成view和controller难以解耦的原因在于:多数的用户请求都包含一定成分的展现逻辑和一定成分的业务逻辑,两种逻辑揉合在一个请求里,在处理的时候,view与controller很难合理地分工。解决这一问题的关键是要在view与controller之间建立一种可以将展现逻辑与业务逻辑进行有效分割的机制,在这方面,PureMVC[ii]的设计非常值得参考,它通过引入mediator+二次事件委派机制很好的解决了view与controller之间的紧耦合问题。

 

Mediator是一种设计模式,这种模式在组件化的图形界面框架中似乎有着普遍的应用场景,即使是在四人帮的《设计模式》一书中,也使用了一个图形界面程序的示例来讲解mediator。mediator的设计用意在于通过一个媒介对象,完成一组对象的交互,避免对象间相互引用,产生复杂的依赖关系。mediator应用于图形界面程序时,往往作为一组关系紧密的图形组件的交互媒介,完成组件间的协调工作(比如点选某一按钮,其他组件将不可用)。在PureMVC中,mediator被广泛应用,其定位也发生了微妙的变化,它不再只是图形组件间的媒介,同时也成为了图形组件与command之间的媒介,这使得它不再是可选的,而是成为了架构中的必需设施。对应到传统MVC架构中,mediator就是view与controller之间的媒介(当然,也依然是view之间的媒介),所有从view发出的用户请求都经过了mediator再传递给controller,它的出现在一定程度上缓解了view与controller的紧密耦合问题。
阅读全文

解析提高PHP执行效率的50个技巧

a1

 

1、用单引号代替双引号来包含字符串,这样做会更快一些。因为PHP会在双引号包围的字符串中搜寻变量, 单引号则不会,注意:只有echo能这么做,它是一种可以把多个字符串当作参数的”函数”(译注:PHP手册中说echo是语言结构,不是真正的函数,故 把函数加上了双引号)。

2、如果能将类的方法定义成static,就尽量定义成static,它的速度会提升将近4倍。

3、$row['id'] 的速度是$row[id]的7倍。

4、echo 比 print 快,并且使用echo的多重参数(译注:指用逗号而不是句点)代替字符串连接,比如echo $str1,$str2。

5、在执行for循环之前确定最大循环数,不要每循环一次都计算最大值,最好运用foreach代替。

6、注销那些不用的变量尤其是大数组,以便释放内存。
阅读全文

你需要立刻突破的十大编程禁忌

original

 

程序员在编程的时候难免会犯错误,但如果不从错误中吸取教训,那么习惯成自然,你会经常犯错的。从错误中不断的学习,锻炼好的行为习惯有助于事业上的稳定。这就是我们如何将小麦从糟糠中区别出来以及如何避免编程禁忌的绝佳经验。此外,最重要的就是可以为客户带来更好的用户体验。

1. 不提升非技术技能

我们认为非技术技能是项目成功的主要因素。这些非技术技能也可以称之为“软技能”,总体上来说,它已经被公司证明为能够驾驭企业和客户之间的长期商业关系,因此也能决定公司的成长发展路径。一些关键的软技能指标包括:

b1

 

a.纪律——这是最重要的特征之一,缺乏纪律,最终会让这个开发团队在开发能力上“缺乏自信”。解决这一问题的矫正方法就是每天制定详细的to-do清单:兑现你的承诺、完成你开始做的事情、避免多重任务,因为这些往往会让你的生活产生混乱。
阅读全文

早该知道的7个JavaScript技巧

我写JavaScript代码已经很久了,都记不起是什么年代开始的了。对于JavaScript这种语言近几年所取得的成就,我感到非常的兴奋;我很幸运也是这些成就的获益者。我写了不少的文章,章节,还有一本专门讨论它的书,然而,我现在依然能发现一些关于这种语言的新知识。下面的描述的就是过去让我不由得发出“啊!”的感叹的编程技巧,这些技巧你应该现在就试试,而不是等着未来的某个时候偶然的发现它们。

简洁写法

JavaScript里我最喜欢的一种东西就是生成对象和数组的简写方法。在过去,如果你想创建一个对象,你需要这样:

1       var car = new Object();
2 	car.colour = 'red';
3 	car.wheels = 4;
4 	car.hubcaps = 'spinning';
5 	car.age = 4;

下面的写法能够达到同样的效果:

1 	var car = {
2 		colour:'red',
3 		wheels:4,
4 		hubcaps:'spinning',
5 		age:4
6 	}

简单多了,你不需要反复使用这个对象的名称。这样 car 就定义好了,也许你会遇到 invalidUserInSession 的问题,这只有你在使用IE时会碰到,只要记住一点,不要右大括号前面写逗号,你就不会有麻烦。

另外一个十分方便的简写是针对数组的。传统的定义数组的方法是这样:

1 	var moviesThatNeedBetterWriters = new Array(
2 		'Transformers','Transformers2','Avatar','IndianaJones 4'
3 	);

简写版的是这样:

1 	var moviesThatNeedBetterWriters = [
2 		'Transformers','Transformers2','Avatar','IndianaJones 4'
3 	];

对于数组,这里有个问题,其实没有什么图组功能。但你会经常发现有人这样定义上面的 car ,就像这样

1 	var car = new Array();
2 	car['colour'] = 'red';
3 	car['wheels'] = 4;
4 	car['hubcaps'] = 'spinning';
5 	car['age'] = 4;

数组不是万能的;这样写不对,会让人困惑。图组实际上是对象的功能,人们混淆了这两个概念。

另外一个非常酷的简写方法是使用与三元条件符号。你不必写成下面的样子…

1 	var direction;
2 	if(x < 200){
3 		direction = 1;
4 	} else {
5 		direction = -1;
6 	}

 


你可以使用三元条件符号简化它:

1 var direction = x < 200 ? 1 : -1;

当条件为true 时取问号后面的值,否则取冒号后面的值。

用 JSON 形式存储数据

在我发现JSON之前,我使用各种疯狂的方法把数据存贮在JavaScript固有的数据类型里面,例如:数组,字符串,中间夹杂着容易进行拆分的标志符号以及其它的令人讨厌的东西。Douglas Crockford 发明了JSON 之后,一切全变了。使用JSON,你可以使用JavaScript自有功能把数据存贮成复杂的格式,而且不需要再做其它的额外转换,直接可以访问使用。JSON 是 “JavaScript Object Notation” 的缩写,它用到了上面提到的两种简写方法。于是,如果你想描述一个乐队,你可能会像这样写:

01 var band = {
02 "name":"The Red Hot Chili Peppers",
03 "members":[
04 {
05 "name":"Anthony Kiedis",
06 "role":"lead vocals"
07 },
08 {
09 "name":"Michael 'Flea' Balzary",
10 "role":"bass guitar, trumpet, backing vocals"
11 },
12 {
13 "name":"Chad Smith",
14 "role":"drums,percussion"
15 },
16 {
17 "name":"John Frusciante",
18 "role":"Lead Guitar"
19 }
20 ],
21 "year":"2009"
22 }

你可以在JavaScript里直接使用JSON,可以把它封装在函数里,甚至作为一个API的返回值形式。我们把这称作 JSON-P ,很多的API都使用这种形式。
你可以调用一个数据提供源,在script代码里直接返回 JSON-P 数据:

01
<div id="delicious"></div>
<script type="text/javascript">// <![CDATA[
02 function delicious(o){
03 var out = '
<ul>';
04 for(var i=0;i<o.length;i++){
05 out += '
<li><a href="' + o[i].u + '">' +
06 o[i].d + '</a></li>

';
07 }
08 out += '</ul>

';
09 document.getElementById('delicious').innerHTML = out;
10 }
11 
// ]]></script>
12<script type="text/javascript" src="http://feeds.delicious.com/v2/json/codepo8/javascript?count=15&amp;callback=delicious"></script>

这是调用 Delicious 网站提供的 Web service 功能,获得JSON格式的最近的无序书签列表。
阅读全文

笑谈 关于程序员懒惰之极的确凿证据

程序员很懒是出了名的,但很多书里都说程序员的懒是一种美德,但懒是应该有节制的,应该分场合。比如下面的这些懒惰的行为,相信没有人会欣赏他们的这种表现。

1、躺着编程。另外一个也是躺在床上工作的职业是妓女。

a1
阅读全文

C语言指针安全及指针使用问题

指针是C语言的灵魂,同时也是最让初学者头痛的一个知识点,本文主要分项了C语言指针安全及指针使用问题。

指针的声明与初始化

1、不恰当的指针声明

考虑如下的声明:

int* ptr1, ptr2; // ptr1为指针,ptr2为整数

正确的写法如下:

int* ptr1, *ptr2;

用类型定义代替宏定义是一个好的习惯,类型定义允许编译器检查作用域规则,而宏定义不一定会。

使用宏定义辅助声明变量,如下所示:

#define PINT int*
PINT ptr1, ptr2;

不过结果和前面所说的一致,更好的方法是使用下面的类型定义:

typedef int* PINT;
PINT ptr1, ptr2;

 2、使用指针未初始化

在使用指针之前未初始化会导致运行时错误,如下面的代码:

int* p;
...
printf("%d\n", *p);

指针p未被初始化,可能含有垃圾数据

3、处理未初始化指针

总是用NULL来初始化指针
用assert函数
用第三方工具
把指针初始化为NULL更容易检查是否使用正确,即便这样,检查空值也比较麻烦,如下所示:

int *pi = NULL;
...
if(pi == NULL) {
//不应该解引pi
} else {
//可以使用pi
}

 
我们可以使用assert函数来测试指针是否为空值:

assert(pi != NULL);

指针的使用问题

缓冲区溢出

缓冲区溢出是指当计算机向缓冲区内填充数据位数时超过了缓冲区本身的容量,使得溢出的数据覆盖在合法数据上,理想的情况是程序检查数据长度并不允许输入超过缓冲区长度的字符,但是绝大多数程序都会假设数据长度总是与所分配的储存空间相匹配,这就为缓冲区溢出埋下隐患。操作系统所使用的缓冲区又被称为”堆栈”.。在各个操作进程之间,指令会被临时储存在”堆栈”当中,”堆栈”也会出现缓冲区溢出。

下面几种情况可能导致缓冲区的溢出:

访问数组元素时没有检查索引值
对数组指针做指针算术运算时不够小心
用gets这样的函数从标准输入读取字符串
误用strcpy和strcat这样的函数
1、测试NULL

使用malloc这样的函数的时候一定要检查返回值,否则可能会导致程序的非正常终止,下面是一般的方法:

float *vector = malloc(20 * sizeof(float));
if(vector == NULL) {
//malloc分配内存失败
} else {
//处理vector
}

2、错误使用解引操作

声明和初始化指针的常用方法如下:

int num;
int *pi = #

下面是一种看似等价但是错误的声明方法:

int num;
int *pi;
*pi = #

3、迷途指针

参见《C迷途指针》

4、越过数组边界访问内存

没有什么可以阻止程序访问为数组分配的空间以外的内存,下面的例子中,我们声明并初始化了三个数组来说明这种行为:

#include
int main()
{
char firstName[8] = "1234567";
char middleName[8] = "1234567";
char lastName[8] = "1234567";
middleName[-2] = 'X';
middleName[0] = 'X';
middleName[10] = 'X';
printf("%p %s\n", firstName, firstName);
printf("%p %s\n", middleName, middleName);
printf("%p %s\n", lastName, lastName);
return 0;
}

运行结果如下:

f

下图说明了内存分配情况:

d

5、错误计算数组长度

将数组传给函数时,一定要同时传递数组长度,这个信息帮助函数避免越过数组边界

#include
void replace(char buffer[], char replacement, size_t size)
{
size_t count = 0;
while(*buffer && count++ < size) {
*buffer = replacement;
buffer++;
}
}
int main()
{
char name[8];
strcpy(name, "Alexander");
replace(name, '+', sizeof(name));
printf("%s\n", name);
return 0;
}

6、错误使用sizeof操作符

其中一个例子是试图检查指针边界但方法错误

#include
int main()
{
int buffer[20];
int *pbuffer = buffer;
for(int i = 0; i < sizeof(buffer); i++) {
*(pbuffer++) = 0;
}
return 0;
}

 
改为:i < sizeof(buffer) / sizeof(int);

7、有界指针

有界指针是指指针的使用被限制在有效的区域内,C没有对这类指针提供直接的支持,但是可以自己显示地确保。如下所示:

#define SIZE 32

char name[SIZE];

char *p = name;

if(name != NULL) {

if(p >= name && p < name + SIZE) {
//有效指针,继续
} else {
//无效指针,错误分支
}
}

 

一种有趣的变化是创建一个指针检验函数;

下面的代码定义一个函数消除无效指针:

int valid(void *ptr) {
return (ptr != NULL);
}

下面的代码依赖于_etext的地址,定义于很多的类linux操作系统,在windows上无效:

#include 
#include

int valid(void *p) {
extern char _etext;
return (p != NULL) && ((char*) p > &_etext);
}

int global;

int main(void) {
int local;

printf("pointer to local var valid? %d\n", valid(&local));
printf("pointer to static var valid? %d\n", valid(&global));
printf("pointer to function valid? %d\n", valid((void *)main));

int *p = (int *) malloc(sizeof(int));
printf("pointer to heap valid? %d\n", valid(p));
printf("pointer to end of allocated heap valid? %d\n", valid(++p));
free(--p);
printf("pointer to freed heap valid? %d\n", valid(p));
printf("null pointer valid? %d\n", valid(NULL));

return 0;
}

 

在linux平台运行结果如下:

pointer to local var valid? 1
pointer to static var valid? 1
pointer to function valid? 0
pointer to heap valid? 1
pointer to end of allocated heap valid? 1
pointer to freed heap valid? 1
null pointer valid? 0

 

另一种方法是利用ANSI-C和C++的边界检查工具(CBMC)

原文来自:codingwu