邵珠庆の日记 生命只有一次,你可以用它来做很多伟大的事情–Make the world a little better and easier


283月/13

55张图看懂这个世界

发布在 邵珠庆

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.无法看清自己。
39.邪恶的内心,远比邪恶的外表可怕得多。
40.兄弟,你的镰刀out了。
41.已经有很多伤口了,但是还是在不断受伤。
42.要想受到膜拜,那么就同样的不怕受到唾弃。
43.男人藏在背后的往往是鲜花,而女人多数是利刃。
44.有谁看见过小丑的悲伤呢?
45.拥有一个诚实的敌人,好过一个虚伪的朋友。
46.有时候,你不知道自己的话有多伤人。
47.有危险在靠近,跟你最亲近的全然不知,而被你抛弃的心知肚明,却无法告诉你。
48.再忙也给家里打个电话。
49.至诚的外表和诚敬的行为演示一颗魔鬼般的心。
50.众人皆醉唯我独醒。
51.周几最高兴,自己懂得。

 

 
52.左脑掌控理性,右脑掌控感性,天才的精神世界都是很奇异。


53.有些人就是拥有的太多才不懂得珍惜。

图片
54.你说无论我变成什么样子,你都不会离开我,于是我摘下了面具,却看到了落荒而逃的你。
图片

273月/13

编写可读代码的艺术

发布在 邵珠庆

代码为什么要易于理解

“Code should be written to minimize the time it would take for someone else to understand it.”

日常工作的事实是:

  • 写代码前的思考和看代码的时间远大于真正写的时间
  • 读代码是很平常的事情,不论是别人的,还是自己的,半年前写的可认为是别人的代码
  • 代码可读性高,很快就可以理解程序的逻辑,进入工作状态
  • 行数少的代码不一定就容易理解
  • 代码的可读性与程序的效率、架构、易于测试一点也不冲突

整本书都围绕“如何让代码的可读性更高”这个目标来写。这也是好代码的重要标准之一。

如何命名

变量名中应包含更多信息

使用含义明确的词,比如用download而不是get,参考以下替换方案:

1
2
3
4
send -> deliver, dispatch, announce, distribute, route
 find -> search, extract, locate, recover
start -> lanuch, create, begin, open
 make -> create,set up, build, generate, compose, add, new

避免通用的词

tmpretval这样词,除了说明是临时变量和返回值之外,没有任何意义。但是给他加一些有意义的词,就会很明确:

1
2
3
tmp_file = tempfile.NamedTemporaryFile()
...
SaveData(tmp_file, ...)

不使用retval而使用变量真正代表的意义:

1
sum_squares += v[i]; // Where's the "square" that we're summing? Bug!

嵌套的for循环中,ij也有同样让人困惑的时候:

1
2
3
4
for (int i = 0; i < clubs.size(); i++)
 for (int j = 0; j < clubs[i].members.size(); j++)
 for (int k = 0; k < users.size(); k++) if (clubs[i].members[k] == users[j])
 cout << "user[" << j << "] is in club[" << i << "]" << endl;

换一种写法就会清晰很多:

1
if (clubs[ci].members[mi] == users[ui]) # OK. First letters match.

所以,当使用一些通用的词,要有充分的理由才可以。

使用具体的名字

CanListenOnPort就比ServerCanStart好,can start比较含糊,而listen on port确切的说明了这个方法将要做什么。

--run_locally就不如--extra_logging来的明确。

增加重要的细节,比如变量的单位_ms,对原始字符串加_raw

如果一个变量很重要,那么在名字上多加一些额外的字就会更加易读,比如将string id; // Example: "af84ef845cd8"换成string hex_id;

1
2
3
4
Start(int delay) --> delay → delay_secs
 CreateCache(int size) --> size → size_mb
ThrottleDownload(float limit) --> limit → max_kbps
 Rotate(float angle) --> angle → degrees_cw

更多例子:

1
2
3
4
password -> plaintext_password
 comment -> unescaped_comment
 html -> html_utf8
 data -> data_urlenc

对于作用域大的变量使用较长的名字

在比较小的作用域内,可以使用较短的变量名,在较大的作用域内使用的变量,最好用长一点的名字,编辑器的自动补全都可以很好的减少键盘输入。对于一些缩写前缀,尽量选择众所周知的(如str),一个判断标准是,当新成员加入时,是否可以无需他人帮助而明白前缀代表什么。

合理使用_-等符号,比如对私有变量加_前缀。

1
2
3
4
5
6
7
8
var x = new DatePicker(); // DatePicker() 是类的"构造"函数,大写开始
var y = pageHeight(); // pageHeight() 是一个普通函数
 
var $all_images = $("img"); // $all_images 是jQuery对象
var height = 250; // height不是
 
//id和class的写法分开
<div id="middle_column" class="main-content"> ...

命名不能有歧义

命名的时候可以先想一下,我要用的这个词是否有别的含义。举个例子:

1
results = Database.all_objects.filter("year <= 2011")

现在的结果到底是包含2011年之前的呢还是不包含呢?

使用minmax代替limit

1
2
3
4
5
6
7
CART_TOO_BIG_LIMIT = 10
 if shopping_cart.num_items() >= CART_TOO_BIG_LIMIT:
 Error("Too many items in cart.")
 
MAX_ITEMS_IN_CART = 10
 if shopping_cart.num_items() > MAX_ITEMS_IN_CART:
 Error("Too many items in cart.")

对比上例中CART_TOO_BIG_LIMITMAX_ITEMS_IN_CART,想想哪个更好呢?

使用firstlast来表示闭区间

1
2
3
4
print integer_range(start=2, stop=4)
# Does this print [2,3] or [2,3,4] (or something else)?
 
set.PrintKeys(first="Bart", last="Maggie")

firstlast含义明确,适宜表示闭区间。

使用beiginend表示前闭后开(2,9))区间

1
2
3
PrintEventsInRange("OCT 16 12:00am", "OCT 17 12:00am")
 
PrintEventsInRange("OCT 16 12:00am", "OCT 16 11:59:59.9999pm")

上面一种写法就比下面的舒服多了。

Boolean型变量命名

1
bool read_password = true;

这是一个很危险的命名,到底是需要读取密码呢,还是密码已经被读取呢,不知道,所以这个变量可以使用user_is_authenticated代替。通常,给Boolean型变量添加ishascanshould可以让含义更清晰,比如:

1
2
SpaceLeft() --> hasSpaceLeft()
bool disable_ssl = false --> bool use_ssl = true

符合预期

1
2
3
4
5
6
7
public class StatisticsCollector {
 public void addSample(double x) { ... }
 public double getMean() {
 // Iterate through all samples and return total / num_samples
 }
 ...
}

在这个例子中,getMean方法遍历了所有的样本,返回总额,所以并不是普通意义上轻量的get方法,所以应该取名computeMean比较合适。

漂亮的格式

写出来漂亮的格式,充满美感,读起来自然也会舒服很多,对比下面两个例子:

1
2
3
4
5
6
7
8
9
10
11
12
class StatsKeeper {
 public:
 // A class for keeping track of a series of doubles
 void Add(double d); // and methods for quick statistics about them
 private: int count; /* how many so far
 */ public:
 double Average();
 private: double minimum;
 list<double>
 past_items
 ;double maximum;
};

什么是充满美感的呢:

1
2
3
4
5
6
7
8
9
10
11
12
// A class for keeping track of a series of doubles
// and methods for quick statistics about them.
class StatsKeeper {
 public:
 void Add(double d);
 double Average();
 private:
 list<double> past_items;
 int count; // how many so far
 double minimum;
 double maximum;
};

考虑断行的连续性和简洁

这段代码需要断行,来满足不超过一行80个字符的要求,参数也需要注释说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class PerformanceTester {
 public static final TcpConnectionSimulator wifi = new TcpConnectionSimulator(
 500, /* Kbps */
 80, /* millisecs latency */
 200, /* jitter */
 1 /* packet loss % */);
 
 public static final TcpConnectionSimulator t3_fiber = new TcpConnectionSimulator(
 45000, /* Kbps */
 10, /* millisecs latency */
 0, /* jitter */
 0 /* packet loss % */);
 
 public static final TcpConnectionSimulator cell = new TcpConnectionSimulator(
 100, /* Kbps */
 400, /* millisecs latency */
 250, /* jitter */
 5 /* packet loss % */);
}

考虑到代码的连贯性,先优化成这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class PerformanceTester {
 public static final TcpConnectionSimulator wifi =
 new TcpConnectionSimulator(
 500, /* Kbps */
 80, /* millisecs latency */ 200, /* jitter */
 1 /* packet loss % */);
 
 public static final TcpConnectionSimulator t3_fiber =
 new TcpConnectionSimulator(
 45000, /* Kbps */
 10, /* millisecs latency */
 0, /* jitter */
 0 /* packet loss % */);
 
 public static final TcpConnectionSimulator cell =
 new TcpConnectionSimulator(
 100, /* Kbps */
 400, /* millisecs latency */
 250, /* jitter */
 5 /* packet loss % */);
}

连贯性好一点,但还是太罗嗦,额外占用很多空间:

1
2
3
4
5
6
7
8
9
10
11
12
public class PerformanceTester {
 // TcpConnectionSimulator(throughput, latency, jitter, packet_loss)
 // [Kbps] [ms] [ms] [percent]
 public static final TcpConnectionSimulator wifi =
 new TcpConnectionSimulator(500, 80, 200, 1);
 
 public static final TcpConnectionSimulator t3_fiber =
 new TcpConnectionSimulator(45000, 10, 0, 0);
 
 public static final TcpConnectionSimulator cell =
 new TcpConnectionSimulator(100, 400, 250, 5);
}

用函数封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Turn a partial_name like "Doug Adams" into "Mr. Douglas Adams".
// If not possible, 'error' is filled with an explanation.
string ExpandFullName(DatabaseConnection dc, string partial_name, string* error);
 
DatabaseConnection database_connection;
string error;
assert(ExpandFullName(database_connection, "Doug Adams", &error)
 == "Mr. Douglas Adams");
assert(error == "");
assert(ExpandFullName(database_connection, " Jake Brown ", &error)
 == "Mr. Jacob Brown III");
assert(error == "");
assert(ExpandFullName(database_connection, "No Such Guy", &error) == "");
assert(error == "no match found");
assert(ExpandFullName(database_connection, "John", &error) == "");
assert(error == "more than one result");

上面这段代码看起来很脏乱,很多重复性的东西,可以用函数封装:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
CheckFullName("Doug Adams", "Mr. Douglas Adams", "");
CheckFullName(" Jake Brown ", "Mr. Jake Brown III", "");
CheckFullName("No Such Guy", "", "no match found");
CheckFullName("John", "", "more than one result");
 
void CheckFullName(string partial_name,
 string expected_full_name,
 string expected_error) {
 // database_connection is now a class member
 string error;
 string full_name = ExpandFullName(database_connection, partial_name, &error);
 assert(error == expected_error);
 assert(full_name == expected_full_name);
}

列对齐

列对齐可以让代码段看起来更舒适:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
CheckFullName("Doug Adams" , "Mr. Douglas Adams" , "");
CheckFullName(" Jake Brown ", "Mr. Jake Brown III", "");
CheckFullName("No Such Guy" , "" , "no match found");
CheckFullName("John" , "" , "more than one result");
 
commands[] = {
 ...
 { "timeout" , NULL , cmd_spec_timeout},
 { "timestamping" , &opt.timestamping , cmd_boolean},
 { "tries" , &opt.ntry , cmd_number_inf},
 { "useproxy" , &opt.use_proxy , cmd_boolean},
 { "useragent" , NULL , cmd_spec_useragent},
 ...
};

代码用块区分

1
2
3
4
5
6
7
8
9
10
11
12
13
class FrontendServer {
 public:
 FrontendServer();
 void ViewProfile(HttpRequest* request);
 void OpenDatabase(string location, string user);
 void SaveProfile(HttpRequest* request);
 string ExtractQueryParam(HttpRequest* request, string param);
 void ReplyOK(HttpRequest* request, string html);
 void FindFriends(HttpRequest* request);
 void ReplyNotFound(HttpRequest* request, string error);
 void CloseDatabase(string location);
 ~FrontendServer();
};

上面这一段虽然能看,不过还有优化空间:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class FrontendServer {
 public:
 FrontendServer();
 ~FrontendServer();
 // Handlers
 void ViewProfile(HttpRequest* request);
 void SaveProfile(HttpRequest* request);
 void FindFriends(HttpRequest* request);
 
 // Request/Reply Utilities
 string ExtractQueryParam(HttpRequest* request, string param);
 void ReplyOK(HttpRequest* request, string html);
 void ReplyNotFound(HttpRequest* request, string error);
 
 // Database Helpers
 void OpenDatabase(string location, string user);
 void CloseDatabase(string location);
};

再来看一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
# Import the user's email contacts, and match them to users in our system.
# Then display a list of those users that he/she isn't already friends with.
def suggest_new_friends(user, email_password):
 friends = user.friends()
 friend_emails = set(f.email for f in friends)
 contacts = import_contacts(user.email, email_password)
 contact_emails = set(c.email for c in contacts)
 non_friend_emails = contact_emails - friend_emails
 suggested_friends = User.objects.select(email__in=non_friend_emails)
 display['user'] = user
 display['friends'] = friends
 display['suggested_friends'] = suggested_friends
 return render("suggested_friends.html", display)

全都混在一起,视觉压力相当大,按功能化块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def suggest_new_friends(user, email_password):
 # Get the user's friends' email addresses.
 friends = user.friends()
 friend_emails = set(f.email for f in friends)
 
 # Import all email addresses from this user's email account.
 contacts = import_contacts(user.email, email_password)
 contact_emails = set(c.email for c in contacts)
 
 # Find matching users that they aren't already friends with.
 non_friend_emails = contact_emails - friend_emails
 suggested_friends = User.objects.select(email__in=non_friend_emails)
 
 # Display these lists on the page. display['user'] = user
 display['friends'] = friends
 display['suggested_friends'] = suggested_friends
 
 return render("suggested_friends.html", display)

让代码看起来更舒服,需要在写的过程中多注意,培养一些好的习惯,尤其当团队合作的时候,代码风格比如大括号的位置并没有对错,但是不遵循团队规范那就是错的。

如何写注释

当你写代码的时候,你会思考很多,但是最终呈现给读者的就只剩代码本身了,额外的信息丢失了,所以注释的目的就是让读者了解更多的信息。

应该注释什么

不应该注释什么

这样的注释毫无价值:

1
2
3
4
5
6
7
8
9
10
// The class definition for Account
class Account {
 public:
 // Constructor
 Account();
 // Set the profit member to a new value
 void SetProfit(double profit);
 // Return the profit from this Account
 double GetProfit();
};

不要像下面这样为了注释而注释:

1
2
3
4
// Find a Node with the given 'name' or return NULL.
// If depth <= 0, only 'subtree' is inspected.
// If depth == N, only 'subtree' and N levels below are inspected.
Node* FindNodeInSubtree(Node* subtree, string name, int depth);

不要给烂取名注释

1
2
3
// Enforce limits on the Reply as stated in the Request,
// such as the number of items returned, or total byte size, etc.
void CleanReply(Request request, Reply reply);

注释的大部分都在解释clean是什么意思,那不如换个正确的名字:

1
2
// Make sure 'reply' meets the count/byte/etc. limits from the 'request'
void EnforceLimitsFromRequest(Request request, Reply reply);

记录你的想法

我们讨论了不该注释什么,那么应该注释什么呢?注释应该记录你思考代码怎么写的结果,比如像下面这些:

1
2
3
4
5
6
7
// Surprisingly, a binary tree was 40% faster than a hash table for this data.
// The cost of computing a hash was more than the left/right comparisons.
 
// This heuristic might miss a few words. That's OK; solving this 100% is hard.
 
// This class is getting messy. Maybe we should create a 'ResourceNode' subclass to
// help organize things.

也可以用来记录流程和常量:

1
2
3
4
5
6
7
// TODO: use a faster algorithm
// TODO(dustin): handle other image formats besides JPEG
 
NUM_THREADS = 8 # as long as it's >= 2 * num_processors, that's good enough.
 
// Impose a reasonable limit - no human can read that much anyway.
const int MAX_RSS_SUBSCRIPTIONS = 1000;

可用的词有:

  • TODO : Stuff I haven’t gotten around to yet
  • FIXME : Known-broken code here
  • HACK : Adimittedly inelegant solution to a problem
  • XXX : Danger! Major problem here

站在读者的角度去思考

当别人读你的代码时,让他们产生疑问的部分,就是你应该注释的地方。

1
2
3
4
5
6
7
struct Recorder {
 vector<float> data;
 ...
 void Clear() {
 vector<float>().swap(data); // Huh? Why not just data.clear()?
 }
};

很多C++的程序员啊看到这里,可能会想为什么不用data.clear()来代替vector.swap,所以那个地方应该加上注释:

1
2
// Force vector to relinquish its memory (look up "STL swap trick")
vector<float>().swap(data);

说明可能陷阱

你在写代码的过程中,可能用到一些hack,或者有其他需要读代码的人知道的陷阱,这时候就应该注释:

1
void SendEmail(string to, string subject, string body);

而实际上这个发送邮件的函数是调用别的服务,有超时设置,所以需要注释:

1
2
// Calls an external service to deliver email. (Times out after 1 minute.)
void SendEmail(string to, string subject, string body);

全景的注释

有时候为了更清楚说明,需要给整个文件加注释,让读者有个总体的概念:

1
2
// This file contains helper functions that provide a more convenient interface to our
// file system. It handles file permissions and other nitty-gritty details.

总结性的注释

即使是在函数内部,也可以有类似文件注释那样的说明注释:

1
2
3
4
5
# Find all the items that customers purchased for themselves.
for customer_id in all_customers:
 for sale in all_sales[customer_id].sales:
 if sale.recipient == customer_id:
 ...

或者按照函数的步进,写一些注释:

1
2
3
4
5
6
7
8
def GenerateUserReport():
 # Acquire a lock for this user
 ...
 # Read user's info from the database
 ...
 # Write info to a file
 ...
 # Release the lock for this user

很多人不愿意写注释,确实,要写好注释也不是一件简单的事情,也可以在文件专门的地方,留个写注释的区域,可以写下你任何想说的东西。

注释应简明准确

前一个小节讨论了注释应该写什么,这一节来讨论应该怎么写,因为注释很重要,所以要写的精确,注释也占据屏幕空间,所以要简洁。

精简注释

1
2
3
4
// The int is the CategoryType.
// The first float in the inner pair is the 'score',
// the second is the 'weight'.
typedef hash_map<int, pair<float, float> > ScoreMap;

这样写太罗嗦了,尽量精简压缩成这样:

1
2
// CategoryType -> (score, weight)
typedef hash_map<int, pair<float, float> > ScoreMap;

避免有歧义的代词

1
// Insert the data into the cache, but check if it's too big first.

这里的it's有歧义,不知道所指的是data还是cache,改成如下:

1
// Insert the data into the cache, but check if the data is too big first.

还有更好的解决办法,这里的it就有明确所指:

1
// If the data is small enough, insert it into the cache.

语句要精简准确

1
# Depending on whether we've already crawled this URL before, give it a different priority.

这句话理解起来太费劲,改成如下就好理解很多:

1
# Give higher priority to URLs we've never crawled before.

精确描述函数的目的

1
2
// Return the number of lines in this file.
int CountLines(string filename) { ... }

这样的一个函数,用起来可能会一头雾水,因为他可以有很多歧义:

  • ”” 一个空文件,是0行还是1行?
  • “hello” 只有一行,那么返回值是0还是1?
  • “hellon” 这种情况返回1还是2?
  • “hellon world” 返回1还是2?
  • “hellonr crueln worldr” 返回2、3、4哪一个呢?

所以注释应该这样写:

1
2
// Count how many newline bytes ('n') are in the file.
int CountLines(string filename) { ... }

用实例说明边界情况

1
2
3
// Rearrange 'v' so that elements < <span class="wp_keywordlink"><a href="http://blog.jobbole.com/24057/" title="Pivot — 创业者最重要的本领" rel="nofollow" target="_blank">Pivot</a></span> come before those >= <span class="wp_keywordlink"><a href="http://blog.jobbole.com/24057/" title="Pivot — 创业者最重要的本领" rel="nofollow" target="_blank">Pivot</a></span>;
// Then return the largest 'i' for which v[i] < pivot (or -1 if none are < pivot)
int Partition(vector<int>* v, int pivot);

这个描述很精确,但是如果再加入一个例子,就更好了:

1
2
3
// ...
// Example: Partition([8 5 9 8 2], 8) might result in [5 2 | 8 9 8] and return 1
int Partition(vector<int>* v, int pivot);

说明你的代码的真正目的

1
2
3
4
5
6
7
8
void DisplayProducts(list<Product> products) {
 products.sort(CompareProductByPrice);
 // Iterate through the list in reverse order
 for (list<Product>::reverse_iterator it = products.rbegin(); it != products.rend();
 ++it)
 DisplayPrice(it->price);
 ...
}

这里的注释说明了倒序排列,单还不够准确,应该改成这样:

1
2
// Display each price, from highest to lowest
for (list<Product>::reverse_iterator it = products.rbegin(); ... )

函数调用时的注释

看见这样的一个函数调用,肯定会一头雾水:

1
Connect(10, false);

如果加上这样的注释,读起来就清楚多了:

1
2
3
4
def Connect(timeout, use_encryption): ...
 
# Call the function using named parameters
Connect(timeout = 10, use_encryption = False)

使用信息含量丰富的词

1
2
3
4
// This class contains a number of members that store the same information as in the
// database, but are stored here for speed. When this class is read from later, those
// members are checked first to see if they exist, and if so are returned; otherwise the
// database is read from and that data stored in those fields for next time.

上面这一大段注释,解释的很清楚,如果换一个词来代替,也不会有什么疑惑:

1
// This class acts as a caching layer to the database.

简化循环和逻辑

流程控制要简单

让条件语句、循环以及其他控制流程的代码尽可能自然,让读者在阅读过程中不需要停顿思考或者在回头查找,是这一节的目的。

条件语句中参数的位置

对比下面两种条件的写法:

1
2
3
4
5
if (length >= 10)
while (bytes_received < bytes_expected)
 
if (10 <= length)
while (bytes_expected > bytes_received)

到底是应该按照大于小于的顺序来呢,还是有其他的准则?是的,应该按照参数的意义来

  • 运算符左边:通常是需要被检查的变量,也就是会经常变化的
  • 运算符右边:通常是被比对的样本,一定程度上的常量

这就解释了为什么bytes_received < bytes_expected比反过来更好理解。

if/else的顺序

通常,if/else的顺序你可以自由选择,下面这两种都可以:

1
2
3
4
5
6
7
8
9
10
11
if (a == b) {
 // Case One ...
} else {
 // Case Two ...
}
 
if (a != b) {
 // Case Two ...
} else {
 // Case One ...
}

或许对此你也没有仔细斟酌过,但在有些时候,一种顺序确实好过另一种:

  • 正向的逻辑在前,比如if(debug)就比if(!debug)
  • 简单逻辑的在前,这样ifelse就可以在一个屏幕显示 – 有趣、清晰的逻辑在前

举个例子来看:

1
2
3
4
5
6
7
8
9
if (!url.HasQueryParameter("expand_all")) {
 response.Render(items);
 ...
} else {
 for (int i = 0; i < items.size(); i++) {
 items[i].Expand();
 }
 ...
}

看到if你首先想到的是expand_all,就好像告诉你“不要想大象”,你会忍不住去想它,所以产生了一点点迷惑,最好写成:

1
2
3
4
5
6
7
8
9
if (url.HasQueryParameter("expand_all")) {
 for (int i = 0; i < items.size(); i++) {
 items[i].Expand();
 }
 ...
} else {
 response.Render(items);
 ...
}

三目运算符(?:)

1
2
3
4
5
6
7
8
time_str += (hour >= 12) ? "pm" : "am";
 
Avoiding the ternary operator, you might write:
 if (hour >= 12) {
 time_str += "pm";
 } else {
 time_str += "am";
}

使用三目运算符可以减少代码行数,上例就是一个很好的例证,但是我们的真正目的是减少读代码的时间,所以下面的情况并不适合用三目运算符:

1
2
3
4
5
6
7
return exponent >= 0 ? mantissa * (1 << exponent) : mantissa / (1 << -exponent);
 
if (exponent >= 0) {
 return mantissa * (1 << exponent);
} else {
 return mantissa / (1 << -exponent);
}

所以只在简单表达式的地方用。

避免使用do/while表达式

1
2
3
do {
 continue;
} while (false);

这段代码会执行几遍呢,需要时间思考一下,do/while完全可以用别的方法代替,所以应避免使用。

尽早return

1
2
3
4
5
public boolean Contains(String str, String substr) {
 if (str == null || substr == null) return false;
 if (substr.equals("")) return true;
 ...
}

函数里面尽早的return,可以让逻辑更加清晰。

减少嵌套

1
2
3
4
5
6
7
8
9
10
11
if (user_result == SUCCESS) {
 if (permission_result != SUCCESS) {
 reply.WriteErrors("error reading permissions");
 reply.Done();
 return;
 }
 reply.WriteErrors("");
} else {
 reply.WriteErrors(user_result);
}
reply.Done();

这样一段代码,有一层的嵌套,但是看起来也会稍有迷惑,想想自己的代码,有没有类似的情况呢?可以换个思路去考虑这段代码,并且用尽早return的原则修改,看起来就舒服很多:

1
2
3
4
5
6
7
8
9
10
11
12
if (user_result != SUCCESS) {
 reply.WriteErrors(user_result);
 reply.Done();
 return;
}
if (permission_result != SUCCESS) {
 reply.WriteErrors(permission_result);
 reply.Done();
 return;
}
reply.WriteErrors("");
reply.Done();

同样的,对于有嵌套的循环,可以采用同样的办法:

1
2
3
4
5
6
7
8
9
for (int i = 0; i < results.size(); i++) {
 if (results[i] != NULL) {
 non_null_count++;
 if (results[i]->name != "") {
 cout << "Considering candidate..." << endl;
 ...
 }
 }
}

换一种写法,尽早return,在循环中就用continue:

1
2
3
4
5
6
7
8
for (int i = 0; i < results.size(); i++) {
 if (results[i] == NULL) continue;
 non_null_count++;
 
 if (results[i]->name == "") continue;
 cout << "Considering candidate..." << endl;
 ...
}

拆分复杂表达式

很显然的,越复杂的表达式,读起来越费劲,所以应该把那些复杂而庞大的表达式,拆分成一个个易于理解的小式子。

用变量

将复杂表达式拆分最简单的办法,就是增加一个变量:

1
2
3
4
5
6
if line.split(':')[0].strip() == "root":
 
//用变量替换
username = line.split(':')[0].strip()
if username == "root":
 ...

或者这个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
if (request.user.id == document.owner_id) {
 // user can edit this document...
}
...
if (request.user.id != document.owner_id) {
// document is read-only...
}
 
//用变量替换
final boolean user_owns_document = (request.user.id == document.owner_id);
if (user_owns_document) {
 // user can edit this document...
}
...
if (!user_owns_document) {
 // document is read-only...
}

逻辑替换

  • 1) not (a or b or c) <–> (not a) and (not b) and (not c)
  • 2) not (a and b and c) <–> (not a) or (not b) or (not c)

所以,就可以这样写:

1
2
3
4
if (!(file_exists && !is_protected)) Error("Sorry, could not read file.");
 
//替换
if (!file_exists || is_protected) Error("Sorry, could not read file.");

不要滥用逻辑表达式

1
assert((!(bucket = FindBucket(key))) || !bucket->IsOccupied());

这样的代码完全可以用下面这个替换,虽然有两行,但是更易懂:

1
2
bucket = FindBucket(key);
if (bucket != NULL) assert(!bucket->IsOccupied());

像下面这样的表达式,最好也不要写,因为在有些语言中,x会被赋予第一个为true的变量的值:

1
x = a || b || c

拆解大表达式

1
2
3
4
5
6
7
8
9
10
11
12
var update_highlight = function (message_num) {
 if ($("#vote_value" + message_num).html() === "Up") {
 $("#thumbs_up" + message_num).addClass("highlighted");
 $("#thumbs_down" + message_num).removeClass("highlighted");
 } else if ($("#vote_value" + message_num).html() === "Down") {
 $("#thumbs_up" + message_num).removeClass("highlighted");
 $("#thumbs_down" + message_num).addClass("highlighted");
 } else {
 $("#thumbs_up" + message_num).removeClass("highighted");
 $("#thumbs_down" + message_num).removeClass("highlighted");
 }
};

这里面有很多重复的语句,我们可以用变量还替换简化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var update_highlight = function (message_num) {
 var thumbs_up = $("#thumbs_up" + message_num);
 var thumbs_down = $("#thumbs_down" + message_num);
 var vote_value = $("#vote_value" + message_num).html();
 var hi = "highlighted";
 
 if (vote_value === "Up") {
 thumbs_up.addClass(hi);
 thumbs_down.removeClass(hi);
 } else if (vote_value === "Down") {
 thumbs_up.removeClass(hi);
 thumbs_down.addClass(hi);
 } else {
 thumbs_up.removeClass(hi);
 thumbs_down.removeClass(hi);
 }
}

消除变量

前一节,讲到利用变量来拆解大表达式,这一节来讨论如何消除多余的变量。

没用的临时变量

1
2
now = datetime.datetime.now()
root_message.last_view_time = now

这里的now可以去掉,因为:

  • 并非用来拆分复杂的表达式
  • 也没有增加可读性,因为`datetime.datetime.now()`本就清晰
  • 只用了一次

所以完全可以写作:

1
root_message.last_view_time = datetime.datetime.now()

消除条件控制变量

1
2
3
4
5
6
7
8
boolean done = false;
while (/* condition */ && !done) {
 ...
 if (...) {
 done = true;
 continue;
 }
}

这里的done可以用别的方式更好的完成:

1
2
3
4
5
6
while (/* condition */) {
 ...
 if (...) {
 break;
 }
}

这个例子非常容易修改,如果是比较复杂的嵌套,break可能并不够用,这时候就可以把代码封装到函数中。

减少变量的作用域

我们都听过要避免使用全局变量这样的忠告,是的,当变量的作用域越大,就越难追踪,所以要保持变量小的作用域。

1
2
3
4
5
6
7
8
9
10
11
12
class LargeClass {
 string str_;
 void Method1() {
 str_ = ...;
 Method2();
 }
 void Method2() {
 // Uses str_
 }
 // Lots of other methods that don't use str_
 ... ;
}

这里的str_的作用域有些大,完全可以换一种方式:

1
2
3
4
5
6
7
8
9
10
class LargeClass {
 void Method1() {
 string str = ...;
 Method2(str);
 }
 void Method2(string str) {
 // Uses str
 }
 // Now other methods can't see str.
};

str通过变量函数参数传递,减小了作用域,也更易读。同样的道理也可以用在定义类的时候,将大类拆分成一个个小类。

不要使用嵌套的作用域

1
2
3
4
5
6
7
8
9
# No use of example_value up to this point.
if request:
 for value in request.values:
 if value > 0:
 example_value = value
 break
 
for logger in debug.loggers:
 logger.log("Example:", example_value)

这个例子在运行时候会报example_value is undefined的错,修改起来不算难:

1
2
3
4
5
6
7
8
9
example_value = None
if request:
 for value in request.values:
 if value > 0: example_value = value
 break
 
if example_value:
 for logger in debug.loggers:
 logger.log("Example:", example_value)

但是参考前面的消除中间变量准则,还有更好的办法:

1
2
3
4
5
6
7
8
9
def LogExample(value):
 for logger in debug.loggers:
 logger.log("Example:", value)
 
 if request:
 for value in request.values:
 if value > 0:
 LogExample(value) # deal with 'value' immediately
 break

用到了再声明

在C语言中,要求将所有的变量事先声明,这样当用到变量较多时候,读者处理这些信息就会有难度,所以一开始没用到的变量,就暂缓声明:

1
2
3
4
5
6
7
8
9
10
11
12
13
def ViewFilteredReplies(original_id):
 filtered_replies = []
 root_message = Messages.objects.get(original_id)
 all_replies = Messages.objects.select(root_id=original_id)
 root_message.view_count += 1
 root_message.last_view_time = datetime.datetime.now()
 root_message.save()
 
 for reply in all_replies:
 if reply.spam_votes <= MAX_SPAM_VOTES:
 filtered_replies.append(reply)
 
 return filtered_replies

读者一次处理变量太多,可以暂缓声明:

1
2
3
4
5
6
7
8
9
10
11
12
13
def ViewFilteredReplies(original_id):
 root_message = Messages.objects.get(original_id)
 root_message.view_count += 1
 root_message.last_view_time = datetime.datetime.now()
 root_message.save()
 
 all_replies = Messages.objects.select(root_id=original_id)
 filtered_replies = []
 for reply in all_replies:
 if reply.spam_votes <= MAX_SPAM_VOTES:
 filtered_replies.append(reply)
 
 return filtered_replies

变量最好只写一次

前面讨论了过多的变量会让读者迷惑,同一个变量,不停的被赋值也会让读者头晕,如果变量变化的次数少一些,代码可读性就更强。

一个例子

假设有一个页面,如下,需要给第一个空的input赋值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<input type="text" id="input1" value="Dustin">
<input type="text" id="input2" value="Trevor">
<input type="text" id="input3" value="">
<input type="text" id="input4" value="Melissa">
...
var setFirstEmptyInput = function (new_value) {
 var found = false;
 var i = 1;
 var elem = document.getElementById('input' + i);
 while (elem !== null) {
 if (elem.value === '') {
 found = true;
 break;
 }
 i++;
 elem = document.getElementById('input' + i);
 }
 if (found) elem.value = new_value;
 return elem;
};

这段代码能工作,有三个变量,我们逐一去看如何优化,found作为中间变量,完全可以消除:

1
2
3
4
5
6
7
8
9
10
11
12
13
var setFirstEmptyInput = function (new_value) {
 var i = 1;
 var elem = document.getElementById('input' + i);
 while (elem !== null) {
 if (elem.value === '') {
 elem.value = new_value;
 return elem;
 }
 i++;
 elem = document.getElementById('input' + i);
 }
 return null;
};

再来看elem变量,只用来做循环,调用了很多次,所以很难跟踪他的值,i也可以用for来修改:

1
2
3
4
5
6
7
8
9
10
11
var setFirstEmptyInput = function (new_value) {
 for (var i = 1; true; i++) {
 var elem = document.getElementById('input' + i);
 if (elem === null)
 return null; // Search Failed. No empty input found.
 if (elem.value === '') {
 elem.value = new_value;
 return elem;
 }
 }
};

工程师就是将大问题分解为一个个小问题,然后逐个解决,这样也易于保证程序的健壮性、可读性。如何分解子问题,下面给出一些准则:

  • 看看这个方法或代码,问问你自己“这段代码的最终目标是什么?”
  • 对于每一行代码,要问“它与目标直接相关,或者是不相关的子问题?”
  • 如果有足够多行的代码是处理与目标不直接相关的问题,那么抽离成子函数

来看一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
ajax_post({
 url: 'http://example.com/submit',
 data: data,
 on_success: function (response_data) {
 var str = "{n";
 for (var key in response_data) {
 str += " " + key + " = " + response_data[key] + "n";
 }
 alert(str + "}");
 // Continue handling 'response_data' ...
 }
});

这段代码的目标是发送一个ajax请求,所以其中字符串处理的部分就可以抽离出来:

1
2
3
4
5
6
7
var format_pretty = function (obj) {
 var str = "{n";
 for (var key in obj) {
 str += " " + key + " = " + obj[key] + "n";
 }
 return str + "}";
};

意外收获

有很多理由将format_pretty抽离出来,这些独立的函数可以很容易的添加feature,增强可靠性,处理边界情况,等等。所以这里,可以将format_pretty增强,就会得到一个更强大的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var format_pretty = function (obj, indent) {
 // Handle null, undefined, strings, and non-objects.
 if (obj === null) return "null";
 if (obj === undefined) return "undefined";
 if (typeof obj === "string") return '"' + obj + '"';
 if (typeof obj !== "object") return String(obj);
 if (indent === undefined) indent = "";
 
 // Handle (non-null) objects.
 
 var str = "{n";
 for (var key in obj) {
 str += indent + " " + key + " = ";
 str += format_pretty(obj[key], indent + " ") + "n"; }
 return str + indent + "}";
};

这个函数输出:

1
2
3
4
5
6
7
8
9
10
11
{
 key1 = 1
 key2 = true
 key3 = undefined
 key4 = null
 key5 = {
 key5a = {
 key5a1 = "hello world"
 }
 }
}

多做这样的事情,就是积累代码的过程,这样的代码可以复用,也可以形成自己的代码库,或者分享给别人。

业务相关的函数

那些与目标不相关函数,抽离出来可以复用,与业务相关的也可以抽出来,保持代码的易读性,例如:

1
2
3
4
5
6
7
8
9
10
11
business = Business()
business.name = request.POST["name"]
 
url_path_name = business.name.lower()
url_path_name = re.sub(r"['.]", "", url_path_name)
url_path_name = re.sub(r"[^a-z0-9]+", "-", url_path_name)
url_path_name = url_path_name.strip("-")
business.url = "/biz/" + url_path_name
 
business.date_created = datetime.datetime.utcnow()
business.save_to_database()

抽离出来,就好看很多:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
CHARS_TO_REMOVE = re.compile(r"['.']+")
CHARS_TO_DASH = re.compile(r"[^a-z0-9]+")
 
def make_url_friendly(text):
 text = text.lower()
 text = CHARS_TO_REMOVE.sub('', text)
 text = CHARS_TO_DASH.sub('-', text)
 return text.strip("-")
 
business = Business()
business.name = request.POST["name"]
business.url = "/biz/" + make_url_friendly(business.name)
business.date_created = datetime.datetime.utcnow()
business.save_to_database()

简化现有接口

我们来看一个读写cookie的函数:

1
2
3
4
5
6
7
8
var max_results;
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
 var c = cookies[i];
 c = c.replace(/^[ ]+/, ''); // remove leading spaces
 if (c.indexOf("max_results=") === 0)
 max_results = Number(c.substring(12, c.length));
}

这段代码实在太丑了,理想的接口应该是这样的:

1
2
set_cookie(name, value, days_to_expire);
delete_cookie(name);

对于并不理想的接口,你永远可以用自己的函数做封装,让接口更好用。

按自己需要写接口

1
2
3
4
5
6
7
ser_info = { "username": "...", "password": "..." }
user_str = json.dumps(user_info)
cipher = Cipher("aes_128_cbc", key=PRIVATE_KEY, init_vector=INIT_VECTOR, op=ENCODE)
encrypted_bytes = cipher.update(user_str)
encrypted_bytes += cipher.final() # flush out the current 128 bit block
url = "http://example.com/?user_info=" + base64.urlsafe_b64encode(encrypted_bytes)
...

虽然终极目的是拼接用户信息的字符,但是代码大部分做的事情是解析python的object,所以:

1
2
3
4
5
def url_safe_encrypt(obj):
 obj_str = json.dumps(obj)
 cipher = Cipher("aes_128_cbc", key=PRIVATE_KEY, init_vector=INIT_VECTOR, op=ENCODE) encrypted_bytes = cipher.update(obj_str)
 encrypted_bytes += cipher.final() # flush out the current 128 bit block
 return base64.urlsafe_b64encode(encrypted_bytes)

这样在其他地方也可以调用:

1
2
user_info = { "username": "...", "password": "..." }
url = "http://example.com/?user_info=" + url_safe_encrypt(user_info)

分离子函数是好习惯,但是也要适度,过度的分离成多个小函数,也会让查找变得困难。

代码应该是一次只完成一个任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var place = location_info["LocalityName"]; // e.g. "Santa Monica"
if (!place) {
 place = location_info["SubAdministrativeAreaName"]; // e.g. "Los Angeles"
}
if (!place) {
 place = location_info["AdministrativeAreaName"]; // e.g. "California"
}
if (!place) {
 place = "Middle-of-Nowhere";
}
if (location_info["CountryName"]) {
 place += ", " + location_info["CountryName"]; // e.g. "USA"
} else {
 place += ", Planet Earth";
}
 
return place;

这是一个用来拼地名的函数,有很多的条件判断,读起来非常吃力,有没有办法拆解任务呢?

1
2
3
4
var town = location_info["LocalityName"]; // e.g. "Santa Monica"
var city = location_info["SubAdministrativeAreaName"]; // e.g. "Los Angeles"
var state = location_info["AdministrativeAreaName"]; // e.g. "CA"
var country = location_info["CountryName"]; // e.g. "USA"

先拆解第一个任务,将各变量分别保存,这样在后面使用中不需要去记忆那些繁长的key值了,第二个任务,解决地址拼接的后半部分:

1
2
3
4
5
6
7
// Start with the default, and keep overwriting with the most specific value. var second_half = "Planet Earth";
if (country) {
 second_half = country;
}
if (state && country === "USA") {
 second_half = state;
}

再来解决前半部分:

1
2
3
4
5
6
7
8
9
10
var first_half = "Middle-of-Nowhere";
if (state && country !== "USA") {
 first_half = state;
}
if (city) {
 first_half = city;
}
if (town) {
 first_half = town;
}

大功告成:

1
return first_half + ", " + second_half;

如果注意到有USA这个变量的判断的话,也可以这样写:

1
2
3
4
5
6
7
8
9
var first_half, second_half;
if (country === "USA") {
 first_half = town || city || "Middle-of-Nowhere";
 second_half = state || "USA";
} else {
 first_half = town || city || state || "Middle-of-Nowhere";
 second_half = country || "Planet Earth";
}
return first_half + ", " + second_half;

要把一个复杂的东西解释给别人,一些细节很容易就让人产生迷惑,所以想象把你的代码用平实的语言解释给别人听,别人是否能懂,有一些准则可以帮助你让代码更清晰:

  • 用最平实的语言描述代码的目的,就像给读者讲述一样
  • 注意描述中关键的字词
  • 让你的代码符合你的描述

下面这段代码用来校验用户的权限:

1
2
3
4
5
6
7
8
9
10
11
$is_admin = is_admin_request();
if ($document) {
 if (!$is_admin && ($document['username'] != $_SESSION['username'])) {
 return not_authorized();
 }
} else {
 if (!$is_admin) {
 return not_authorized();
 }
}
// continue rendering the page ...

这一段代码不长,里面的逻辑嵌套倒是复杂,参考前面章节所述,嵌套太多非常影响阅读理解,将这个逻辑用语言描述就是:

1
2
3
4
有两种情况有权限:
1、你是管理员(admin)
2、你拥有这个文档
否则就没有权限

根据描述来写代码:

1
2
3
4
5
6
7
8
if (is_admin_request()) {
 // authorized
} elseif ($document && ($document['username'] == $_SESSION['username'])) {
 // authorized
} else {
 return not_authorized();
}
// continue rendering the page ...

最易懂的代码就是没有代码!

  • 去掉那些没意义的feature,也不要过度设计
  • 重新考虑需求,解决最简单的问题,也能完成整体的目标
  • 熟悉你常用的库,周期性研究他的API

还有一些与测试相关的章节,留给你自己去研读吧,再次推荐此书:

203月/13

邵珠庆博客 12年通过justgive为中国儿童慈善捐款$250美金

发布在 邵珠庆

183月/13

创业公司的股权分配

发布在 邵珠庆

导读:创业公司应该如何分配股权?一般标准是谁最重要谁就该最多股份。比如:产品型团队,工程师拿最多股份。业务型,销售合伙人拿最多。商业模式型,那会融资的就应该拿最多股份。



这几天网上流传一篇文章,叫初创企业如何分配股份期权,里面说到硅谷一般原则如下:

1、外聘CEO :5%到8%;

2、副总0.8%到1.3%;

3、一线管理人员 0.25%;

4、普通员工 0.1%;

5、外聘董事会董事 0.25%;

6、期权总共占公司15%到20%股份。

期权工作1年后开始兑现,4年完毕。利益分配公平,才能形成有战斗力的创业团队。


很明显,硅谷的分配原则不符合中国的实际情况。别说外聘CEO根本不靠谱,初创企业你试试看找个不管是CTO还是CMO,只给0.8%到1.3%看看;估计没人跟你干。市场的行情是,CTO往往没10个点左右,根本找不到合适的人才。实际上,你给10个点,也未必有牛逼的人愿意跟你干。

而期权,基本上除了企业发展到一定阶段要改造成股份企业,并准备上市,对普通员工来讲,那基本如同废纸。还不如老板多发点奖金,或者老板在全公司员工面前颁发一个最佳员工管用。


那么中国式股权究竟应该如何分配呢?其实原则很简单,创业者要搞清楚,谁是公司除创始人之外最重要的人?谁最重要谁就拿最多股份。如果该公司是产品驱动型团队,工程师产品经理就应该拿最多股份。如果是业务型,销售合伙人拿最多。商业模式型,需要前期烧钱的,那会融资的就应该拿最多股份。

产品型团队最典型的就是网络游戏。做游戏的,几乎不用动任何其他心思,完全看产品。产品好,用户爱玩,渠道会主动来找你联运。开发游戏,人多了无用,资金多了也无用。把钱砸在办公室,场面上都无用。反而不如在一个三室两厅七八个兄弟一起干能成事。弹弹堂的CEO曹凯,在游戏已经过月收入千万后,还每天工作到12点,看游戏中存在的各种问题。

销售型团队往往是要靠客户订单才能维持下去。即使是想转型为产品型团队,往往前期也是要靠外包才能活下去。在这种情况下,谁能带来销售谁就最牛逼。牛逼的销售死人都能说成活人,用户没有需求也能说出需求。在中国典型的官商勾结的回扣文化中,牛逼的销售真的是一个人顶一万人;分最多的利益无可厚非。

商业模式型团队,往往前期要烧钱,甚至烧钱不是几百万,而是几千万,上亿的。动辄货币单位不是人民币而是美金的;那谁能融钱,谁就应该拿大的利益。举个例子,一个朋友帮兄弟的互联网公司在去年资本市场已经很差的情况下从温州老板手里面募了800万人民币,估值咂舌的达到了1个亿人民币,产品甚至都没上线。兄弟给了他给了15个点股份,不用全职。朋友全职工作仍然是投融资,维护好和温州投资人的关系。


很多初创公司的创始人,心怀大志,团队既没有关羽和张飞这样独当一面的人才,又想做平台级产品。出来委托融资顾问时,只愿意给很少的利益;最后融资不成,公司倒闭。这真应了红楼梦那句话:机关算尽太聪明,反误了卿卿性命。

企业发展过程中,所需人才不同,需要随时调配利益。最典型的你看刘备打天下,初期是关羽张飞;后期诸葛亮就获得更大的权益。为了保持平衡,关羽被赋予了一方诸侯的使命。这就类似初创公司发展大了以后,把某块业务划给某个团队的老成员经营,以换得他在总公司管理上的出局。这些议题不在本文讨论之中。


结语:中国是个草莽江湖;创业公司要的就是野蛮性增长。美国那一套细化的股权分配方式在企业大到一个规模前往往不管用。创业公司最重要的是有自知之明,分配利益有的放矢。

 

创业公司如何公平分配股权?

创业网友提问:

我有一个新的社会网络应用的构想。我并不期望这个应用可以获得巨大成功,但我想还是有些潜力的。我找过一些好友和同事交流过这个想法,他们都十分喜欢这个构想。还有些朋友甚至提出想作为合作伙伴加入一起进行开发,把想法变为一个可用的软件。

我无法用自己的钱给他们支付工资(他们也不期望那样),而且大家都打算把这项工作作为我们平时晚上或周末的业余项目。因为我认为这个想法有潜力可以变为成功的企业,我想从目前这个阶段开始,就解决企业所有权/薪酬的问题,免得将来因为没界定清楚导致真正的问题。我倾向于把公司所有权在我们三个创始人当中平分,而且基于这个分配规则来确定将来盈利后如何分配。这个选择对吗?如果对,怎么才算公平的股权分配?这是我自己提出的想法而且已花了不少时间做规划(而且我很确定我将自己负担所有开发过程中发生的费用),所以我感觉我应该理所当然持有更大的股权。这个要求是否合理?

我还同时企图想办法按付出的努力来激励我的合伙人。我不担心有人加入后什么都不干,但我的确认为我们当中有一个或多个人也许将十分努力付出,比其他人付出更多。如果情况是这样,我想这类合伙人应该获得更多的股份。在规划股权架构方面,您有何建议?

Joel Spolsky回答:

这个问题实在是太普遍了,我打算对这个为题给出这个世界上最详细的回答。我希望,将来如果这个坛子上有人问到类似的问题,大家只需要引用我的回答。

最重要的(股权分配)原则:公平,而且可感知到的公平,比真正拥有大的股份更有价值

在一个创业公司,几乎所有可能会出错的地方都会出错,而且会出错的问题当中最大最大的问题是创始人之间巨大的、令人气愤的、吵到面红耳赤的关于“谁更努力工作”的争论,谁拥有更多股份,谁提出的想法等等。这也是我总会与一个朋友50-50平分一个新公司的股权,而不是坚持自己拥有60%的股权,因为“这是我的想法”,或者因为“我比你更有经验”,或者任何其它原因。为什么呢?因为如果我把股权拆分为60-40,公司将在我们(创始人)不断争吵当中走向失败!如果你只是说,“去他妈的,我们永远也无法知道正确的股权分配比例,我们还是像哥们儿那样50-50平分”,你们将继续是朋友而且公司将生存下去。

所以,我郑重向大家推出:Joel的适用于任何创业公司创始人完全公平划分股权的秘笈!

为简单起见,我将假设你们不打算拿风险投资,而且你们将不会有外来的投资人。随后,我再解释如何处理风险投资,但目前我们暂时假设没有投资人。同样为简单起见,我们临时假设所有创始人都辞掉了他们的全职工作,而且同时开始全职为新公司工作。随后,我再解释如何处理后来加入的创始人。

来啦,原则是这样的:随着你们公司的成长,你们将一层一层/一批一批地加入新员工。公司的首批员工就是第一个创始人(或者第一批创始人)。也许有1个,2个,3个或者更多,但你们都同时开始在新公司工作,而且你们要冒一样的风险……例如辞掉你们的工作加入一个未被市场认可的新公司。

第二批进来的人就是首个(批)真正的员工。当你聘任这批人时,你已从某个来源获得现金(投资人或者客户,这个无所谓)。这些人不需要冒多大风险因为他们从工作的第一天开始就拿了工资,而且,老实说,他们不是公司的创始人,他们是加入公司打工的。

第三批的人是更后来加入到员工。他们加入公司时,公司已运作得不错。

对于很多公司而言,每隔大约1年将进来一“批”员工。当你的公司规模大到可以卖给谷歌或上市或是其它,你公司员工也许已经有了6批:创始人1批,员工大约5批。每一批员工人数都比上一批更多。也许有2个创始人,第二批当中有5名最早的员工,第三批有25名员工,而第四批有200名员工。越迟加入公司的员工需要冒的风险越低。

好啦,你将这样利用上述信息:创始人应该最终拿整个公司大约50%的股份;首层下面的5层员工的每一层最终都分别分到大约10%的公司股份,每一层的员工都将平分这10%的股份。


例子:

2个创始人启动公司。他们每人拿2500份股份。公司总市值按5000股算,所以每个创始人拿一半。

第一年,他们聘用了4名员工。这4名员工每人拿250份股份。公司总市值按6000股算。

第二年,他们又聘用了一批20名员工。这些员工每人拿50份股份。他们获得更少股份因为他们要承受的风险更少。因为公司给每一批员工派发的股份是1000股,所以他们每人拿到50股。

直到公司员工有了6批,你已给出10000股。每个创始人最终持有公司25%的股份。每个员工“层级”持有10%的股份。所有员工当中,最早进入公司的员工,因为他们与迟来的相比要承担的风险最大,在所有员工中持有最多股份。


靠谱吗?你不必严格按照这个公式来规划股份,但基本思路是:你设立不同的资历“层”,最高的层级中的员工承受最大的风险,最低层层级的员工承担最少的风险,而每个“层”的员工平分公司分配给这个层级的股份,这个规则神奇地让越早加入到员工获得越多的股份。

使用“层级”的一个稍微不同的方式是“资历”。你的顶部层级是公司创始人,再下一层,你需要预留一整层给将来招聘牛逼哄哄并坚持需要10%股份的CEO;再下一层是给那些早期进来的员工以及顶级经理人的,等等。无论你如何组织你的层级,它们应该是设计清晰明了,容易理解,不容易产生纷争。


现在,你搞定了一个公平的份股系统,但还有一个重要的原则:你必须执行“股份绑定”(vesting)。

股份绑定期最好是4到5年。任何人都必须在公司做够起码1年才可持有股份(包括创始人)。好的股份绑定计划一般是头一年给25%,然后接下来每个月落实2%。否则,你的合作创始人将加入公司3个星期后跑掉,然后7年后又出现,并声称他拥有公司的25%的股份。没有“股份绑定”条款,你派股份给任何人都是不靠谱的!没有执行“股份绑定”是极其普遍的现象,后果可以十分严重。你看到有些公司的3个创始人没日没夜地工作了5年,然后你发现有些混蛋加入后2个星期就离开,这混蛋还以为他仍然拥有公司25%的股份,就因为他工作过的那2个星期。


好了,让我们清理一下整个设计蓝图中没搞定的小问题。如果你的公司融资了,股份如何分割?投资可以来自任何方向,一个天使投资人,一个风险投资公司,或者是某人的老爸。基本上,回答很简单:新的投资将“稀释”所有人的股份。

沿用上面的例子,我们有2个创始人,我们给了自己每人2500股股份,所以我们每人拥有公司的50%股份,然后我们找了个风投,风投提出给我们100万换取1/3的公司股份。公司1/3的股份 = 2500股。所以,你发行2500股给了风投。风投持有1/3公司股份,而你和另外一个创始人各持1/3。就这么多。如果并不是所有早期员工都需要拿工资,怎么办?很多时候,有些公司创始人有不少个人积蓄,她决定公司启动后的某个阶段可以不拿工资。而有些创始人则需要现金,所以拿了工资。很多人认为不拿工资的创始人可以多拿一些股份,作为创业初期不拿工资的回报。问题是,你永远不可能计算出究竟应该给多多少股份(作为初期不拿工资的回报)。这样做将导致未来的纷争。千万不要用分配股权来解决这些问题。其实,你只需要针对每位创始人拿的工资做好记帐:不拿工资创始人就给她记着工资“欠条”。当公司有了足够现金,就根据这个工资欠条补发工资给她。接下来的几年中,当公司现金收入逐步增加,或者当完成第一轮风险投资后,你可以给每一位创始人补发工资,以确保每一位创始人都可从公司得到完全一样的工资收入。


创业构想是我提出的,难道我不应该多拿股份吗?不拿。构想基本上是不值钱的。仅仅因为提出创业构想就获得更多股权,因此导致纷争是不值得的。如果你们当中有人首先提出的创业构想,但你们都同时辞工并同时开始创业,你们应该拿同等的股份。为公司工作才是创造价值的原因,而你洗澡的时候突发奇想的“创业点子”根本不值什么钱。

如果创始人之一不是全职投入创业公司工作,该怎么办?那么,他(们)就不能算是“创始人”。在我的概念中,如果一个人不全职投入公司的工作就不能算是创始人。任何边干着他们其它的全职工作边帮公司干活的人只能拿工资或者工资“欠条”,但是不要给股份。如果这个“创始人”一直干着某份全职工作直到公司拿到风投,然后辞工全职过来公司干活,他(们)和第一批员工相比好不了多少,毕竟他们并没有冒其他创始人一样的风险。

如果有人为公司提供设备或其它有价值的东西(专利、域名等),怎么处理?很好啊。按这些东西的价值支付现金或开个“欠条”咯,别给股份。你准确算一下他给公司带来的那台电脑的价值,或者他们自带的某个聪明的字处理专利的价格,给他们写下欠条,公司有钱后再偿还即可。在创业初期就用股权来购买某些公司需要的东西将导致不平等,纷争和不公平。

投资人、创始人和雇员分别应该拥有多少股份?这都要看市场情况来确定。现实地看,如果投资人最终获得超过50%的公司股权,创始人将感觉自己不重要而且会丧失动力,所以好的投资人也不会这样干(拿超过50%的股权)。如果公司能依赖自我积累来发展而不依靠外来投资,创始人和员工一起将拥有公司100%的股权。有趣的是,这样的安排将给未来投资人带来足够的压力,以平衡投资人与创始人/员工。一条老经验是:公司上市时(当你雇佣了足够的员工而且筹集了足够的投资后),投资人将拥有50%股份,创始人+员工将拥有50%股份,但是就2011年热门的网络公司而言,他们的投资人最终拥有的股份都比50%少得多。


结论

虽然创业公司股权分配原则这个问题没有一刀切的解决方案,但是你得尽可能让它简单化,透明化,直接了当,而最重要的是:要公平。只有这样你的公司才更有可能成功。

183月/13

如何在nginx服务器下防盗链

发布在 邵珠庆

 

盗链是指一个网站的资源(图片或附件)未经允许在其它网站提供浏览和下载。
尤其热门资源的盗链,对网站带宽的消耗非常大,本文通过nginx的配置指令location来实现简单的图片和其它类型文件的防盗链。

Nginx 的配置文件 :

location ~ .(jpe?g|png|gif)$ {
     valid_referers none blocked mysite.com *.mysite.com;
     if ($invalid_referer) {
        return   403;
    }
}

用 (“|”) 来分隔你想保护的文件的扩展名。

valid_referers指令包含允许访问资源的网站列表,不在列表中请求的返回403。下面是valid_referers指令参数的解释 :

none - 匹配没有Referer的HTTP请求(Matches the requests with no Referer header).
blocked - 请求有Referer ,但是被防火墙或者代理服务器修改,去掉了https://或http:// (Matches the requests with blocked Referrer header).
*.mydomain.com - 匹配mysite.com的所有二级域名(Matches all the sub domains of mydomain.com. Since v0.5.33, * wildcards can be used in the server names).

除了使用location对文件访问进行限制,也可以对特定目录进行限制,下面的配置会禁止访问images目录下所有文件

location /images/ {
     valid_referers none blocked mysite.com *.mysite.com;
     if ($invalid_referer) {
        return   403;
    }
}

以上配置都是简单通过验证请求头来实现防盗链,如果盗链的网站通过伪造来路的http请求时不能屏蔽。

183月/13

这样教出来的孩子不优秀很难!

发布在 邵珠庆

这样教出来的孩子不优秀很难!

一则

儿两岁。某日,头撞桌角,长一包,大哭。一分钟余,我走向桌子,大声问:"桌子呀,是谁把你撞疼了?哭得这么伤心?”

儿止哭,泪眼看我。我抚桌,冲儿问:"谁呀?谁撞疼了桌子?""我,爸爸,我撞的!""哦,是你撞的,那还不快向桌子鞠个躬,说对不起!"儿含泪,鞠躬,说:"对不起"。

自此,儿学会了责任和担当!

二则

儿三岁。无故大哭,我问:"咋了,哪不舒服?""没有不舒服。""那为什么哭!""就是要哭!"明显撒娇。“好吧,你要哭我们都没意见,可是你在这儿哭不合适,会打扰我们说话的,爸爸给你找个地方,你一个人好好哭,哭够了再叫我们。”说完将儿关进了洗手间:"哭完了敲门。"2分钟,儿拍门:“爸爸,爸爸,我哭完了!”。"好,哭完了?哭完了就出来吧。"

至今,儿18岁,仍未学会操纵和迁怒!

三则

儿5岁。傍晚,牵儿散步经小桥,桥下碧水见底,暗流汹涌。儿仰头看我:“爸爸,小河好美,我想跳下去游泳。"我一愣:“好吧,爸爸跟你一起跳。不过我们先回家,换一下衣服。”

回,儿换衣毕,见一盆水在面前,困惑。

“儿子,下水游泳得把脸埋进水里,这你懂吧?”儿点头。“那我们现在就先练习一下,看看你能埋多久。"我看表。"好!"儿把脸埋进水里,豪气冲天?仅10秒:"呸呸,爸爸,呛水了,好难受。""是吗?等等跳到河里,可能会更难受些。""爸爸,我们可以不去跳吗?""好吧,不去就不去了。"

从此,儿学会了谨慎而不冒失,三思而后行。

四则

儿6岁,好吃。某晚,放学经麦当劳,驻足:"爸爸,麦当劳!”垂涎欲滴。

"嗯,麦当劳!想吃?"

"想吃!"

"儿子,一个人想吃就吃呢,叫狗熊;想吃而能不吃呢,叫英雄。"

接着问:“儿子,你要做英雄呢还是做狗熊?”

“爸爸,我当然要做英雄!”

"好!那英雄,想吃麦当劳时会怎样呢?"

"能不吃!"很坚定!

"太棒了,英雄!回家吧。"

儿流着口水,随我回。

从此,儿学会了有所为而有所不为,经得起诱惑。

五则

儿8岁,顽皮,与大同学打架。伤痕累累,回,大哭不止。

"委屈?"

"委屈!"儿泣答。

"愤怒?"

"愤怒!"儿嚎啕。

"你打算怎么办?"再问,"需要爸爸为你做点什么?"

"爸爸,我要找块砖头,明天从背后去砸他!"

"嗯,我看行!爸爸明天为你准备砖头。"继续问,"还有呢?"

"爸爸,你给我弄把刀,我明天从背后去捅他!"

"好!这个更解气,爸爸这就去准备一下。"我上楼。

理解支持,儿渐平静。约20分钟,我从楼上搬一大堆衣服及棉被?

"儿子,你决定了吗?是用砖头,还是用刀呀?"

"但是,爸爸,你搬那么多衣服被子干吗?"儿困惑。

"儿子,是这样的:如果你用砖头砸他,那么警察 就会把我们带走,在监狱 里大概只要住一个月,我们就带些短衣薄被就可以;如果你用刀子捅他,那么我们在监狱里至少3年回不来,我们可要多带些衣服被子,四季都要带齐?"

"所以,儿子你决定了吗?爸爸愿意支持你!"

"要这样的?"儿惊愕。

"是这样的,法律是这样规定的!"我趁机普法。

"爸爸,那我们就不干了吧?!"

"儿子,你不是很愤怒吗?"

"嗨嗨,爸爸,我已经不愤怒了,其实我也有错。"儿脸红。

"好,爸爸支持你!"

自此,儿学会了选择和代价。

六则:

儿9岁,四年级,数学不及格,闷闷不乐。

"怎么了?考试不及格,还给我们脸色。"

"因为数学老师很讨厌,她的课不爱听。"理直气壮。

"哦,怎么个讨厌法?"我很感兴趣。

"??,??"儿说了很多,"总之她也不喜欢我。"

“哦,别人喜欢你,你就喜欢她;别人不喜欢你,你就讨厌她。这说明你是个主动的人还是被动的人?”

“是个被动的人!"儿子回答。

“是强者,还是弱者?是大人,还是小人?”继续问。

“是弱者,是小人!”儿怯怯。

"那你要做大人,还是小人?"

"做大人!爸爸,我知道了:无论老师喜不喜欢我,我都可以去喜欢她,尊敬她,主动影响她,做一个强者。”

翌日,开心上学,数学从此优秀。并知道了何为大人,何为小人。

七则

儿10岁,玩游戏。妻屡教,子不改。

"儿子,听说你每天玩这个?"我指着电脑。

"嗯。"承认,低头!

“每次玩完之后,什么感受?"

"茫然,空虚,没劲,自责,看不起自已?"

"那为什么还玩呢?把持不住自己,是不?"

"是的,爸爸。"儿很无助。

"好!爸爸帮助你!"我搬来电脑,给儿子一小锤,"儿子,砸了它!"

"爸爸!"儿惊愕!

"砸了它,爸爸可以没有电脑,但不能没了儿子!"

儿流泪,亲手砸了电脑!

从此,儿懂得了什么叫原则。

昔孟母,择邻处,子不学,断机杼。

八则

儿11岁。我与妻久居异乡,每日致电老母亲,问候。一日,儿接电话:

"爸爸,您好!"很兴奋!

"嗯,好!奶奶呢?请奶奶听电话。"

"爸爸,你为什么每天只给奶奶打电话呀?"

"这有什么好奇怪?因为那是我妈!"

"那我呢?我也想你们!"

"你找你妈去呀!"

从此,妻每天6点,能接到儿子问候,风雨无阻,至今已8年!

九则

儿12岁,六年级,作业繁重,情绪焦躁。傍晚,儿放学归,刚进门。

"臭小子,你昨天是不是把我的盘子打破了?"我妹妹开始发难。

"没有呀,姑姑,我没有!"一脸困惑。

"我看见你打的,还耍赖!"我母亲又铁证如山。

"我没有呀!你们冤枉我?"大哭,躺地,情绪爆发?

约5分钟,我从房间出,厉声道:

"咋了?在这里发疯!"

"爸爸,姑姑和奶奶冤枉我!"

"冤枉?冤枉你又咋样!冤枉你就躺下了?没出息!你是不是男子汉?"

儿止哭,站起,低头:

"爸爸,她们冤枉我。"

"男子汉大丈夫,就算天塌下来,也不能躺下!何况一个小小的盘子?没出息!"我继续,"人这一辈子,要经历多少风风雨雨,被冤枉、污蔑、背叛、出卖?你就趴下了?那是孬种!"

儿挺腰,抬头:

"爸爸,我懂了,现在我该怎么办?"

"现在?问问你自已,你有很多时间吗?"

"没有,很多作业要做。"

"那还不去做作业!记住,哪怕山崩地裂,不理它,先做好自己的事!"

儿提起书包,向奶奶,姑姑行礼,从容走进书房。

我们仨会心一笑。

"宠辱不惊,看庭前花开花落;去留无意,望天空云卷云舒."儿子,当你长大后,看到这幅对子时,或许,你会想起今天,想起奶奶,想起姑姑,想起爸爸的良苦用心!

143月/13

用于展现图表的50种JavaScript库

发布在 邵珠庆

 

在很多项目中都会有在前端展现数据图表的需求,而在开发过程中,开发者往往会使用一些JavaScript库,从而更有效地达到想要的目标。最近,TechSlide上的一篇文章总结了50种用于展现图表的JavaScript库,并对每种库做了简要的说明。这对于想要选择合适JavaScript库的开发者很有参考意义。

  文章作者首推的库是D3,他说到:

  它非常让人惊叹,我很喜欢它的简洁性。它的文档非常完备,源代码托管在GitHub上,而且不断会添加新的示例。有一种叫做Tributary的创建D3原型的工具,其中有很多非常棒的示例。这个库非常好,以至于xcharts、nvd3、Rickshaw、Cubism.js、dc.js、xkcd都是基于它构建的。如果你想要做出优秀的自定义数据可视化效果,那么D3可能是你最佳选择,或者对于更简单的图,你可以选择上面所提到的基于D3的库。最后,我强烈推荐阅读Scott Murray关于D3的免费书《Interactive Data Visualization for the Web》和《Dashing D3 tutorials》。

  接下来,他列举并简要说明了其它用于展现数据、制作表格和图表的JavaScript库,列在前20位的如下:

  • HighCharts——它非常强大,你可以在JSFiddle中查看和编辑大量示例。它不免费,但拥有很多客户(IBM、NASA、MasterCard等)。它还向下兼容IE 8。
  • jqPlot——如果你已经在使用jQuery,不想为HighCharts付费,而且情况很简单,不需要D3那样复杂的库,那么jqPlot是很好的选择。
  • dygraphs——一种开源的JavaScript库,可以做出可交互、可缩放的时间线图表。对于大数据集合非常适用。
  • Protovis——和D3出自同一支团队之手,是一种免费的开源库。你可以查看这个stackoveflow 页面来了解D3与其的区别。
  • Flot Charts——与jqPlot一样,Flot是一种针对jQuery的纯JavaScript库,专注于简单的用法、引人注目的外观和交互特性。
  • Google Chart Tools——强大、免费、易于使用。内容丰富,从最简单的线状图到负责的层级树状图都有,在展示页面中提供了大量设计良好的图表类型。
  • dc.js——基于D3的JavaScript图表库,拥有本地跨过滤器(crossfilter)的支持,并让你可以高效率地浏览大型多维数据集。
  • xcharts——基于D3用于构建自定义图表的库。
  • nvd3——让你可以构建可重用的图表和图表组件,同时具有d3.js的强大功能。
  • rickshaw——用于创建可交互时间线图表的JavaScript工具。
  • Cubism.js——用于可视化时间线的D3插件。使用了Cubism构建更好的实时仪表盘,可以从Graphite、Cube和其他源拉取数据。
  • xkcd——让你可以使用D3在JavaScript中做出XKCD样式的图表。
  • jQuery Sparklines——一种jQuery插件,可以直接在浏览器中创建小型的内嵌图表。
  • peity——一种简单的jQuery插件,可以把元素的内容转换成简单的饼图、线图和柱状图。
  • BonsaiJS——一种轻量级的图形库,拥有直观的图形API和SVG渲染器。
  • Flotr——为Prototype.js所用的JavaScript图表库。它拥有很多特性,像对负数值的支持、鼠标跟踪、选定支持、缩放支持、事件挂钩、CSS样式支持、在画布(canvas)中包含文字、旋转的标签、渐变颜色、图形标题和子标题、电子表格、CSV数据下载等等。
  • ProtoChart——物如其名,ProtoChart让你可以使用JavaScript和Prototype创建很漂亮的图表。它是一种开源库。
  • Flotr2——HumbleSoftware当前正在做的项目,让你可以使用Canvas和JavaScript创建图表。
  • jQuery-Visualize——HTML的table元素驱动的HTML5 canvas图表。也是针对jQuery的图表插件。
  • JS Charts——基于JavaScript的图表生成器,只需要很少甚至不需要编码。免费版会有水印,可以通过付费去掉。
  • ……

  文章中还列举的JavaScript库有:PlotKitMilkChartmoochartmoowheeltable2chartCanvas 3D GraphTufteGraphArborJSTimePlotgRaphaelICOElychartsZingChartRGraphDojo ChartingBluffcanvasXpressccchartJITJSXGraphSmoothie ChartsYUI ChartsamchartsEmprise JavaScript ChartsFusionChartsJavaScript Diagram BuilderjGraphSencha Touch ChartsStyle ChartAwesomeChartJS等,都各有千秋,如果你对这些库感兴趣的话,可以访问相应的链接或者阅读原文

  这个列表对于想要利用JavaScript技术创建图表展现数据的开发者来说,非常具有参考意义,你可以从中选择最适合的库,从而高效、高质量地完成任务。

123月/13

Chrome浏览器超强调试工具

发布在 邵珠庆

 

 

在Google Chrome浏览器出来之前,我一直使用FireFox,因为FireFox的插件非常丰富,更因为FireFox有强大的Firebug,对于前端开发可谓神器。在Chrome出来的时候,我就喜欢上它的简洁、快速,无论是启动速度还是页面解析速度还是Javascript执行速度(现在的FireFox4 也比之前的FireFox3有很大的进步)。

 

不过当时由于Chrome的开发者工具还不是很完善,而我又不是很熟悉,加之对于Firebug的好感和依 赖,当时还是用回FireFox作为我的主浏览器。

后来由于开发Chrome的插件(现在的FaWave),就一直使用Google Chrom作为我的主浏览器,渐渐熟悉Chrome的开发者工具,而Chrome也一直在快速迭代,快速进步中,到现在,Chrome已经绝对成为我的主 浏览器,Chrome的开发者工具,我也认为比Firebug更好用。

得益于Google V8的快速,和对HTML5和CSS3的支持也算比较完善,html类的富客户端应用Chrome上无论是流畅性还是呈现的效果,都是比较出色的,这对于开发者,特别是对于那些喜欢研究前沿技术的前端开发者来说,是很重要的。

对于本文,作为一个Web开发人员,除了上面的原因以外,与我们开发相关的,就是Chrome的开发者工具。而本文,就是要详细说说Chrome的开发者工具,说说我为什么认为它比Firebug要好用。

怎样打开Chrome的开发者工具?

你可以直接在页面上点击右键,然后选择审查元素:

或者在Chrome的工具中找到:

或者,你直接记住这个快捷方式: Ctrl+Shift+I (或者Ctrl+Shift+J直接打开控制台),或者直接按F12。

打开的开发者工具就长下面的样子:

不过我一般习惯与点左下角的那个按钮,将开发者工具弹出作为一个独立的窗口:

下面来分别说下每个Tab的作用。

Elements标签页

这个就是查看、编辑页面上的元素,包括HTML和CSS:

左侧就是对页面HTML结构的查看与编辑,你可以直接在某个元素上双击修改元素的属性,或者你点右键选"Edit as Html"直接对元素的HTML进行编辑,或者删除某个元素,所有的修改都会即时在页面上得到呈现。(注:看到上面右键菜单的最后一个选项"审查元素"了么?这是不是说明这个开发者工具的页面也是HTML来的呢?你点一下就知道了哦,嘿嘿)
你还可以对某个元素进行监听,在JS对元素的属性或者HTML进行修改的时候,直接触发断点,跳转到对改元素进行修改的JS代码处:

Elements标签页的右侧可以对元素的CSS进行查看与编辑修改:

你还可以通过这里看到各CSS选择器设置的CSS值的覆盖情况。
下面的Metrics可以看到元素占的空间情况(宽、高、Padding、Margin神马的):

注意到上面的Properties没有?这个很有用哦,可以让你看到元素具有的方法与属性,比查API手册要方便得多哦(要注意某些方法和属性在IE、FireFox等其他浏览器下面的支持情况哦)。

Resources标签页

Resources标签页可以查看到请求的资源情况,包括CSS、JS、图片等的内容,同时还可以查看到存储相关的如Cookies、HTML5的Database和LocalStore等,你可以对存储的内容编辑和删除。
这里的CSS文件有一个好玩的特性,你可以直接修改CSS文件,并且修改即时生效哦:

Network标签页

Network标签页对于分析网站请求的网络情况、查看某一请求的请求头和响应头还有响应内容很有用,特别是在查看Ajax类请求的时候,非常有帮助。注意是在你打开Chrome开发者工具后发起的请求,才会在这里显示的哦。
点击左侧某一个具体去请求URL,可以看到该请求的详细HTTP请求情况:

我们可以在这里看到HTTP请求头、HTTP响应头、HTTP返回的内容等信息,对于开发、调试,都是很有用的。

Scripts标签页

很明显,这个标签页就是查看JS文件、调试JS代码的,直接看下图的说明:

还有你可以打开Javascript控制台,做一些其他的查看或者修改:

你甚至还可以为某一XHR请求或者某一事件设置断点:

Timeline标签页

注意这个Timeline的标签页不是指网络请求的时间响应情况哦(这个在Network标签页里查看),这个Timeline指的JS执行时间、页面元素渲染时间:

点击底部的Record就可以开始录制页面上执行的内容。(这个不熟悉,请参考文末链接)

Profiles标签页

这个主要是做性能优化的,包括查看CPU执行时间与内存占用:

这个也不熟悉,不多说,还是请参考文末链接吧。

Audits标签页

这个对于优化前端页面、加速网页加载速度很有用哦(相当与Yslow):

点击run按钮,就可以开始分析页面,分析完了就可以看到分析结果了:

它甚至可以分析出页面上样式表中有哪些CSS是没有被使用的哦:

Console标签页

就是Javascript控制台了:

这个除了查看错误信息、打印调试信息(console.log())、写一些测试脚本以外,还可以当作Javascript API查看用。
例如我想查看console都有哪些方法和属性,我可以直接在Console中输入"console"并执行:

怎么样,一目了然了吧 ?再例如我想查看日期函数都有哪些方法:

(注:注意在这里看到的某些方法和属性是ES5新增的,记得兼容其他浏览器的支持情况哦)

结语

Google Chrome除了简洁、快速,现在的Chrome的插件也非常的丰富了。而对于web开发者来说,Chrome对于HTML5、CSS3等一些新标准的支 持也是比较完善的,而且Chrome的开发者工具我个人认为真的非常好用,这就是为什么我向web开发者推荐使用Chrome的原因。

注1:本文截图的Chrome版本为:13.0.782.215 m
注2:Chrome开发者工具更详细的说明请参考:http://code.google.com/intl/zh-CN/chrome/devtools/docs/overview.html

123月/13

常用帮助文档链接

发布在 邵珠庆

海底苍鹰(tank)博客 帮助文档大全

http://blog.51yip.com/manual/jquery/

Chrome Developer Tools

https://developers.google.com/chrome-developer-tools/docs/overview?hl=zh-CN

 

 

 

 

 

 

 

 

 

 

113月/13

迄今见过的最好的职业规划的文章

发布在 邵珠庆

 

如果这篇文章没有分享给你,那是我的错。

如果这篇文章分享给你了,你却没有读,继续走弯路的你不要怪我。
如果你看了这篇文章,只读了一半你就说没时间了,说明你已经是个人了。
如果你看完了,你觉得这篇文章只是讲讲大道理,说明你的人生阅历还不够,需要你把这篇文章珍藏,走出去碰几年壁,头破血流后再回来,再读,你就会感叹自己的年少无知。
如果你看完了,觉得很有道理,然后束之高阁,继续走进拥挤的地铁,依然用着自己昨日的观念来思考自己的未来,你的人生也将继续重复着昨日的状况。
如果你看完了,觉得那是一个过来人,对你的人生忠告,并你也愿意用他告诉你的思想去指导自己今后的生活,对你来讲成功不是很难,难的是你是否可以用这篇文章里的思想一直鞭策自己。
如果你看完了,觉得那是一个长辈用他的一生的时间来写的一篇对你忠告的文章,说明你已经有了和他相似的人生阅历,只要你继续努力,成就伟业并不难,难的是你是否可以把自己的人生经验和他人分享呢?

体验决定深度,知识决定广度。你的人生是什么呢?

 

现将原HP公司大中华区总裁孙振耀的一篇文章转发给大家。这是一篇十分深刻却又非常朴实的文章,看了以后很有感触。希望大家也抽空读一读,并有自己的收获。

一、关于工作与生活

外企员工的成功很大程度上是公司的成功,并非个人的成功,西门子的确比国美大,但并不代表西门子中国经理比国美的老板强,甚至可以说差得很远。而进外企的人往往并不能很早理解这一点,把自己的成功90%归功于自己的能力,实际上,外企公司随便换个中国区总经理并不会给业绩带来什么了不起的影响。

二三十岁的时候,生活的压力还比较小,身体还比较好,上面的父母身体还好,下面又没有孩子,不用还房贷,也没有孩子要上大学,当个外企小白领还是很光鲜的,挣得不多也够花了。但是人终归要结婚生子,终归会老,到了40岁,父母老了,要看病要吃药,要有人看护,自己要还房贷,要过基本体面的生活,要养小孩……那个时候需要挣多少钱才够花才重要。所以,看待工作,眼光要放远一点,一时的谁高谁低并不能说明什么。

从这个角度上来说,我不太赞成过于关注第一份工作的薪水,更没有必要攀比第一份工作的薪水,这在刚刚出校园的学生中间是很常见的。正常人大概要工作35年,这好比是一场马拉松比赛,和真正的马拉松比赛不同的是,这次比赛没有职业选手,每个人都只有一次机会。要知到,有很多人甚至坚持不到终点,大多数人最后是走到终点的,只有少数人是跑过终点的,因此在刚开始的时候,去抢领先的位置并没有太大的意义。刚进社会的时候如果进500强公司,大概能拿到3k-6k/月的工资,有些特别技术的人才可能可以到8k/月,可问题是,5年以后拿多少?估计5k-10k了不起了。起点虽然高,但增幅有限,而且,后面的年轻人追赶的压力越来越大。

职业生涯就像一场体育比赛,有初赛、复赛、决赛。初赛的时候大家都刚刚进社会,大多数都是实力一般的人,这时候努力一点认真一点很快就能让人脱颖而出,于是有的人二十多岁做了经理,有的人迟些也终于赢得了初赛,三十多岁成了经理。然后是复赛,能参加复赛的都是赢得初赛的,每个人都有些能耐,在聪明才智上都不成问题,这个时候再想要胜出就不那么容易了,单靠一点点努力和认真还不够,要有很强的坚忍精神,要懂得靠团队的力量,要懂得收服人心,要有长远的眼光…… 

看上去赢得复赛并不容易,但还不是那么难。因为这个世界的规律就是给人一点成功的同时让人骄傲自满,刚刚赢得初赛的人往往不知道自己赢得的仅仅是初赛,有了一点小小的成绩大多数人都会骄傲自满起来,认为自己已经懂得了全部,不需要再努力再学习了,他们会认为之所以不能再进一步已经不是自己的原因了。虽然他们仍然不好对付,但是他们没有耐性,没有容人的度量,更没有清晰长远的目光。就像一只愤怒的斗牛,虽然猛烈,最终是会败的,而赢得复赛的人则象斗牛士一样,不急不躁,跟随着自己的节拍,慢慢耗尽对手的耐心和体力。赢得了复赛以后,大约已经是一位很了不起的职业经理人了,当上了中小公司的总经理,大公司的副总经理,主管着每年几千万乃至几亿的生意。

最终的决赛来了,说实话我自己都还没有赢得决赛,因此对于决赛的决胜因素也只能凭自己的猜测而已,这个时候的输赢或许就像武侠小说里写得那样,大家都是高手,只能等待对方犯错了,要想轻易击败对手是不可能的,除了使上浑身解数,还需要一点运气和时间。这和体育比赛是一样的,最后高手之间的比赛,就看谁失误少谁就赢得了决赛。

二、 根源

你工作快乐么?你的工作好么? 

有没有觉得干了一段时间以后工作很不开心?有没有觉得自己入错了行?有没有觉得自己没有得到应有的待遇?有没有觉得工作像一团乱麻每天上班都是一种痛苦?有没有很想换个工作?有没有觉得其实现在的公司并没有当初想象得那么好?有没有觉得这份工作是当初因为生存压力而找的,实在不适合自己?你从工作中得到你想要得到的了么?你每天开心么? 

天涯上愤怒的人很多,你有没有想过,你为什么不快乐?你为什么愤怒? 

其实,你不快乐的根源,是因为你不知道要什么!你不知道要什么,所以你不知道去追求什么,你不知道追求什么,所以你什么也得不到。 

我总觉得,职业生涯首先要关注的是自己,自己想要什么?大多数人大概没想过这个问题,唯一的想法只是——我想要一份工作,我想要一份不错的薪水,我知道所有人对于薪水的渴望,可是,你想每隔几年重来一次找工作的过程么?你想每年都在这种对于工作和薪水的焦急不安中度过么?不想的话,就好好想清楚。越是焦急,越是觉得自己需要一份工作,越饥不择食,越想不清楚,越容易失败,你的经历越来越差,下一份工作的人看着你的简历就皱眉头。于是你越喝越渴,越渴越喝,陷入恶性循环。最终只能哀叹世事不公或者生不逢时,只能到天涯上来发泄一把,在失败者的共鸣当中寻求一点心理平衡罢了。大多数人都有生存压力,我也是,有生存压力就会有很多焦虑,积极的人会从焦虑中得到动力,而消极的人则会因为焦虑而迷失方向。所有人都必须在压力下做出选择,这就是世道,你喜欢也罢不喜欢也罢。 

一般我们处理的事情分为重要的事情和紧急的事情,如果不做重要的事情就会常常去做紧急的事情。比如锻炼身体保持健康是重要的事情,而看病则是紧急的事情。如果不锻炼身体保持健康,就会常常为了病痛烦恼。又比如防火是重要的事情,而救火是紧急的事情,如果不注意防火,就要常常救火。找工作也是如此,想好自己究竟要什么是重要的事情,找工作是紧急的事情,如果不想好,就会常常要找工作。往往紧急的事情给人的压力比较大,迫使人们去赶紧做,相对来说重要的事情反而没有那么大的压力,大多数人做事情都是以压力为导向的,压力之下,总觉得非要先做紧急的事情,结果就是永远到处救火,永远没有停歇的时候。(很多人的工作也像是救火队一样忙碌痛苦,也是因为工作中没有做好重要的事情。)那些说自己活在水深火热为了生存顾不上那么多的朋友,今天找工作困难是当初你们没有做重要的事情,是结果不是原因。如果今天你们还是因为急于要找一份工作而不去思考,那么或许将来要继续承受痛苦找工作的结果。 

天下没有轻松的成功,成功,要付代价。请先忘记一切的生存压力,想想这辈子你最想要的是什么?所以,最要紧的事情,先想好自己想要什么。

三、什么是好工作 

当初微软有个唐骏,很多大学里的年轻人觉得这才是他们向往的职业生涯,我在清华bbs里发的帖子被这些学子们所不屑,那个时候学生们只想出国或者去外企,不过如今看来,我还是对的,唐骏去了盛大,陈天桥创立的盛大,一家民营公司。一个高学历的海归在500强的公司里拿高薪水,这大约是很多年轻人的梦想,问题是,每年毕业的大学生都在做这个梦,好的职位却只有500个。

人都是要面子的,也是喜欢攀比的,即使在工作上也喜欢攀比,不管那是不是自己想要的。大家认为外企公司很好,可是好在哪里呢?好吧,他们在比较好的写字楼,这是你想要的么?他们出差住比较好的酒店,这是你想要的么?别人会羡慕一份外企公司的工作,这是你想要的么?那一切都是给别人看的,你干吗要活得那么辛苦给别人看?另一方面,他们薪水福利一般,并没有特别了不起,他们的晋升机会比较少,很难做到很高阶的主管,他们虽然厌恶常常加班,却不敢不加班,因为你不干有得是人干,大部分情况下会找个台湾人香港人新加坡人来管你,而这些人又往往有些莫名其妙的优越感。你想清楚了么?500强一定好么?找工作究竟是考虑你想要什么,还是考虑别人想看什么? 

我的大学同学们大多数都到美国了,甚至毕业这么多年了,还有人最近到国外去了。出国真的有那么好么?我的大学同学们,大多数还是在博士、博士后、访问学者地挣扎着,至今只有一个正经在一个美国大学里拿到个正式的教职。国内的教授很难当么?我有几个表亲也去了国外了,他们的父母独自在国内,没有人照顾,有好几次人在家里昏倒都没人知道,出国,真的这么光彩么?就像有人说的很多事情就像看A片,看的人觉得很爽,做的人未必。” 

人总想找到那个最好的,可是,什么是最好的?你觉得是最好的那个,是因为你的确了解,还是因为别人说他是最好的?即使他对于别人是最好的,对于你也一定是最好的么? 

对于自己想要什么,自己要最清楚,别人的意见并不是那么重要。很多人总是常常被别人的意见所影响,亲戚的意见,朋友的意见,同事的意见……问题是,你究竟是要过谁的一生?人的一生不是父母一生的续集,也不是儿女一生的前传,更不是朋友一生的外篇,只有你自己对自己的一生负责,别人无法也负不起这个责任。自己做的决定,至少到最后,自己没什么可后悔。对于大多数正常智力的人来说,所做的决定没有大的对错,无论怎么样的选择,都是可以尝试的。比如你没有考自己上的那个学校,没有入现在这个行业,这辈子就过不下去了?就会很失败?不见得。 

我想,好工作,应该是适合你的工作,具体点说,应该是能给你带来你想要的东西的工作,你或许应该以此来衡量你的工作究竟好不好,而不是拿公司的大小,规模,外企还是国企,是不是有名,是不是上市公司来衡量。小公司,未必不是好公司,赚钱多的工作,也未必是好工作。你还是要先弄清楚你想要什么,如果你不清楚你想要什么,你就永远也不会找到好工作,因为你永远只看到你得不到的东西,你得到的,都是你不想要的。 

可能,最好的,已经在你的身边,只是,你还没有学会珍惜。人们总是盯着得不到的东西,而忽视了那些已经得到的东西。

四、普通人

我发现中国人的励志和国外的励志存在非常大的不同,中国的励志比较鼓励人立下大志愿,卧薪尝胆,有朝一日成富成贵。而国外的励志比较鼓励人勇敢面对现实生活,面对普通人的困境,虽然结果也是成富成贵,但起点不一样,相对来说,我觉得后者在操作上更现实,而前者则需要用 999 个失败者来堆砌一个成功者的故事。

我们都是普通人,普通人的意思就是,概率这件事是很准的。因此,我们不会买彩票中500万,我们不会成为比尔盖茨或者李嘉诚,我们不会坐飞机掉下来,我们当中很少的人会创业成功,我们之中有 30 %的人会离婚,我们之中大部分人会活过65 ……

所以请你在想自己要什么的时候,要得现实一点,你说我想要做李嘉诚,抱歉,我帮不上你。成为比尔盖茨或者李嘉诚这种人,是靠命的,看我写的这篇文章绝对不会让你成为他们,即使你成为了他们,也绝对不是我这篇文章的功劳。王侯将相宁有种乎但真正当皇帝的只有一个人,王侯将相,人也不多。目标定得高些对于喜欢挑战的人来说有好处,但对于大多数普通人来说,反而比较容易灰心沮丧,很容易就放弃了。

回过头来说, 李嘉诚比你有钱大致50 万倍,他比你更快乐么?或许。有没有比你快乐50万倍,一定没有。他比你最多也就快乐一两倍,甚至有可能还不如你快乐 。寻找自己想要的东西不是和别人比赛,比谁要得更多更高,比谁的目标更远大。虽然成为李嘉诚这个目标很宏大,但你并不见得会从这个目标以及追求目标的过程当中获得快乐,而且基本上你也做不到。你必须听听你内心的声音,寻找真正能够使你获得快乐的东西,那才是你想要的东西。

你想要的东西,或者我们把它称之为目标,目标其实并没有高低之分,你不需要因为自己的目标没有别人远大而不好意思,达到自己的目标其实就是成功,成功有大有小,快乐却是一样的。我们追逐成功,其实追逐的是成功带来的快乐,而非成功本身。职业生涯的道路上,我们常常会被攀比的心态蒙住眼睛,忘记了追求的究竟是什么,忘记了是什么能使我们更快乐。

社会上一夜暴富的新闻很多,这些消息,总会在我们的心里面掀起很多涟漪,涟漪多了就变成惊涛骇浪,心里的惊涛骇浪除了打翻承载你目标的小船,并不会使得你也一夜暴富。

只见贼吃肉,不见贼挨揍。我们这些普通人既没有当贼的勇气,又缺乏当贼的狠辣绝决,虽然羡慕吃肉,却更害怕挨揍,偶尔看到几个没挨揍的贼就按奈不住,或者心思活动,或者大感不公,真要叫去做贼,却也不敢。

我还是过普s通人的日子,要普通人的快乐,至少,晚上睡得着觉 。

五、跳槽与积累

首先要说明,工作是一件需要理智的事情,所以不要在工作上耍个性,天涯上或许会有人觉得你很有个性而叫好,煤气公司电话公司不会因为觉得你很有个性而免了你的帐单。当你很帅地炒掉了你的老板,当你很酷地挖苦了一番招聘的 HR ,账单还是要照付,只是你赚钱的时间更少了,除了你自己,没人受损失。

我并不反对跳槽,但跳槽决不是解决问题的办法,而且频繁跳槽的后果是让人觉得没有忠诚度可言,而且不能安心工作。现在很多人从网上找工作,很多找工作的网站常常给人出些馊主意,要知道他们是盈利性企业,当然要从自身盈利的角度来考虑,大家越是频繁跳槽频繁找工作他们越是生意兴隆,所以鼓动人们跳槽是他们的工作。所以他们会常常告诉你,你拿的薪水少了,你享受的福利待遇差了,又是薪情快报又是赞叹自由奔放的灵魂。至于是否会因此让你不能安心,你跳了槽是否解决问题,是否更加开心,那个,他们管不着。

要跳槽肯定是有问题,一般来说问题发生了,躲是躲不开的,很多人跳槽是因为这样或者那样的不开心,如果这种不开心,在现在这个公司不能解决,那么在下一个公司多半也解决不掉。你必须相信, 90% 的情况下,你所在的公司并没有那么烂,你认为不错的公司也没有那么好。就像围城里说的,城里的人拼命想冲出来,而城外的人拼命想冲进去。

每个公司都有每个公司的问题,没有问题的公司是不存在的。换个环境你都不知道会碰到什么问题,与其如此,不如就在当下把问题解决掉。很多问题当你真的想要去解决的时候,或许并没有那么难。有的时候你觉得问题无法解决,事实上,那只是你觉得

人生的曲线应该是曲折向上的,偶尔会遇到低谷但大趋势总归是曲折向上的,而不是象脉冲波一样每每回到起点,我见过不少面试者,30 多岁了,四五份工作经历,每次多则3年,少则 1 年,30 多岁的时候回到起点从一个初级职位开始干起,拿基本初级的薪水,和20多岁的年轻人一起竞争,不觉得有点辛苦么?这种日子好过么?

我非常不赞成在一个行业超过3 年以后换行业,基本上, 35岁以前我们的生存资本靠打拼, 35 岁以生存的资本靠的就是积累,这种积累包括人际关系,经验,人脉,口碑 …… 如果常常更换行业,代表几年的积累付之东流,一切从头开始,如果换了两次行业, 35岁的时候大概只有5年以下的积累,而一个没有换过行业的人至少有了 10 年的积累,谁会占优势?工作到2-3年的时候,很多人觉得工作不顺利,好像到了一个瓶颈,心情烦闷,就想辞职,乃至换一个行业,觉得这样所有一切烦恼都可以抛开,会好很多。其实这样做只是让你从头开始,到了时候还是会发生和原来行业一样的困难,熬过去就向上跨了一大步,要知道每个人都会经历这个过程,每个人的职业生涯中都会碰到几个瓶颈,你熬过去了而别人没有熬过去你就领先了。跑长跑的人会知道,开始的时候很轻松,但是很快会有第一次的难受,但过了这一段又能跑很长一段,接下来会碰到第二次的难受,坚持过了以后又能跑一段,如此往复,难受一次比一次厉害,直到坚持不下去了。大多数人第一次就坚持不了了,一些人能坚持到第二次,第三次虽然大家都坚持不住了,可是跑到这里的人也没几个了,这点资本足够你安稳活这一辈子了。

一份工作到两三年的时候,大部分人都会变成熟手,这个时候往往会陷入不断的重复,有很多人会觉得厌倦,有些人会觉得自己已经搞懂了一切,从而懒得去寻求进步了。很多时候的跳槽是因为觉得失去兴趣了,觉得自己已经完成比赛了。其实这个时候比赛才刚刚开始,工作两三年的人,无论是客户关系,人脉,手下,和领导的关系,在业内的名气……

还都是远远不够的,但稍有成绩的人总是会自我感觉良好的,每个人都觉得自己跟客户关系铁得要命,觉得自己在业界的口碑好得很。其实可以肯定地说,一定不是,这个时候,还是要拿出前两年的干劲来,稳扎稳打,积累才刚刚开始 。

你足够了解你的客户吗?你知道他最大的烦恼是什么吗?你足够了解你的老板么?你知道他最大的烦恼是什么吗?你足够了解你的手下么?你知道他最大的烦恼是什么吗?如果你不知道,你凭什么觉得自己已经积累够了?如果你都不了解,你怎么能让他们帮你的忙,做你想让他们做的事情?如果他们不做你想让他们做的事情,你又何来的成功?

六、等待

这是个浮躁的人们最不喜欢的话题,本来不想说这个话题,因为会引起太多的争论,而我又无意和人争论这些,但是考虑到对于职业生涯的长久规划,这是一个躲避不了的话题,还是决定写一写,不爱看的请离开吧。

并不是每次穿红灯都会被汽车撞,并不是每个罪犯都会被抓到,并不是每个错误都会被惩罚,并不是每个贪官都会被枪毙,并不是你的每一份努力都会得到回报,并不是你的每一次坚持都会有人看到,并不是你每一点付出都能得到公正的回报,并不是你的每一个善意都能被理解 …… 这个,就是世道。好吧,世道不够好,可是,你有推翻世道的勇气么?如果没有,你有更好的解决办法么?有很多时候,人需要一点耐心,一点信心。每个人总会轮到几次不公平的事情,而通常,安心等待是最好的办法。

有很多时候我们需要等待,需要耐得住寂寞,等待属于你的那一刻。周润发等待过,刘德华等待过,周星驰等待过,王菲等待过,张艺谋也等待过…… 看到了他们如今的功成名就的人,你可曾看到当初他们的等待和耐心?你可曾看到金马奖影帝在街边摆地摊?你可曾看到德云社一群人在剧场里给一位观众说相声?你可曾看到周星驰的角色甚至连一句台词都没有?每一个成功者都有一段低沉苦闷的日子,我几乎能想象得出来他们借酒浇愁的样子,我也能想象得出他们为了生存而挣扎的窘迫。在他们一生最中灿烂美好的日子里,他们渴望成功,但却两手空空,一如现在的你。没有人保证他们将来一定会成功,而他们的选择是耐住寂寞。如果当时的他们总念叨着成功只是属于特权阶级的,你觉得他们今天会怎样?

曾经我也不明白有些人为什么并不比我有能力却要坐在我的头上,年纪比我大就一定要当我的领导么?为什么有些烂人不需要努力就能赚钱?为什么刚刚改革开放的时候的人能那么容易赚钱,而轮到我们的时候,什么事情都要正规化了?有一天我突然想,我还在上学的时候他们就在社会里挣扎奋斗了,他们在社会上奋斗积累了十几二十年,我们新人来了,他们有的我都想要,我这不是在要公平,我这是在要抢劫。因为我要得太急,因为我忍不住寂寞。二十多岁的男人,没有钱,没有事业,却有蓬勃的欲望 。

人总是会遇到挫折的,人总是会有低潮的,人总是会有不被人理解的时候的,人总是有要低声下气的时候,这些时候恰恰是人生最关键的时候,因为大家都会碰到挫折,而大多数人过不了这个门槛,你能过,你就成功了。在这样的时刻,我们需要耐心等待,满怀信心地去等待,相信,生活不会放弃你,机会总会来的。至少,你还年轻,你没有坐牢,没有生治不了的病,没有欠还不起的债。比你不幸的人远远多过比你幸运的人,你还怕什么?路要一步步走,虽然到达终点的那一步很激动人心,但大部分的脚步是平凡甚至枯燥的,但没有这些脚步,或者耐不住这些平凡枯燥,你终归是无法迎来最后的那些激动人心。

逆境,是上帝帮你淘汰竞争者的地方。要知道,你不好受,别人也不好受,你坚持不下去了,别人也一样,千万不要告诉别人你坚持不住了,那只能让别人获得坚持的信心,让竞争者看着你微笑的面孔,失去信心,退出比赛。胜利属于那些有耐心的人。

在最绝望的时候,我会去看电影《 The Pursuit of Happyness》(当幸福来敲门)《 Jerry Maguire 》,让自己重新鼓起勇气,因为,无论什么时候,我们总还是有希望。当所有的人离开的时候,我不失去希望,我不放弃。每天下班坐在车里,我喜欢哼着《隐形的翅膀》看着窗外,我知道,我在静静等待,等待属于我的那一刻。

原贴里伊吉网友的话我很喜欢,抄录在这里:

每个人都希望,自己是独一无二的特殊者含着金匙出生、投胎到好家庭、工作安排到电力局拿 1w月薪这样的小概率事件,当然最好轮到自己红军长征两万五、打成右派反革命、胼手胝足牺牲尊严去奋斗,最好留给祖辈父辈和别人自然,不是每个吃过苦的人都会得到回报,但是,任何时代,每一个既得利益者身后,都有他的祖辈父辈奋斗挣扎乃至流血付出生命的身影羡慕别人有个好爸爸,没什么不可以问题是,你的下一代,会有一个好爸爸吗?

七、入对行跟对人

在中国,大概很少有人是一份职业做到底的第一份工作还是有些需要注意的地方,有两件事情格外重要,第一件是入行,第二件事情是跟人其实没有哪个行业特别好,也没有哪个行业特别差,或许有报道说哪个行业的平均薪资比较高,但是他们没说的是,那个行业的平均压力也比较大。看上去很美的行业一旦进入才发现很多地方其实并不那么完美,只是外人看不见。

我的建议只是让人快乐工作的建议,不是如何发大财的建议,我们只讨论一般普通打工者的情况。我认为选择什么行业并没有太大关系,看问题不能只看眼前 从前年开始,国家开始整顿医疗行业,很多医药公司开不下去,很多医药行业的销售开始转行。其实医药行业的不景气是针对所有公司的,并非针对一家公司,大家的日子都不好过,这个时候跑掉是非常不划算的,大多数正规的医药公司即使不做新生意撑个两三年总是能撑的,大多数医药销售靠工资撑个两三年也是可以撑的,国家不可能永远捏着医药行业不放的,两三年以后光景总归还会好起来的,那个时候别人都跑了而你没跑,那时的日子应该会好过很多。

有的时候觉得自己这个行业不行了,问题是,再不行的行业,做得人少了也变成了好行业,当大家都觉得不好的时候,往往却是最好的时候。大家都觉得金融行业好,金融行业门槛高不说,有多少人削尖脑袋要钻进去,竞争激励,进去以后还要时时提防,一个疏忽,就被后来的人给挤掉了,压力巨大,又如何谈得上快乐?也就未必是工作了。

其实选什么行业真的不重要,关键是怎么做。事情都是人做出来的,关键是人。

有一点是需要记住的,这个世界上,有史以来直到我们能够预见得到的未来,成功的人总是少数,有钱的人总是少数,大多数人是一般的,普通的,不太成功的。因此,大多数人的做法和看法,往往都不是距离成功最近的做法和看法。因此大多数人说好的东西不见得好,大多数人说不好的东西不见得不好。大多数人都去炒股的时候说明跌只是时间问题,大家越是热情高涨的时候,跌的日子越近。

年轻人在职业生涯的刚开始,尤其要注意的是,要做对的事情,不要让自己今后几十年的人生总是提心吊胆,更不值得为了一份工作赔上自己的青春年华。勇于给钱的公司,不是倒了,就是跑了,要不就是每天睡不好觉,人还是要看长远一点。很多时候,看起来最近的路,其实是最远的路,看起来最远的路,其实是最近的路。

跟对人是说,入行后要跟个好领导好老师,刚进社会的人做事情往往没有经验,需要有人言传身教。对于一个人的发展来说,一个好领导是非常重要的。所谓的标准,不是他让你少干活多拿钱,而是以下三个。

首先,好领导要有宽广的心胸,其次,领导要愿意从下属的角度来思考问题 第三,领导敢于承担责任 多认识一些人,多和比自己强的人打交道,同样能找到好的老师,不要和一群同样郁闷的人一起控诉社会,控诉老板,这帮不上你,只会让你更消极。和那些比你强的人打交道,看他们是怎么想的,怎么做的,学习他们,然后跟更强的人打交道。

八、选择

每天,每一刻我们都在做这样那样的决定,我们可以漫不经心,也可以多花些心思,成千上万的小选择累计起来,就决定了最终我们是个什么样的人。

从某种意义上来说我们的未来不是别人给的,是我们自己选择的,大多数人都没有什么选择 但每天你都可以选择是否为客户服务更周到一些,是否对同事更耐心一些,是否把工作做得更细致一些,是否把情况了解得更清楚一些,是否把不清楚的问题再弄清楚一些……

你也可以选择在是否在痛苦中继续坚持,是否抛弃掉自己的那些负面的想法,是否原谅一个人的错误,是否相信我在这里写下的这些话,是否不要再犯同样的错误……

生活每天都在给你选择的机会,每天都在给你改变自己人生的机会,你可以选择赖在地上撒泼打滚,也可以选择咬牙站起来。你永远都有选择。

每天生活都在给你机会,他不会给你一叠现金也不会拱手送你个好工作,但实际上,他还是在给你机会。

你最终会成为什么样的人,就决定在你的每个小小的选择之间。  

你选择相信什么?你选择和谁交朋友?你选择做什么?你选择怎么做?……

选择做什么产品其实并不那么重要,而选择怎么做才重要。选择用什么人并不重要,而选择怎么带这些人才重要。

一个大学生毕业了,他要去微软也好,他要卖猪肉也好,他要创业也好,他要做游戏代练也好,只要不犯法,不害人,都没有什么关系,要紧的是,选择了以后,怎么把事情做好。

你还可以选择时间和环境,比如,你可以选择把这辈子最大的困难放在最有体力最有精力的时候,也可以走一步看一步,等到了40岁再说,只是到了40多岁,那正是一辈子最脆弱的时候,上有老下有小,如果在那个时候碰上了职业危机,实在是一件很苦恼的事情。与其如此不如在20多岁30多岁的时候吃点苦,好让自己脆弱的时候活得从容一些。你可以选择在温室里成长,也可以选择到野外磨砺,你可以选择在办公室吹冷气的工作,也可以选择40度的酷热下,去见你的客户,只是,这一切最终会累积起来,引导你到你应得的未来。

绝大部分事情你有选择,只是往往你不把这当作一种选择。认真对待每一次选择,才会有比较好的未来。

 九、选择职业 

职业的选择,总的来说,无非就是销售、市场、客服、物流、行政、人事、财务、技术、管理几个大类有个有趣的现象就是,500强的CEO当中最多的是销售出身,第二多的人是财务出身,这两者加起来大概超过95%。

销售就是一门跟人打交道的学问,而管理其实也是跟人打交道的学问很多人在工作中觉得为什么领导这么保守,这也不行那也不行,错过很多机会。很多时候是因为,你还年轻,你想的是能做什么,而作为公司领导要考虑的方面很多,他比较关心不能做什么  

究竟选择什么样的职业,和你究竟要选择什么样的人生有关系,有些人就喜欢下班按时回家,看看书听听音乐,那也挺好,但就不适合找个销售的工作了,否则会是折磨自己。

先想好自己要过怎样的人生,再决定要找什么样的职业。有很多的不快乐,其实是源自不满足,而不满足,很多时候是源自于心不定,而心不定则是因为不清楚究竟自己要什么,不清楚要什么的结果就是什么都想要,结果什么都没得到。

我想,我们还是因为生活而工作,不是因为工作而生活,生活是最要紧的,工作只是生活中的一部分。所以要有娱乐、要有社交、要锻炼身体,要有和睦的家庭……最要紧的,要开心如果你做得越好越不开心,那你为什么还要工作?首先的首先,人还是要让自己高兴起来,让自己心态好起来,这种发自内心的改变会让你更有耐心,更有信心,更有气质,更能包容……否则,看看镜子里的你,你满意么? 

人会说,你说得容易,我每天加班,不加班老板就会把我炒掉,每天累得要死,哪有时间娱乐、社交、锻炼?

你没时间去健身房锻炼身体,但是上下班的时候多走几步可以吧,有楼梯的时候走走楼梯不走电梯可以吧?办公的间隙扭扭脖子拉拉肩膀做做俯卧撑可以吧?谁规定锻炼就一定要拿出每天2个小时去健身房?你没时间社交,每月参加郊游一次可以吧,周末去参加个什么音乐班,绘画班之类的可以吧,去尝试认识一些同行,和他们找机会交流交流可以吧?开始的时候总是有些难的,但迈出这一步就会向良性循环的方向发展。而每天工作得很苦闷,剩下的时间用来咀嚼苦闷,只会陷入恶性循环,让生活更加糟糕。

虽然离开惠普仅有十五天,但感觉上惠普已经离我很远。我的心思更多放在规划自己第二阶段的人生,这并非代表我对惠普没有任何眷恋,主要还是想以此驱动自己往前走。 

由于受我父亲早逝的影响,我很早就下定决心,要在有生之年实现自己的愿望,我不要像我父亲一样,为家庭生活忙碌一辈子,临终前感伤,懊恼自己有很多没有实现的理想。 

Carly Fiorina曾经对我说过这个世界上有好想法的人很多,但有能力去实现的人很少2007521日在北大演讲时,有人问起那些书对我影响较大,我想对我人生观有影响的其中一本书叫“TriggerPoint”,它的主要观点是:人生最需要的不是规划,而是在适当的时机掌握机会,采取行动。

放松自己,不给事情设定目标及时间表不正确,没有年度,季度,月及周计划也不正确。

我的人生观是完美的演出来自充分的准备勇于改变自己,适应不断变化的环境,机会将不断出现快乐及有意义的人生来自于实现自己心中的愿望,而非外在的掌声

我总结人生有三个阶段,一个阶段是为现实找一份工作,一个阶段是为现实,但可以选择一份自己愿意投入的工作,一个阶段是为理想去做一些事情。 

   下一页