├── README.md ├── android └── README.md ├── cpp └── README.md ├── css └── README.md ├── html └── README.md ├── javascript └── README.md ├── objective-c └── README.md └── python ├── README.md └── django └── README.md /README.md: -------------------------------------------------------------------------------- 1 | 云匠科技官方编程规范 2 | ==================== 3 | 4 | 由云匠工程师们热情创建和维护。 5 | 6 | -------------------------------------------------------------------------------- /android/README.md: -------------------------------------------------------------------------------- 1 | 2 | ### 文件的命名 3 | #### 类的命名 4 | 命名应该遵循[驼峰命名法](http://baike.baidu.com/link?url=36TNYWM87ZKQKN5r1RayLumvi7wqv3vmVcgi7eicJVD4VpbpNyMUp443RFJ4coFeosuNIg1TZny2p9fTTlpOva) 5 | 对于继承自 Android 组件的类来说,命名应以该组件的名称结尾;例如: `SignInActivity`, `SignInFragment`, `ImageUploaderService`, `ChangePasswordDialog`。 6 | #### Res 中文件的命名 7 | 资源文件应该以小写 + 下划线( _ )的格式命名。 8 | ##### 图片文件 9 | 以下是对于图片文件的命名习惯 10 | 11 | | Asset Type | Prefix | Example | 12 | |:--------------| :------------------|:-----------------------------| 13 | | Action bar | `ab_` | `ab_stacked.9.png` | 14 | | Button | `btn_` | `btn_send_pressed.9.png` | 15 | | Dialog | `dialog_` | `dialog_top.9.png` | 16 | | Divider | `divider_` | `divider_horizontal.9.png` | 17 | | Icon | `ic_` | `ic_star.png` | 18 | | Menu | `menu_ ` | `menu_submenu_bg.9.png` | 19 | | Notification | `notifi_` | `notifi_bg.9.png` | 20 | | Tabs | `tab_` | `tab_pressed.9.png` | 21 | 22 | 对于图标的命名习惯 23 | 24 | | Asset Type | Prefix | Example | 25 | | :--------------------------------| :---------------- | :---------------------------- | 26 | | Icons | `ic_` | `ic_star.png` | 27 | | Launcher icons | `ic_launcher` | `ic_launcher_calendar.png` | 28 | | Menu icons and Action Bar icons | `ic_menu` | `ic_menu_archive.png` | 29 | | Status bar icons | `ic_stat_notify` | `ic_stat_notify_msg.png` | 30 | | Tab icons | `ic_tab` | `ic_tab_recent.png` | 31 | | Dialog icons | `ic_dialog` | `ic_dialog_info.png` | 32 | 33 | 对于选择器状态的命名习惯 34 | 35 | | State | Suffix | Example | 36 | |:--------------|:-----------------|:-----------------------------| 37 | | Normal | `_normal` | `btn_order_normal.9.png` | 38 | | Pressed | `_pressed` | `btn_order_pressed.9.png` | 39 | | Focused | `_focused` | `btn_order_focused.9.png` | 40 | | Disabled | `_disabled` | `btn_order_disabled.9.png` | 41 | | Selected | `_selected` | `btn_order_selected.9.png` | 42 | 43 | ##### 布局文件 44 | 布局文件应该和将要用于的 Android 组件的名称相匹配,但是这次应以组件的名称开头。例如, 如果我们为 `SignInActivity`,创建布局文件,那布局文件的名称就应该为 `activity_sign_in.xml`. 45 | 46 | | Component | Class Name | Layout Name | 47 | | ---------------- | ---------------------- | ----------------------------- | 48 | | Activity | `UserProfileActivity` | `activity_user_profile.xml` | 49 | | Fragment | `SignUpFragment` | `fragment_sign_up.xml` | 50 | | Dialog | `ChangePasswordDialog` | `dialog_change_password.xml` | 51 | | AdapterView item | --- | `item_person.xml` | 52 | | Partial layout | --- | `partial_stats_bar.xml` | 53 | 54 | 一个特殊的情况就是在为 Adapter 中的子项创建布局的时候, 例如, 显示 ListView 中的内容。在这种情况下,布局文件的前缀应该为 `item_` 55 | 应该注意到还有一个特殊情况的存在,那就是在创建一个布局中的其中一小块布局时,在这种情况下就应该使用前缀 `partial_` 56 | ##### menu 文件 57 | 与布局文件的命名的规则相似,menu 文件也应该和将要用于的 Android 组件的名称相匹配。例如,当我们在为 `UserActivity` 创建 menu 文件时,那 menu 文件的名称就应该是 `activity_user.xml` 58 | 命名规则是不把单词 `menu` 作为名称的一部分,因为这些文件已经存放在 `menu` 的文件下了。 59 | ##### 资源文件 60 | 在 `values` 文件夹中的资源文件在命名时应该为复数。例如, `strings.xml`, `styles.xml`, `colors.xml`, `dimens.xml`, `attrs.xml` 61 | ## 代码规范 62 | ### Java 语言的规范 63 | #### 不要忽略异常的处理 64 | 永远不要编写出以下的代码 65 | ```Java 66 | void setServerPort(String value) { 67 | try { 68 | serverPort = Integer.parseInt(value); 69 | } catch (NumberFormatException e) { } 70 | } 71 | ``` 72 | 不要认为你的代码永远不会触发此类的异常或者不足以处理,或者如以上代码似的,在你的代码里留下缺口让别人在以后帮你来填上.你必须按照规范来捕获每一个异常.也可以查看 [Android 官方的文档描述](https://source.android.com/source/code-style.html#dont-ignore-exceptions). 73 | #### 不要捕获通用的异常 74 | 永远不要编写以下的代码 75 | ```Java 76 | try { 77 | someComplicatedIOFunction(); // may throw IOException 78 | someComplicatedParsingFunction(); // may throw ParsingException 79 | someComplicatedSecurityFunction(); // may throw SecurityException 80 | // phew, made it all the way 81 | } catch (Exception e) { // I'll just catch all exceptions 82 | handleError(); // with one generic handler! 83 | } 84 | ``` 85 | 具体原因可以查看 [Android 官方文档描述](https://source.android.com/source/code-style.html#dont-catch-generic-exception) 86 | #### 不要使用 finalizers 87 | 不要使用 finalizers ([不知道 finalize 的可以查看这里](http://stackoverflow.com/questions/2506488/when-is-the-finalize-method-called-in-java)). 即便 finalizer 最终都是会被调用的但是什么时候会别调用是没有保证的. 在大多数的情况下,你可以通过好的异常捕获机制来取代 finalizer. 如果在某种情况下你必须要使用到它, 定义一个 `close()` 方法(或者类似的)然后准确的说明一下什么时候该方法会被调用到. 88 | #### 规范的引用 89 | 这是一个不好的引用编写: `import foo.*;` 90 | 这是一个合格的引用编写: `import foo.Bar;` 91 | 更多信息查看[这里](https://source.android.com/source/code-style.html#fully-qualify-imports) 92 | 93 | ### Java 风格规范 94 | #### 变量的定义与命名 95 | 变量应该定义在文件头部的位置,并且应该遵循以下的命名规则. 96 | - Private, 非静态变量应该以 **m** 开头命名 97 | - Private, 静态变量应该以 **s** 开头命名 98 | - 其余的变量应该以小写字母开头 99 | - 静态的常量应该都是以大写字母加下划线的格式命名. 例如, `ALL_CAPS_WITH_UNDERSCORES`. 100 | 例子如下 101 | ```Java 102 | public class MyClass { 103 | public static final int SOME_CONSTANT = 42; 104 | public int publicField; 105 | private static MyClass sSingleton; 106 | int mPackagePrivate; 107 | private int mPrivate; 108 | protected int mProtected; 109 | } 110 | ``` 111 | #### 将英文的缩略词也看做成一个单词 112 | | Good | Bad | 113 | | -------------- | -------------- | 114 | | `XmlHttpRequest` | `XMLHTTPRequest` | 115 | | `getCustomerId` | `getCustomerID` | 116 | | `String url` | `String URL` | 117 | | `long id` | `long ID` | 118 | #### 使用空格来进行缩进 119 | 使用**4 个空格**来进行代码块的缩进 120 | ```Java 121 | if (x == 1) { 122 | x++; 123 | } 124 | ``` 125 | 使用** 8 个空格**来进行代码的换行 126 | ```Java 127 | Instrument i = 128 | someLongExpression(that, wouldNotFit, on, one, line); 129 | ``` 130 | #### 大括号的使用规范 131 | 左大括号应该跟在其之前的代码在同一行上 132 | ```Java 133 | class MyClass { 134 | int func() { 135 | if (something) { 136 | // ... 137 | } else if (somethingElse) { 138 | // ... 139 | } else { 140 | // ... 141 | } 142 | } 143 | } 144 | ``` 145 | 如果条件语句跟结果语句正好可以在同一行上并且其长度也小于同一行的最大长度限制, 则可以省略大括号 146 | 例如 147 | ```Java 148 | if (condition) body(); 149 | ``` 150 | 错误的范例 151 | ```Java 152 | if (condition) 153 | body(); // bad! 154 | ``` 155 | #### 注解 156 | ##### 注解的应用 157 | 根据 Android 官方文档, 在 Java 中对于一些预先确定的注解的标准应用如下 158 | - `@Override`: 该注解**必须用与**任何时候想要重写或者实现父类的某个方法的时候. 例如,当你使用了 `@inheritDocs` 标签,并且是源于一个类而并不是接口的时候,你必须同时也在此方法上加上 `@Override` 标签. 159 | - `@SuppressWarnings`: 该标签只有在遇到无法忽略的警告的条件下才可以使用. 如果一个警告符合"无法忽略掉"的条件时,该标签是必须需要被使用的,为的是保证所有的警告都能反映出代码中实际存在的问题. 160 | 更多关于注解的规范请参考[这里](http://source.android.com/source/code-style.html#use-standard-java-annotations) 161 | 162 | ##### 注解格式 163 | - 类,方法和构造函数: 当注解被用于类,方法和构造函数的时候,应该将注解位于注释的下面,并且每个注解作为一行的形式.如下所示 164 | ```Java 165 | /* This is the documentation block about the class */ 166 | @AnnotationA 167 | @AnnotationB 168 | public class MyAnnotatedClass { } 169 | ``` 170 | - 对象: 注解应该与对象保持在同一行,除非该行达到了最大字符的限制数. 171 | ```Java 172 | @Nullable @Mock DataManager mDataManager; 173 | ``` 174 | #### 限制变量的作用域 175 | 局部变量的作用域应该保持到最小.这样可以增加代码的可读性和维护性,并且降低出错的概率. 176 | 局部变量应该尽量在它第一次被调用的时候被声明出来.而且声明局部变量时应该初始化该变量,如果你还没有足够的信息来初始化该变量,那就应该推迟声明直到拥有足够的信息来初始化此变量的时候.更多信息可查看[这里](https://source.android.com/source/code-style.html#limit-variable-scope) 177 | #### 日志的规范 178 | 请使用公司通用的 `LogUtil` 类来取代 Android 原生的 `Log` 类来打印日志. 179 | #### 类成员排列的规范 180 | 这部分没有强制的要求,但是使用一种合乎逻辑并且常用的方式来排列类成员,可以增强代码的可读性 181 | - 常量 182 | - 对象 183 | - 构造函数 184 | - 重写和回调函数(包括公有的和私有的) 185 | - 公有函数 186 | - 私有函数 187 | - 内部类及内部接口 188 | 189 | 例如: 190 | ```Java 191 | public class Child extends Parent { 192 | 193 | private static final int CONSTANT = 1; 194 | 195 | private String mName; 196 | private int mAge; 197 | 198 | public Child(String name, int age) { 199 | mName = name; 200 | mAge = age; 201 | } 202 | 203 | @Override 204 | public void changeName() { 205 | ... 206 | } 207 | 208 | public void setName(String name) { 209 | mName = name; 210 | } 211 | 212 | private void setSomething() { 213 | ... 214 | } 215 | 216 | static class AnInnerClass { 217 | 218 | } 219 | } 220 | ``` 221 | 如果你的类是继承自**Android 的组件**,例如 Activity 和 Fragment, 比较好的习惯是按照该组件的生命周期来重写方法.例如,如果你有一个 Activity 实现了 `onCreate()`, `onDestroy()`, `onPause()` 和 `onResume()`,则正确的顺序为: 222 | ```Java 223 | public class MainActivity extends Activity { 224 | 225 | //Order matches Activity lifecycle 226 | @Override 227 | public void onCreate() {} 228 | 229 | @Override 230 | public void onResume() {} 231 | 232 | @Override 233 | public void onPause() {} 234 | 235 | @Override 236 | public void onDestroy() {} 237 | 238 | } 239 | ``` 240 | #### 函数中参数的顺序 241 | 在编写 Android 的代码时,函数中含有参数 `Context` 是非常常见的.如果遇到这种情况,那么必须将**Context**作为第一个参数. 242 | 对应的**回调接口**应该永远作为函数的最后一个参数 243 | 例如: 244 | ```Java 245 | // Context always goes first 246 | public User loadUser(Context context, int userId); 247 | 248 | // Callbacks always go last 249 | public void loadUserAsync(Context context, int userId, UserCallback callback); 250 | ``` 251 | #### 字符串常量的命名 252 | Android SDK 中包含很多需要键值对的元素例如, `SharedPreferences`, `Bundle` 和 `Intent`.即使在写一个很小的 app 应用时,也会产生很多字符串常量. 253 | 当使用以上组件的时候,你**必须**将字符串定义为 `static final`,并且它们的前缀应该遵循以下的命名规则: 254 | 255 | | Element | Field Name Prefix | 256 | | ----------------- | ----------------- | 257 | | SharedPreferences | `PREF_` | 258 | | Bundle | `BUNDLE_` | 259 | | Fragment Arguments | `ARGUMENT_` | 260 | | Intent Extra | `EXTRA_` | 261 | | Intent Action | `ACTION_` | 262 | | Handler Action | `ACTION_` | 263 | 虽然 `Fragment.getArguments()` 返回的也是一个 Bundle,但是为了用于区分,所以使用 `ARGUMENT_` 作为其前缀. 264 | ```Java 265 | // Note the value of the field is the same as the name to avoid duplication issues 266 | static final String PREF_EMAIL = "PREF_EMAIL"; 267 | static final String BUNDLE_AGE = "BUNDLE_AGE"; 268 | static final String ARGUMENT_USER_ID = "ARGUMENT_USER_ID"; 269 | 270 | // Intent-related items use full package name as value 271 | static final String EXTRA_SURNAME = "com.myapp.extras.EXTRA_SURNAME"; 272 | static final String ACTION_OPEN_USER = "com.myapp.action.ACTION_OPEN_USER"; 273 | ``` 274 | #### Fragment 和 Activity 中的参数 275 | 当数据通过 `Intent` 或 `Bundle` 传递给 `Fragment` 和 `Activity`的时候, Key 的命名必须要遵循以上的命名规范. 276 | 当 `Activity` 或 `Fragment` 需要接受参数的时候, 需要创建一个 `public static` 的方法来创建与之相关的 `Intent` 或 `Fragment`. 277 | Activity 的情况下,通常将方法命名为 `getStartIntent()`: 278 | ```Java 279 | public static Intent getStartIntent(Context context, User user) { 280 | Intent intent = new Intent(context, ThisActivity.class); 281 | intent.putParcelableExtra(EXTRA_USER, user); 282 | return intent; 283 | } 284 | ``` 285 | Fragment 的情况下,通常将方法命名为 `newInstance()`,通过传入的参数来创建 Fragment 286 | ```Java 287 | public static UserFragment newInstance(User user) { 288 | UserFragment fragment = new UserFragment; 289 | Bundle args = new Bundle(); 290 | args.putParcelable(ARGUMENT_USER, user); 291 | fragment.setArguments(args) 292 | return fragment; 293 | } 294 | ``` 295 | **注意 1**:此类方法应该声明在类的前部, `onCreate()` 方法之前 296 | **注意 2**:如果我们已经声明了以上的方法,那么为 Intent 或 Bundle 声明的 `Key` 应该是 `private`, 因为不需要类之外来使用. 297 | #### 行长度的限制 298 | 一行代码的长度不应该超过**100 个字符**,如果一行代码过长,通常有如下两种方法来减少代码的长度: 299 | - 提取出一个`局部变量`或`方法`**(推荐)** 300 | - 通过换行将一行代码变为多行 301 | 302 | 有两种例外的情况可以允许行代码超过 100 个字符 303 | - 无法换行的代码,例如: 网址 URL 304 | - `package` 和 `import` 的声明 305 | 306 | ##### 换行的规范 307 | 这里也没有强制行的规范,但是有几条比较通用的规范希望可以遵守 308 | **运算符** 309 | 当要在运算符处进行换行的时候,换行应该在运算符之前,例如: 310 | ```Java 311 | int longName = anotherVeryLongVariable + anEvenLongerOne - thisRidiculousLongOne 312 | + theFinalOne; 313 | ``` 314 | 但是以上规则不适用于 `=` 运算符,换行应该在等号运算符之后,例如: 315 | ```Java 316 | int longName = 317 | anotherVeryLongVariable + anEvenLongerOne - thisRidiculousLongOne + theFinalOne; 318 | ``` 319 | **方法链的情况下** 320 | 当多个方法在同一行组合在一起成为方法链的时候.例如, Builder 模式下,每一个方法应该独立成一行,并换行应该在 `.` 之前. 321 | ```Java 322 | Picasso.with(context).load("http://ribot.co.uk/images/sexyjoe.jpg").into(imageView); 323 | ``` 324 | ```Java 325 | Picasso.with(context) 326 | .load("http://ribot.co.uk/images/sexyjoe.jpg") 327 | .into(imageView); 328 | ``` 329 | **多个参数的情况下** 330 | 当一个函数多个参数并且参数过长时,我们应该在 `,` 后进行换行. 331 | ```Java 332 | loadPicture(context, "http://ribot.co.uk/images/sexyjoe.jpg", mImageViewProfilePicture, clickListener, "Title of the picture"); 333 | ``` 334 | ```Java 335 | loadPicture( 336 | context, 337 | "http://ribot.co.uk/images/sexyjoe.jpg", 338 | mImageViewProfilePicture, 339 | clickListener, 340 | "Title of the picture" 341 | ); 342 | ``` 343 | #### 针对于 RxJava 的规范 344 | Rx 的方法链同样需要换行.每一个操作必须独立为一行,而且换行应该在 `.` 之前. 345 | ```Java 346 | public Observable syncLocations() { 347 | return mDatabaseHelper.getAllLocations() 348 | .concatMap(new Func1>() { 349 | @Override 350 | public Observable call(Location location) { 351 | return mRetrofitService.getLocation(location.id); 352 | } 353 | }) 354 | .retry(new Func2() { 355 | @Override 356 | public Boolean call(Integer numRetries, Throwable throwable) { 357 | return throwable instanceof RetrofitError; 358 | } 359 | }); 360 | } 361 | ``` 362 | ## XML 的规范 363 | ### 使用自结束标签 364 | 当一个 XML 里的元素内没有其他元素时,应该使用自结束标签 365 | 这是正确的: 366 | ```xml 367 | 371 | ``` 372 | 这是错误的: 373 | ```xml 374 | 375 | 379 | 380 | ``` 381 | ### 资源的命名 382 | 资源的 ID 和名称都应该是**小写 + 下划线**的格式 383 | #### ID 的命名 384 | ID 应该以控件的名称作为前缀来命名.例如: 385 | 386 | | Element | Prefix | 387 | | ----------------- | ----------------- | 388 | | `TextView` | `text_` | 389 | | `ImageView` | `image_` | 390 | | `Button` | `button_` | 391 | | `Menu` | `menu_` | 392 | ImageView 例子: 393 | ```xml 394 | 398 | 399 | ``` 400 | Menu 例子: 401 | ```xml 402 | 403 | 406 | 407 | ``` 408 | ##### 字符串 409 | 字符串的名字应该以一个可以标明其所属领域的前缀来做开头. 例如, `registration_email_hint` 或 `registration_name_hint`.如果一个字符串不属于任何的领域,那应该遵循以下规则: 410 | 411 | | Prefix | Description | 412 | | ----------------- | --------------------------------------| 413 | | `error_` | An error message | 414 | | `msg_` | A regular information message | 415 | | `title_` | A title, i.e. a dialog title | 416 | | `action_` | An action such as "Save" or "Create" | 417 | #### Style 和 Theme 418 | 不同于其他的资源命名规范, Style 中的名字应该遵循[驼峰命名法](http://baike.baidu.com/link?url=36TNYWM87ZKQKN5r1RayLumvi7wqv3vmVcgi7eicJVD4VpbpNyMUp443RFJ4coFeosuNIg1TZny2p9fTTlpOva) 419 | #### Color 420 | color 中颜色的命名应该遵循**小写 + 下划线**的格式 421 | ### 提高 Android 性能的编码规范 422 | #### 不要在 Android 程序里使用 enum 423 | 虽然使用 enum 很方便,但是会比使用静态变量产生多于两倍的内存消耗,所以 Android 官方强烈建议不要在Android程序里面使用到 enum. 424 | 使用 Android Typedef Annotations 可以代替 enum, 具体的使用方法请参考[这里](http://tools.android.com/tech-docs/support-annotations) 425 | #### 关于数组遍历 426 | ```Java 427 | static class Foo { 428 | int mSplat; 429 | } 430 | 431 | Foo[] mArray = ... 432 | 433 | // 最慢,消耗最多 434 | public void zero() { 435 | int sum = 0; 436 | for (int i = 0; i < mArray.length; ++i) { 437 | sum += mArray[i].mSplat; 438 | } 439 | } 440 | 441 | public void one() { 442 | int sum = 0; 443 | Foo[] localArray = mArray; 444 | int len = localArray.length; 445 | 446 | for (int i = 0; i < len; ++i) { 447 | sum += localArray[i].mSplat; 448 | } 449 | } 450 | 451 | // 推荐 452 | public void two() { 453 | int sum = 0; 454 | for (Foo a : mArray) { 455 | sum += a.mSplat; 456 | } 457 | } 458 | ``` 459 | - `zero()` 是最慢的方法,因为 JIT 还不能对当数组进行遍历时每次都要去获得数组的长度进行优化. 460 | - `one()` 更快一点, 因为它所有的数据都拿出来存放在局部变量里,避免了查找.只有提供了数组的长度对性能有了一定的提升 461 | - `two()` 在没有 JIT 的设备里是最快的,但在有 JIT 的设备里与**one()**难分上下.它使用了 Java 1.5 版本中的增强版语法 462 | 所以应该默认使用**增强版的 for** 循环. 463 | -------------------------------------------------------------------------------- /cpp/README.md: -------------------------------------------------------------------------------- 1 | - 2 | -------------------------------------------------------------------------------- /css/README.md: -------------------------------------------------------------------------------- 1 | - 2 | -------------------------------------------------------------------------------- /html/README.md: -------------------------------------------------------------------------------- 1 | - 2 | -------------------------------------------------------------------------------- /javascript/README.md: -------------------------------------------------------------------------------- 1 | # JavaScript Style Guide() { 2 | 3 | *用更合理的方式写 JavaScript* 4 | 5 | ## 目录 6 | 7 | 1. [类型](#types) 8 | 1. [对象](#objects) 9 | 1. [数组](#arrays) 10 | 1. [字符串](#strings) 11 | 1. [函数](#functions) 12 | 1. [属性](#properties) 13 | 1. [变量](#variables) 14 | 1. [提升](#hoisting) 15 | 1. [比较运算符 & 等号](#comparison-operators--equality) 16 | 1. [块](#blocks) 17 | 1. [注释](#comments) 18 | 1. [空白](#whitespace) 19 | 1. [逗号](#commas) 20 | 1. [分号](#semicolons) 21 | 1. [文件类型](#type-casting--coercion) 22 | 1. [命名规则](#naming-conventions) 23 | 1. [存取器](#accessors) 24 | 1. [构造函数](#constructors) 25 | 1. [事件](#events) 26 | 1. [模块](#modules) 27 | 1. [jQuery](#jquery) 28 | 1. [ECMAScript 5 兼容性](#ecmascript-5-compatibility) 29 | 1. [测试](#testing) 30 | 1. [性能](#performance) 31 | 1. [资源](#resources) 32 | 33 | 34 | ## 类型 35 | 36 | - **原始值**: 存取直接作用于它自身。 37 | 38 | + `string` 39 | + `number` 40 | + `boolean` 41 | + `null` 42 | + `undefined` 43 | 44 | ```javascript 45 | var foo = 1; 46 | var bar = foo; 47 | 48 | bar = 9; 49 | 50 | console.log(foo, bar); // => 1, 9 51 | ``` 52 | - **复杂类型**: 存取时作用于它自身值的引用。 53 | 54 | + `object` 55 | + `array` 56 | + `function` 57 | 58 | ```javascript 59 | var foo = [1, 2]; 60 | var bar = foo; 61 | 62 | bar[0] = 9; 63 | 64 | console.log(foo[0], bar[0]); // => 9, 9 65 | ``` 66 | 67 | **[⬆ 回到顶部](#table-of-contents)** 68 | 69 | ## 对象 70 | 71 | - 使用直接量创建对象。 72 | 73 | ```javascript 74 | // bad 75 | var item = new Object(); 76 | 77 | // good 78 | var item = {}; 79 | ``` 80 | 81 | - 不要使用[保留字](http://es5.github.io/#x7.6.1)作为键名,它们在 IE8 下不工作。[更多信息](https://github.com/airbnb/javascript/issues/61)。 82 | 83 | ```javascript 84 | // bad 85 | var superman = { 86 | default: { clark: 'kent' }, 87 | private: true 88 | }; 89 | 90 | // good 91 | var superman = { 92 | defaults: { clark: 'kent' }, 93 | hidden: true 94 | }; 95 | ``` 96 | 97 | - 使用同义词替换需要使用的保留字。 98 | 99 | ```javascript 100 | // bad 101 | var superman = { 102 | class: 'alien' 103 | }; 104 | 105 | // bad 106 | var superman = { 107 | klass: 'alien' 108 | }; 109 | 110 | // good 111 | var superman = { 112 | type: 'alien' 113 | }; 114 | ``` 115 | 116 | **[⬆ 回到顶部](#table-of-contents)** 117 | 118 | ## 数组 119 | 120 | - 使用直接量创建数组。 121 | 122 | ```javascript 123 | // bad 124 | var items = new Array(); 125 | 126 | // good 127 | var items = []; 128 | ``` 129 | 130 | - 向数组增加元素时使用 Array#push 来替代直接赋值。 131 | 132 | ```javascript 133 | var someStack = []; 134 | 135 | 136 | // bad 137 | someStack[someStack.length] = 'abracadabra'; 138 | 139 | // good 140 | someStack.push('abracadabra'); 141 | ``` 142 | 143 | - 当你需要拷贝数组时,使用 Array#slice。[jsPerf](http://jsperf.com/converting-arguments-to-an-array/7) 144 | 145 | ```javascript 146 | var len = items.length; 147 | var itemsCopy = []; 148 | var i; 149 | 150 | // bad 151 | for (i = 0; i < len; i++) { 152 | itemsCopy[i] = items[i]; 153 | } 154 | 155 | // good 156 | itemsCopy = items.slice(); 157 | ``` 158 | 159 | - 使用 Array#slice 将类数组对象转换成数组。 160 | 161 | ```javascript 162 | function trigger() { 163 | var args = Array.prototype.slice.call(arguments); 164 | ... 165 | } 166 | ``` 167 | 168 | **[⬆ 回到顶部](#table-of-contents)** 169 | 170 | 171 | ## 字符串 172 | 173 | - 使用单引号 `''` 包裹字符串。 174 | 175 | ```javascript 176 | // bad 177 | var name = "Bob Parr"; 178 | 179 | // good 180 | var name = 'Bob Parr'; 181 | 182 | // bad 183 | var fullName = "Bob " + this.lastName; 184 | 185 | // good 186 | var fullName = 'Bob ' + this.lastName; 187 | ``` 188 | 189 | - 超过 80 个字符的字符串应该使用连接符写成多行。 190 | - 注:若过度使用,通过连接符连接的长字符串可能会影响性能。[jsPerf](http://jsperf.com/ya-string-concat) & [讨论](https://github.com/airbnb/javascript/issues/40). 191 | 192 | ```javascript 193 | // bad 194 | var errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.'; 195 | 196 | // bad 197 | var errorMessage = 'This is a super long error that was thrown because \ 198 | of Batman. When you stop to think about how Batman had anything to do \ 199 | with this, you would get nowhere \ 200 | fast.'; 201 | 202 | // good 203 | var errorMessage = 'This is a super long error that was thrown because ' + 204 | 'of Batman. When you stop to think about how Batman had anything to do ' + 205 | 'with this, you would get nowhere fast.'; 206 | ``` 207 | 208 | - 程序化生成的字符串使用 Array#join 连接而不是使用连接符。尤其是 IE 下:[jsPerf](http://jsperf.com/string-vs-array-concat/2). 209 | 210 | ```javascript 211 | var items; 212 | var messages; 213 | var length; 214 | var i; 215 | 216 | messages = [{ 217 | state: 'success', 218 | message: 'This one worked.' 219 | }, { 220 | state: 'success', 221 | message: 'This one worked as well.' 222 | }, { 223 | state: 'error', 224 | message: 'This one did not work.' 225 | }]; 226 | 227 | length = messages.length; 228 | 229 | // bad 230 | function inbox(messages) { 231 | items = '
    '; 232 | 233 | for (i = 0; i < length; i++) { 234 | items += '
  • ' + messages[i].message + '
  • '; 235 | } 236 | 237 | return items + '
'; 238 | } 239 | 240 | // good 241 | function inbox(messages) { 242 | items = []; 243 | 244 | for (i = 0; i < length; i++) { 245 | // use direct assignment in this case because we're micro-optimizing. 246 | items[i] = '
  • ' + messages[i].message + '
  • '; 247 | } 248 | 249 | return '
      ' + items.join('') + '
    '; 250 | } 251 | ``` 252 | 253 | **[⬆ 回到顶部](#table-of-contents)** 254 | 255 | 256 | ## 函数 257 | 258 | - 函数表达式: 259 | 260 | ```javascript 261 | // 匿名函数表达式 262 | var anonymous = function() { 263 | return true; 264 | }; 265 | 266 | // 命名函数表达式 267 | var named = function named() { 268 | return true; 269 | }; 270 | 271 | // 立即调用的函数表达式(IIFE) 272 | (function() { 273 | console.log('Welcome to the Internet. Please follow me.'); 274 | })(); 275 | ``` 276 | 277 | - 永远不要在一个非函数代码块(if、while 等)中声明一个函数,把那个函数赋给一个变量。浏览器允许你这么做,但它们的解析表现不一致。 278 | - **注:** ECMA-262 把 `块` 定义为一组语句。函数声明不是语句。[阅读对 ECMA-262 这个问题的说明](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf#page=97)。 279 | 280 | ```javascript 281 | // bad 282 | if (currentUser) { 283 | function test() { 284 | console.log('Nope.'); 285 | } 286 | } 287 | 288 | // good 289 | var test; 290 | if (currentUser) { 291 | test = function test() { 292 | console.log('Yup.'); 293 | }; 294 | } 295 | ``` 296 | 297 | - 永远不要把参数命名为 `arguments`。这将取代函数作用域内的 `arguments` 对象。 298 | 299 | ```javascript 300 | // bad 301 | function nope(name, options, arguments) { 302 | // ...stuff... 303 | } 304 | 305 | // good 306 | function yup(name, options, args) { 307 | // ...stuff... 308 | } 309 | ``` 310 | 311 | **[⬆ 回到顶部](#table-of-contents)** 312 | 313 | 314 | 315 | ## 属性 316 | 317 | - 使用 `.` 来访问对象的属性。 318 | 319 | ```javascript 320 | var luke = { 321 | jedi: true, 322 | age: 28 323 | }; 324 | 325 | // bad 326 | var isJedi = luke['jedi']; 327 | 328 | // good 329 | var isJedi = luke.jedi; 330 | ``` 331 | 332 | - 当通过变量访问属性时使用中括号 `[]`。 333 | 334 | ```javascript 335 | var luke = { 336 | jedi: true, 337 | age: 28 338 | }; 339 | 340 | function getProp(prop) { 341 | return luke[prop]; 342 | } 343 | 344 | var isJedi = getProp('jedi'); 345 | ``` 346 | 347 | **[⬆ 回到顶部](#table-of-contents)** 348 | 349 | 350 | ## 变量 351 | 352 | - 总是使用 `var` 来声明变量。不这么做将导致产生全局变量。我们要避免污染全局命名空间。 353 | 354 | ```javascript 355 | // bad 356 | superPower = new SuperPower(); 357 | 358 | // good 359 | var superPower = new SuperPower(); 360 | ``` 361 | 362 | - 使用 `var` 声明每一个变量。 363 | 这样做的好处是增加新变量将变的更加容易,而且你永远不用再担心调换错 `;` 跟 `,`。 364 | 365 | ```javascript 366 | // bad 367 | var items = getItems(), 368 | goSportsTeam = true, 369 | dragonball = 'z'; 370 | 371 | // bad 372 | // (跟上面的代码比较一下,看看哪里错了) 373 | var items = getItems(), 374 | goSportsTeam = true; 375 | dragonball = 'z'; 376 | 377 | // good 378 | var items = getItems(); 379 | var goSportsTeam = true; 380 | var dragonball = 'z'; 381 | ``` 382 | 383 | - 最后再声明未赋值的变量。当你需要引用前面的变量赋值时这将变的很有用。 384 | 385 | ```javascript 386 | // bad 387 | var i, len, dragonball, 388 | items = getItems(), 389 | goSportsTeam = true; 390 | 391 | // bad 392 | var i; 393 | var items = getItems(); 394 | var dragonball; 395 | var goSportsTeam = true; 396 | var len; 397 | 398 | // good 399 | var items = getItems(); 400 | var goSportsTeam = true; 401 | var dragonball; 402 | var length; 403 | var i; 404 | ``` 405 | 406 | - 在作用域顶部声明变量。这将帮你避免变量声明提升相关的问题。 407 | 408 | ```javascript 409 | // bad 410 | function() { 411 | test(); 412 | console.log('doing stuff..'); 413 | 414 | //..other stuff.. 415 | 416 | var name = getName(); 417 | 418 | if (name === 'test') { 419 | return false; 420 | } 421 | 422 | return name; 423 | } 424 | 425 | // good 426 | function() { 427 | var name = getName(); 428 | 429 | test(); 430 | console.log('doing stuff..'); 431 | 432 | //..other stuff.. 433 | 434 | if (name === 'test') { 435 | return false; 436 | } 437 | 438 | return name; 439 | } 440 | 441 | // bad - 不必要的函数调用 442 | function() { 443 | var name = getName(); 444 | 445 | if (!arguments.length) { 446 | return false; 447 | } 448 | 449 | this.setFirstName(name); 450 | 451 | return true; 452 | } 453 | 454 | // good 455 | function() { 456 | var name; 457 | 458 | if (!arguments.length) { 459 | return false; 460 | } 461 | 462 | name = getName(); 463 | this.setFirstName(name); 464 | 465 | return true; 466 | } 467 | ``` 468 | 469 | **[⬆ 回到顶部](#table-of-contents)** 470 | 471 | 472 | ## 提升 473 | 474 | - 变量声明会提升至作用域顶部,但赋值不会。 475 | 476 | ```javascript 477 | // 我们知道这样不能正常工作(假设这里没有名为 notDefined 的全局变量) 478 | function example() { 479 | console.log(notDefined); // => throws a ReferenceError 480 | } 481 | 482 | // 但由于变量声明提升的原因,在一个变量引用后再创建它的变量声明将可以正常工作。 483 | // 注:变量赋值为 `true` 不会提升。 484 | function example() { 485 | console.log(declaredButNotAssigned); // => undefined 486 | var declaredButNotAssigned = true; 487 | } 488 | 489 | // 解释器会把变量声明提升到作用域顶部,意味着我们的例子将被重写成: 490 | function example() { 491 | var declaredButNotAssigned; 492 | console.log(declaredButNotAssigned); // => undefined 493 | declaredButNotAssigned = true; 494 | } 495 | ``` 496 | 497 | - 匿名函数表达式会提升它们的变量名,但不会提升函数的赋值。 498 | 499 | ```javascript 500 | function example() { 501 | console.log(anonymous); // => undefined 502 | 503 | anonymous(); // => TypeError anonymous is not a function 504 | 505 | var anonymous = function() { 506 | console.log('anonymous function expression'); 507 | }; 508 | } 509 | ``` 510 | 511 | - 命名函数表达式会提升变量名,但不会提升函数名或函数体。 512 | 513 | ```javascript 514 | function example() { 515 | console.log(named); // => undefined 516 | 517 | named(); // => TypeError named is not a function 518 | 519 | superPower(); // => ReferenceError superPower is not defined 520 | 521 | var named = function superPower() { 522 | console.log('Flying'); 523 | }; 524 | } 525 | 526 | // 当函数名跟变量名一样时,表现也是如此。 527 | function example() { 528 | console.log(named); // => undefined 529 | 530 | named(); // => TypeError named is not a function 531 | 532 | var named = function named() { 533 | console.log('named'); 534 | } 535 | } 536 | ``` 537 | 538 | - 函数声明提升它们的名字和函数体。 539 | 540 | ```javascript 541 | function example() { 542 | superPower(); // => Flying 543 | 544 | function superPower() { 545 | console.log('Flying'); 546 | } 547 | } 548 | ``` 549 | 550 | - 了解更多信息在 [JavaScript Scoping & Hoisting](http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting) by [Ben Cherry](http://www.adequatelygood.com/). 551 | 552 | **[⬆ 回到顶部](#table-of-contents)** 553 | 554 | 555 | 556 | ## 比较运算符 & 等号 557 | 558 | - 优先使用 `===` 和 `!==` 而不是 `==` 和 `!=`. 559 | - 条件表达式例如 `if` 语句通过抽象方法 `ToBoolean` 强制计算它们的表达式并且总是遵守下面的规则: 560 | 561 | + **对象** 被计算为 **true** 562 | + **Undefined** 被计算为 **false** 563 | + **Null** 被计算为 **false** 564 | + **布尔值** 被计算为 **布尔的值** 565 | + **数字** 如果是 **+0、-0 或 NaN** 被计算为 **false**,否则为 **true** 566 | + **字符串** 如果是空字符串 `''` 被计算为 **false**,否则为 **true** 567 | 568 | ```javascript 569 | if ([0]) { 570 | // true 571 | // 一个数组就是一个对象,对象被计算为 true 572 | } 573 | ``` 574 | 575 | - 使用快捷方式。 576 | 577 | ```javascript 578 | // bad 579 | if (name !== '') { 580 | // ...stuff... 581 | } 582 | 583 | // good 584 | if (name) { 585 | // ...stuff... 586 | } 587 | 588 | // bad 589 | if (collection.length > 0) { 590 | // ...stuff... 591 | } 592 | 593 | // good 594 | if (collection.length) { 595 | // ...stuff... 596 | } 597 | ``` 598 | 599 | - 了解更多信息在 [Truth Equality and JavaScript](http://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/#more-2108) by Angus Croll. 600 | 601 | **[⬆ 回到顶部](#table-of-contents)** 602 | 603 | 604 | ## 605 | 606 | - 使用大括号包裹所有的多行代码块。 607 | 608 | ```javascript 609 | // bad 610 | if (test) 611 | return false; 612 | 613 | // good 614 | if (test) return false; 615 | 616 | // good 617 | if (test) { 618 | return false; 619 | } 620 | 621 | // bad 622 | function() { return false; } 623 | 624 | // good 625 | function() { 626 | return false; 627 | } 628 | ``` 629 | 630 | - 如果通过 `if` 和 `else` 使用多行代码块,把 `else` 放在 `if` 代码块关闭括号的同一行。 631 | 632 | ```javascript 633 | // bad 634 | if (test) { 635 | thing1(); 636 | thing2(); 637 | } 638 | else { 639 | thing3(); 640 | } 641 | 642 | // good 643 | if (test) { 644 | thing1(); 645 | thing2(); 646 | } else { 647 | thing3(); 648 | } 649 | ``` 650 | 651 | 652 | **[⬆ 回到顶部](#table-of-contents)** 653 | 654 | 655 | ## 注释 656 | 657 | - 使用 `/** ... */` 作为多行注释。包含描述、指定所有参数和返回值的类型和值。 658 | 659 | ```javascript 660 | // bad 661 | // make() returns a new element 662 | // based on the passed in tag name 663 | // 664 | // @param {String} tag 665 | // @return {Element} element 666 | function make(tag) { 667 | 668 | // ...stuff... 669 | 670 | return element; 671 | } 672 | 673 | // good 674 | /** 675 | * make() returns a new element 676 | * based on the passed in tag name 677 | * 678 | * @param {String} tag 679 | * @return {Element} element 680 | */ 681 | function make(tag) { 682 | 683 | // ...stuff... 684 | 685 | return element; 686 | } 687 | ``` 688 | 689 | - 使用 `//` 作为单行注释。在评论对象上面另起一行使用单行注释。在注释前插入空行。 690 | 691 | ```javascript 692 | // bad 693 | var active = true; // is current tab 694 | 695 | // good 696 | // is current tab 697 | var active = true; 698 | 699 | // bad 700 | function getType() { 701 | console.log('fetching type...'); 702 | // set the default type to 'no type' 703 | var type = this._type || 'no type'; 704 | 705 | return type; 706 | } 707 | 708 | // good 709 | function getType() { 710 | console.log('fetching type...'); 711 | 712 | // set the default type to 'no type' 713 | var type = this._type || 'no type'; 714 | 715 | return type; 716 | } 717 | ``` 718 | 719 | - 给注释增加 `FIXME` 或 `TODO` 的前缀可以帮助其他开发者快速了解这是一个需要复查的问题,或是给需要实现的功能提供一个解决方式。这将有别于常见的注释,因为它们是可操作的。使用 `FIXME -- need to figure this out` 或者 `TODO -- need to implement`。 720 | 721 | - 使用 `// FIXME:` 标注问题。 722 | 723 | ```javascript 724 | function Calculator() { 725 | 726 | // FIXME: shouldn't use a global here 727 | total = 0; 728 | 729 | return this; 730 | } 731 | ``` 732 | 733 | - 使用 `// TODO:` 标注问题的解决方式。 734 | 735 | ```javascript 736 | function Calculator() { 737 | 738 | // TODO: total should be configurable by an options param 739 | this.total = 0; 740 | 741 | return this; 742 | } 743 | ``` 744 | 745 | **[⬆ 回到顶部](#table-of-contents)** 746 | 747 | 748 | ## 空白 749 | 750 | - 使用 2 个空格作为缩进。 751 | 752 | ```javascript 753 | // bad 754 | function() { 755 | ∙∙∙∙var name; 756 | } 757 | 758 | // bad 759 | function() { 760 | ∙var name; 761 | } 762 | 763 | // good 764 | function() { 765 | ∙∙var name; 766 | } 767 | ``` 768 | 769 | - 在大括号前放一个空格。 770 | 771 | ```javascript 772 | // bad 773 | function test(){ 774 | console.log('test'); 775 | } 776 | 777 | // good 778 | function test() { 779 | console.log('test'); 780 | } 781 | 782 | // bad 783 | dog.set('attr',{ 784 | age: '1 year', 785 | breed: 'Bernese Mountain Dog' 786 | }); 787 | 788 | // good 789 | dog.set('attr', { 790 | age: '1 year', 791 | breed: 'Bernese Mountain Dog' 792 | }); 793 | ``` 794 | 795 | - 在控制语句(`if`、`while` 等)的小括号前放一个空格。在函数调用及声明中,不在函数的参数列表前加空格。 796 | 797 | ```javascript 798 | // bad 799 | if(isJedi) { 800 | fight (); 801 | } 802 | 803 | // good 804 | if (isJedi) { 805 | fight(); 806 | } 807 | 808 | // bad 809 | function fight () { 810 | console.log ('Swooosh!'); 811 | } 812 | 813 | // good 814 | function fight() { 815 | console.log('Swooosh!'); 816 | } 817 | ``` 818 | 819 | - 使用空格把运算符隔开。 820 | 821 | ```javascript 822 | // bad 823 | var x=y+5; 824 | 825 | // good 826 | var x = y + 5; 827 | ``` 828 | 829 | - 在文件末尾插入一个空行。 830 | 831 | ```javascript 832 | // bad 833 | (function(global) { 834 | // ...stuff... 835 | })(this); 836 | ``` 837 | 838 | ```javascript 839 | // bad 840 | (function(global) { 841 | // ...stuff... 842 | })(this);↵ 843 | ↵ 844 | ``` 845 | 846 | ```javascript 847 | // good 848 | (function(global) { 849 | // ...stuff... 850 | })(this);↵ 851 | ``` 852 | 853 | - 在使用长方法链时进行缩进。使用前面的点 `.` 强调这是方法调用而不是新语句。 854 | 855 | ```javascript 856 | // bad 857 | $('#items').find('.selected').highlight().end().find('.open').updateCount(); 858 | 859 | // bad 860 | $('#items'). 861 | find('.selected'). 862 | highlight(). 863 | end(). 864 | find('.open'). 865 | updateCount(); 866 | 867 | // good 868 | $('#items') 869 | .find('.selected') 870 | .highlight() 871 | .end() 872 | .find('.open') 873 | .updateCount(); 874 | 875 | // bad 876 | var leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led', true) 877 | .attr('width', (radius + margin) * 2).append('svg:g') 878 | .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') 879 | .call(tron.led); 880 | 881 | // good 882 | var leds = stage.selectAll('.led') 883 | .data(data) 884 | .enter().append('svg:svg') 885 | .classed('led', true) 886 | .attr('width', (radius + margin) * 2) 887 | .append('svg:g') 888 | .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') 889 | .call(tron.led); 890 | ``` 891 | 892 | - 在块末和新语句前插入空行。 893 | 894 | ```javascript 895 | // bad 896 | if (foo) { 897 | return bar; 898 | } 899 | return baz; 900 | 901 | // good 902 | if (foo) { 903 | return bar; 904 | } 905 | 906 | return baz; 907 | 908 | // bad 909 | var obj = { 910 | foo: function() { 911 | }, 912 | bar: function() { 913 | } 914 | }; 915 | return obj; 916 | 917 | // good 918 | var obj = { 919 | foo: function() { 920 | }, 921 | 922 | bar: function() { 923 | } 924 | }; 925 | 926 | return obj; 927 | ``` 928 | 929 | 930 | **[⬆ 回到顶部](#table-of-contents)** 931 | 932 | ## 逗号 933 | 934 | - 行首逗号: **不需要**。 935 | 936 | ```javascript 937 | // bad 938 | var story = [ 939 | once 940 | , upon 941 | , aTime 942 | ]; 943 | 944 | // good 945 | var story = [ 946 | once, 947 | upon, 948 | aTime 949 | ]; 950 | 951 | // bad 952 | var hero = { 953 | firstName: 'Bob' 954 | , lastName: 'Parr' 955 | , heroName: 'Mr. Incredible' 956 | , superPower: 'strength' 957 | }; 958 | 959 | // good 960 | var hero = { 961 | firstName: 'Bob', 962 | lastName: 'Parr', 963 | heroName: 'Mr. Incredible', 964 | superPower: 'strength' 965 | }; 966 | ``` 967 | 968 | - 额外的行末逗号:**不需要**。这样做会在 IE6/7 和 IE9 怪异模式下引起问题。同样,多余的逗号在某些 ES3 的实现里会增加数组的长度。在 ES5 中已经澄清了 ([source](http://es5.github.io/#D)): 969 | 970 | > Edition 5 clarifies the fact that a trailing comma at the end of an ArrayInitialiser does not add to the length of the array. This is not a semantic change from Edition 3 but some implementations may have previously misinterpreted this. 971 | 972 | ```javascript 973 | // bad 974 | var hero = { 975 | firstName: 'Kevin', 976 | lastName: 'Flynn', 977 | }; 978 | 979 | var heroes = [ 980 | 'Batman', 981 | 'Superman', 982 | ]; 983 | 984 | // good 985 | var hero = { 986 | firstName: 'Kevin', 987 | lastName: 'Flynn' 988 | }; 989 | 990 | var heroes = [ 991 | 'Batman', 992 | 'Superman' 993 | ]; 994 | ``` 995 | 996 | **[⬆ 回到顶部](#table-of-contents)** 997 | 998 | 999 | ## 分号 1000 | 1001 | - **使用分号。** 1002 | 1003 | ```javascript 1004 | // bad 1005 | (function() { 1006 | var name = 'Skywalker' 1007 | return name 1008 | })() 1009 | 1010 | // good 1011 | (function() { 1012 | var name = 'Skywalker'; 1013 | return name; 1014 | })(); 1015 | 1016 | // good (防止函数在两个 IIFE 合并时被当成一个参数 1017 | ;(function() { 1018 | var name = 'Skywalker'; 1019 | return name; 1020 | })(); 1021 | ``` 1022 | 1023 | [了解更多](http://stackoverflow.com/a/7365214/1712802). 1024 | 1025 | **[⬆ 回到顶部](#table-of-contents)** 1026 | 1027 | 1028 | ## 类型转换 1029 | 1030 | - 在语句开始时执行类型转换。 1031 | - 字符串: 1032 | 1033 | ```javascript 1034 | // => this.reviewScore = 9; 1035 | 1036 | // bad 1037 | var totalScore = this.reviewScore + ''; 1038 | 1039 | // good 1040 | var totalScore = '' + this.reviewScore; 1041 | 1042 | // bad 1043 | var totalScore = '' + this.reviewScore + ' total score'; 1044 | 1045 | // good 1046 | var totalScore = this.reviewScore + ' total score'; 1047 | ``` 1048 | 1049 | - 使用 `parseInt` 转换数字时总是带上类型转换的基数。 1050 | 1051 | ```javascript 1052 | var inputValue = '4'; 1053 | 1054 | // bad 1055 | var val = new Number(inputValue); 1056 | 1057 | // bad 1058 | var val = +inputValue; 1059 | 1060 | // bad 1061 | var val = inputValue >> 0; 1062 | 1063 | // bad 1064 | var val = parseInt(inputValue); 1065 | 1066 | // good 1067 | var val = Number(inputValue); 1068 | 1069 | // good 1070 | var val = parseInt(inputValue, 10); 1071 | ``` 1072 | 1073 | - 如果因为某些原因 `parseInt` 成为你所做的事的瓶颈而需要使用位操作解决[性能问题](http://jsperf.com/coercion-vs-casting/3)时,留个注释说清楚原因和你的目的。 1074 | 1075 | ```javascript 1076 | // good 1077 | /** 1078 | * parseInt was the reason my code was slow. 1079 | * Bitshifting the String to coerce it to a 1080 | * Number made it a lot faster. 1081 | */ 1082 | var val = inputValue >> 0; 1083 | ``` 1084 | 1085 | - **注:** 小心使用位操作运算符。数字会被当成 [64 位值](http://es5.github.io/#x4.3.19),但是位操作运算符总是返回 32 位的整数([source](http://es5.github.io/#x11.7))。位操作处理大于 32 位的整数值时还会导致意料之外的行为。[讨论](https://github.com/airbnb/javascript/issues/109)。最大的 32 位整数是 2,147,483,647: 1086 | 1087 | ```javascript 1088 | 2147483647 >> 0 //=> 2147483647 1089 | 2147483648 >> 0 //=> -2147483648 1090 | 2147483649 >> 0 //=> -2147483647 1091 | ``` 1092 | 1093 | - 布尔: 1094 | 1095 | ```javascript 1096 | var age = 0; 1097 | 1098 | // bad 1099 | var hasAge = new Boolean(age); 1100 | 1101 | // good 1102 | var hasAge = Boolean(age); 1103 | 1104 | // good 1105 | var hasAge = !!age; 1106 | ``` 1107 | 1108 | **[⬆ 回到顶部](#table-of-contents)** 1109 | 1110 | 1111 | ## 命名规则 1112 | 1113 | - 避免单字母命名。命名应具备描述性。 1114 | 1115 | ```javascript 1116 | // bad 1117 | function q() { 1118 | // ...stuff... 1119 | } 1120 | 1121 | // good 1122 | function query() { 1123 | // ..stuff.. 1124 | } 1125 | ``` 1126 | 1127 | - 使用驼峰式命名对象、函数和实例。 1128 | 1129 | ```javascript 1130 | // bad 1131 | var OBJEcttsssss = {}; 1132 | var this_is_my_object = {}; 1133 | var o = {}; 1134 | function c() {} 1135 | 1136 | // good 1137 | var thisIsMyObject = {}; 1138 | function thisIsMyFunction() {} 1139 | ``` 1140 | 1141 | - 使用帕斯卡式命名构造函数或类。 1142 | 1143 | ```javascript 1144 | // bad 1145 | function user(options) { 1146 | this.name = options.name; 1147 | } 1148 | 1149 | var bad = new user({ 1150 | name: 'nope' 1151 | }); 1152 | 1153 | // good 1154 | function User(options) { 1155 | this.name = options.name; 1156 | } 1157 | 1158 | var good = new User({ 1159 | name: 'yup' 1160 | }); 1161 | ``` 1162 | 1163 | - 使用下划线 `_` 开头命名私有属性。 1164 | 1165 | ```javascript 1166 | // bad 1167 | this.__firstName__ = 'Panda'; 1168 | this.firstName_ = 'Panda'; 1169 | 1170 | // good 1171 | this._firstName = 'Panda'; 1172 | ``` 1173 | 1174 | - 使用 `_this` 保存 `this` 的引用。 1175 | 1176 | ```javascript 1177 | // bad 1178 | function() { 1179 | var self = this; 1180 | return function() { 1181 | console.log(self); 1182 | }; 1183 | } 1184 | 1185 | // bad 1186 | function() { 1187 | var that = this; 1188 | return function() { 1189 | console.log(that); 1190 | }; 1191 | } 1192 | 1193 | // good 1194 | function() { 1195 | var _this = this; 1196 | return function() { 1197 | console.log(_this); 1198 | }; 1199 | } 1200 | ``` 1201 | 1202 | - 给函数命名。这在做堆栈轨迹时很有帮助。 1203 | 1204 | ```javascript 1205 | // bad 1206 | var log = function(msg) { 1207 | console.log(msg); 1208 | }; 1209 | 1210 | // good 1211 | var log = function log(msg) { 1212 | console.log(msg); 1213 | }; 1214 | ``` 1215 | 1216 | - **注:** IE8 及以下版本对命名函数表达式的处理有些怪异。了解更多信息到 [http://kangax.github.io/nfe/](http://kangax.github.io/nfe/)。 1217 | 1218 | - 如果你的文件导出一个类,你的文件名应该与类名完全相同。 1219 | ```javascript 1220 | // file contents 1221 | class CheckBox { 1222 | // ... 1223 | } 1224 | module.exports = CheckBox; 1225 | 1226 | // in some other file 1227 | // bad 1228 | var CheckBox = require('./checkBox'); 1229 | 1230 | // bad 1231 | var CheckBox = require('./check_box'); 1232 | 1233 | // good 1234 | var CheckBox = require('./CheckBox'); 1235 | ``` 1236 | 1237 | **[⬆ 回到顶部](#table-of-contents)** 1238 | 1239 | 1240 | ## 存取器 1241 | 1242 | - 属性的存取函数不是必须的。 1243 | - 如果你需要存取函数时使用 `getVal()` 和 `setVal('hello')`。 1244 | 1245 | ```javascript 1246 | // bad 1247 | dragon.age(); 1248 | 1249 | // good 1250 | dragon.getAge(); 1251 | 1252 | // bad 1253 | dragon.age(25); 1254 | 1255 | // good 1256 | dragon.setAge(25); 1257 | ``` 1258 | 1259 | - 如果属性是布尔值,使用 `isVal()` 或 `hasVal()`。 1260 | 1261 | ```javascript 1262 | // bad 1263 | if (!dragon.age()) { 1264 | return false; 1265 | } 1266 | 1267 | // good 1268 | if (!dragon.hasAge()) { 1269 | return false; 1270 | } 1271 | ``` 1272 | 1273 | - 创建 get() 和 set() 函数是可以的,但要保持一致。 1274 | 1275 | ```javascript 1276 | function Jedi(options) { 1277 | options || (options = {}); 1278 | var lightsaber = options.lightsaber || 'blue'; 1279 | this.set('lightsaber', lightsaber); 1280 | } 1281 | 1282 | Jedi.prototype.set = function(key, val) { 1283 | this[key] = val; 1284 | }; 1285 | 1286 | Jedi.prototype.get = function(key) { 1287 | return this[key]; 1288 | }; 1289 | ``` 1290 | 1291 | **[⬆ 回到顶部](#table-of-contents)** 1292 | 1293 | 1294 | ## 构造函数 1295 | 1296 | - 给对象原型分配方法,而不是使用一个新对象覆盖原型。覆盖原型将导致继承出现问题:重设原型将覆盖原有原型! 1297 | 1298 | ```javascript 1299 | function Jedi() { 1300 | console.log('new jedi'); 1301 | } 1302 | 1303 | // bad 1304 | Jedi.prototype = { 1305 | fight: function fight() { 1306 | console.log('fighting'); 1307 | }, 1308 | 1309 | block: function block() { 1310 | console.log('blocking'); 1311 | } 1312 | }; 1313 | 1314 | // good 1315 | Jedi.prototype.fight = function fight() { 1316 | console.log('fighting'); 1317 | }; 1318 | 1319 | Jedi.prototype.block = function block() { 1320 | console.log('blocking'); 1321 | }; 1322 | ``` 1323 | 1324 | - 方法可以返回 `this` 来实现方法链式使用。 1325 | 1326 | ```javascript 1327 | // bad 1328 | Jedi.prototype.jump = function() { 1329 | this.jumping = true; 1330 | return true; 1331 | }; 1332 | 1333 | Jedi.prototype.setHeight = function(height) { 1334 | this.height = height; 1335 | }; 1336 | 1337 | var luke = new Jedi(); 1338 | luke.jump(); // => true 1339 | luke.setHeight(20); // => undefined 1340 | 1341 | // good 1342 | Jedi.prototype.jump = function() { 1343 | this.jumping = true; 1344 | return this; 1345 | }; 1346 | 1347 | Jedi.prototype.setHeight = function(height) { 1348 | this.height = height; 1349 | return this; 1350 | }; 1351 | 1352 | var luke = new Jedi(); 1353 | 1354 | luke.jump() 1355 | .setHeight(20); 1356 | ``` 1357 | 1358 | 1359 | - 写一个自定义的 `toString()` 方法是可以的,但是确保它可以正常工作且不会产生副作用。 1360 | 1361 | ```javascript 1362 | function Jedi(options) { 1363 | options || (options = {}); 1364 | this.name = options.name || 'no name'; 1365 | } 1366 | 1367 | Jedi.prototype.getName = function getName() { 1368 | return this.name; 1369 | }; 1370 | 1371 | Jedi.prototype.toString = function toString() { 1372 | return 'Jedi - ' + this.getName(); 1373 | }; 1374 | ``` 1375 | 1376 | **[⬆ 回到顶部](#table-of-contents)** 1377 | 1378 | 1379 | ## 事件 1380 | 1381 | - 当给时间附加数据时(无论是 DOM 事件还是私有事件),传入一个哈希而不是原始值。这样可以让后面的贡献者增加更多数据到事件数据而无需找出并更新事件的每一个处理器。例如,不好的写法: 1382 | 1383 | ```js 1384 | // bad 1385 | $(this).trigger('listingUpdated', listing.id); 1386 | 1387 | ... 1388 | 1389 | $(this).on('listingUpdated', function(e, listingId) { 1390 | // do something with listingId 1391 | }); 1392 | ``` 1393 | 1394 | 更好的写法: 1395 | 1396 | ```js 1397 | // good 1398 | $(this).trigger('listingUpdated', { listingId : listing.id }); 1399 | 1400 | ... 1401 | 1402 | $(this).on('listingUpdated', function(e, data) { 1403 | // do something with data.listingId 1404 | }); 1405 | ``` 1406 | 1407 | **[⬆ 回到顶部](#table-of-contents)** 1408 | 1409 | 1410 | ## 模块 1411 | 1412 | - 模块应该以 `!` 开始。这样确保了当一个不好的模块忘记包含最后的分号时,在合并代码到生产环境后不会产生错误。[详细说明](https://github.com/airbnb/javascript/issues/44#issuecomment-13063933) 1413 | - 文件应该以驼峰式命名,并放在同名的文件夹里,且与导出的名字一致。 1414 | - 增加一个名为 `noConflict()` 的方法来设置导出的模块为前一个版本并返回它。 1415 | - 永远在模块顶部声明 `'use strict';`。 1416 | 1417 | ```javascript 1418 | // fancyInput/fancyInput.js 1419 | 1420 | !function(global) { 1421 | 'use strict'; 1422 | 1423 | var previousFancyInput = global.FancyInput; 1424 | 1425 | function FancyInput(options) { 1426 | this.options = options || {}; 1427 | } 1428 | 1429 | FancyInput.noConflict = function noConflict() { 1430 | global.FancyInput = previousFancyInput; 1431 | return FancyInput; 1432 | }; 1433 | 1434 | global.FancyInput = FancyInput; 1435 | }(this); 1436 | ``` 1437 | 1438 | **[⬆ 回到顶部](#table-of-contents)** 1439 | 1440 | 1441 | ## jQuery 1442 | 1443 | - 使用 `$` 作为存储 jQuery 对象的变量名前缀。 1444 | 1445 | ```javascript 1446 | // bad 1447 | var sidebar = $('.sidebar'); 1448 | 1449 | // good 1450 | var $sidebar = $('.sidebar'); 1451 | ``` 1452 | 1453 | - 缓存 jQuery 查询。 1454 | 1455 | ```javascript 1456 | // bad 1457 | function setSidebar() { 1458 | $('.sidebar').hide(); 1459 | 1460 | // ...stuff... 1461 | 1462 | $('.sidebar').css({ 1463 | 'background-color': 'pink' 1464 | }); 1465 | } 1466 | 1467 | // good 1468 | function setSidebar() { 1469 | var $sidebar = $('.sidebar'); 1470 | $sidebar.hide(); 1471 | 1472 | // ...stuff... 1473 | 1474 | $sidebar.css({ 1475 | 'background-color': 'pink' 1476 | }); 1477 | } 1478 | ``` 1479 | 1480 | - 对 DOM 查询使用层叠 `$('.sidebar ul')` 或 父元素 > 子元素 `$('.sidebar > ul')`。 [jsPerf](http://jsperf.com/jquery-find-vs-context-sel/16) 1481 | - 对有作用域的 jQuery 对象查询使用 `find`。 1482 | 1483 | ```javascript 1484 | // bad 1485 | $('ul', '.sidebar').hide(); 1486 | 1487 | // bad 1488 | $('.sidebar').find('ul').hide(); 1489 | 1490 | // good 1491 | $('.sidebar ul').hide(); 1492 | 1493 | // good 1494 | $('.sidebar > ul').hide(); 1495 | 1496 | // good 1497 | $sidebar.find('ul').hide(); 1498 | ``` 1499 | 1500 | **[⬆ 回到顶部](#table-of-contents)** 1501 | 1502 | 1503 | ## ECMAScript 5 兼容性 1504 | 1505 | - 参考 [Kangax](https://twitter.com/kangax/) 的 ES5 [兼容表](http://kangax.github.com/es5-compat-table/). 1506 | 1507 | **[⬆ 回到顶部](#table-of-contents)** 1508 | 1509 | 1510 | ## 测试 1511 | 1512 | - **Yup.** 1513 | 1514 | ```javascript 1515 | function() { 1516 | return true; 1517 | } 1518 | ``` 1519 | 1520 | **[⬆ 回到顶部](#table-of-contents)** 1521 | 1522 | 1523 | ## 性能 1524 | 1525 | - [On Layout & Web Performance](http://kellegous.com/j/2013/01/26/layout-performance/) 1526 | - [String vs Array Concat](http://jsperf.com/string-vs-array-concat/2) 1527 | - [Try/Catch Cost In a Loop](http://jsperf.com/try-catch-in-loop-cost) 1528 | - [Bang Function](http://jsperf.com/bang-function) 1529 | - [jQuery Find vs Context, Selector](http://jsperf.com/jquery-find-vs-context-sel/13) 1530 | - [innerHTML vs textContent for script text](http://jsperf.com/innerhtml-vs-textcontent-for-script-text) 1531 | - [Long String Concatenation](http://jsperf.com/ya-string-concat) 1532 | - Loading... 1533 | 1534 | **[⬆ 回到顶部](#table-of-contents)** 1535 | 1536 | 1537 | ## 资源 1538 | 1539 | 1540 | **工具** 1541 | 1542 | - Code Style Linters 1543 | + [JSHint](http://www.jshint.com/) - [Airbnb Style .jshintrc](https://github.com/airbnb/javascript/blob/master/linters/jshintrc) 1544 | + [JSCS](https://github.com/jscs-dev/node-jscs) - [Airbnb Style Preset](https://github.com/jscs-dev/node-jscs/blob/master/presets/airbnb.json) 1545 | 1546 | **其它风格指南** 1547 | 1548 | - [Google JavaScript Style Guide](http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml) 1549 | - [jQuery Core Style Guidelines](http://docs.jquery.com/JQuery_Core_Style_Guidelines) 1550 | - [Principles of Writing Consistent, Idiomatic JavaScript](https://github.com/rwldrn/idiomatic.js/) 1551 | - [JavaScript Standard Style](https://github.com/feross/standard) 1552 | 1553 | **其它风格** 1554 | 1555 | - [Naming this in nested functions](https://gist.github.com/4135065) - Christian Johansen 1556 | - [Conditional Callbacks](https://github.com/airbnb/javascript/issues/52) - Ross Allen 1557 | - [Popular JavaScript Coding Conventions on Github](http://sideeffect.kr/popularconvention/#javascript) - JeongHoon Byun 1558 | - [Multiple var statements in JavaScript, not superfluous](http://benalman.com/news/2012/05/multiple-var-statements-javascript/) - Ben Alman 1559 | 1560 | **[⬆ 回到顶部](#table-of-contents)** -------------------------------------------------------------------------------- /objective-c/README.md: -------------------------------------------------------------------------------- 1 | # Objective-C-Coding-Style 2 | ## 目录 3 | 4 | * [代码组织](#code-organization) 5 | * [空格](#spacing) 6 | * [注释](#comments) 7 | * [命名](#naming) 8 | * [下划线](#underscores) 9 | * [方法](#methods) 10 | * [方法声明与定义](#methoddef) 11 | * [方法调用](#methodscall) 12 | * [@public和@private标记符](#public&private) 13 | * [协议](#Protocols) 14 | * [闭包](#Blocks) 15 | * [变量](#variables) 16 | * [属性特性](#property-attributes) 17 | * [点符号语法](#dot-notation-syntax) 18 | * [字面值](#literals) 19 | * [常量](#constants) 20 | * [枚举类型](#enumerated-types) 21 | * [Case语句](#case-statements) 22 | * [私有属性](#private-properties) 23 | * [布尔值](#booleans) 24 | * [条件语句](#conditionals) 25 | * [三元操作符](#ternary-operator) 26 | * [Init方法](#init-methods) 27 | * [类构造方法](#class-constructor-methods) 28 | * [CGRect函数](#cgrect-functions) 29 | * [黄金路径](#golden-path) 30 | * [错误处理](#error-handling) 31 | * [单例模式](#singletons) 32 | * [换行符](#line-breaks) 33 | * [Xcode工程](#xcode-project) 34 | 35 | 36 | ## 代码组织 37 | 38 | 在函数分组和protocol/delegate实现中使用`#pragma mark -`来分类方法,要遵循以下一般结构: 39 | 40 | ```objc 41 | #pragma mark - Lifecycle 42 | - (instancetype)init {} 43 | - (void)dealloc {} 44 | - (void)viewDidLoad {} 45 | - (void)viewWillAppear:(BOOL)animated {} 46 | - (void)didReceiveMemoryWarning {} 47 | 48 | #pragma mark - Custom Accessors 49 | - (void)setCustomProperty:(id)value {} 50 | - (id)customProperty {} 51 | 52 | #pragma mark - Action 53 | - (IBAction)submitData:(id)sender {} 54 | - (void)someButtonDidPressed:(UIButton*)button 55 | 56 | #pragma mark - Protocol conformance 57 | #pragma mark - UITextFieldDelegate 58 | #pragma mark - UITableViewDataSource 59 | #pragma mark - UITableViewDelegate 60 | 61 | #pragma mark - Public 62 | - (void)publicMethod {} 63 | 64 | #pragma mark - Private 65 | - (void)privateMethod {} 66 | 67 | #pragma mark - NSCopying 68 | - (id)copyWithZone:(NSZone *)zone {} 69 | 70 | #pragma mark - NSObject 71 | - (NSString *)description {} 72 | 73 | ``` 74 | 75 | 76 | ## 空格 77 | 78 | * 缩进使用**4**个空格,确保在Xcode偏好设置来设置。 79 | * 方法大括号和其他大括号(`if`/`else`/`switch`/`while` 等。)总是在同一行语句打开但在新行中关闭。 80 | 81 | **应该:** 82 | 83 | ```objc 84 | if (user.isHappy) { 85 | // Do something 86 | } else { 87 | // Do something else 88 | } 89 | ``` 90 | 91 | **不应该:** 92 | 93 | ```objc 94 | if (user.isHappy) 95 | { 96 | // Do something 97 | } 98 | else { 99 | // Do something else 100 | } 101 | ``` 102 | 103 | * 在方法之间应该有且只有一行,这样有利于在视觉上更清晰和更易于组织。在方法内的空白应该分离功能,但通常都抽离出来成为一个新方法。 104 | * 优先使用auto-synthesis。但如果有必要,`@synthesize` 和 `@dynamic`应该在实现中每个都声明新的一行。 105 | 106 | 107 | ## 注释 108 | 109 | 当需要注释时,注释应该用来解释这段特殊代码**为什么**要这样做。任何被使用的注释都必须保持最新或被删除。 110 | 111 | 一般都避免使用块注释,因为代码尽可能做到自解释,只有当断断续续或几行代码时才需要注释。*例外:这不应用在生成文档的注释* 112 | 113 | 双斜线开头的注释,双斜线要与后面的文字间留有一个空格。 114 | 115 | 116 | ## 命名 117 | 118 | Apple命名规则尽可能坚持,特别是与这些相关的[memory management rules](https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/MemoryMgmt.html) ([NARC](http://stackoverflow.com/a/2865194/340508))。 119 | 120 | 长的,描述性的方法和变量命名是好的。 121 | 122 | **应该:** 123 | 124 | ```objc 125 | UIButton *settingsButton; 126 | ``` 127 | 128 | **不应该:** 129 | 130 | ```objc 131 | UIButton *setBut; 132 | ``` 133 | 134 | 三个字符前缀应该经常用在类和常量命名,但在Core Data的实体名中应被忽略。对于官方的raywenderlich.com书、初学者工具包或教程,前缀'RWT'应该被使用。 135 | 136 | 常量应该使用驼峰式命名规则,所有的单词首字母大写和加上与类名有关的前缀。 137 | 138 | **应该:** 139 | 140 | ```objc 141 | static NSTimeInterval const RWTTutorialViewControllerNavigationFadeAnimationDuration = 0.3; 142 | ``` 143 | 144 | **不应该:** 145 | 146 | ```objc 147 | static NSTimeInterval const fadetime = 1.7; 148 | ``` 149 | 150 | 属性也是使用驼峰式,但首单词的首字母小写。对属性使用auto-synthesis,而不是手动编写@ synthesize语句,除非你有一个好的理由。 151 | 152 | **应该:** 153 | 154 | ```objc 155 | @property (strong, nonatomic) NSString *descriptiveVariableName; 156 | ``` 157 | 158 | **不应该:** 159 | 160 | ```objc 161 | id varnm; 162 | ``` 163 | 164 | 165 | ### 下划线 166 | 167 | 当使用属性时,实例变量应该使用`self.`来访问和改变。这就意味着所有属性将会视觉效果不同,因为它们前面都有`self.`。 168 | 169 | 但有一个特例:在初始化方法里,实例变量(例如,_variableName)应该直接被使用来避免getters/setters潜在的副作用。 170 | 171 | 局部变量不应该包含下划线。 172 | 173 | 174 | ## 方法 175 | 176 | 177 | ### 方法声明与定义 178 | 179 | "and"这个词的用法应该保留。它不应该用于多个参数来说明,就像`initWithWidth:height`以下这个例子: 180 | 181 | **应该:** 182 | ```objc 183 | - (void)setExampleText:(NSString *)text image:(UIImage *)image; 184 | - (void)sendAction:(SEL)aSelector to:(id)anObject forAllCells:(BOOL)flag; 185 | - (id)viewWithTag:(NSInteger)tag; 186 | - (instancetype)initWithWidth:(CGFloat)width height:(CGFloat)height; 187 | ``` 188 | 189 | **不应该:** 190 | 191 | ```objc 192 | - (void)setT:(NSString *)text i:(UIImage *)image; 193 | - (void)sendAction:(SEL)aSelector :(id)anObject :(BOOL)flag; 194 | - (id)taggedView:(NSInteger)tag; 195 | - (instancetype)initWithWidth:(CGFloat)width andHeight:(CGFloat)height; 196 | - (instancetype)initWith:(int)width and:(int)height; // Never do this. 197 | ``` 198 | 199 | 一个典型的Objective-C函数应该是这样的: 200 | 201 | ```objective-c 202 | - (void)writeVideoFrameWithData:(NSData *)frameData timeStamp:(int)timeStamp { 203 | ... 204 | } 205 | ``` 206 | 207 | 在`-`和`(void)`之间应该有一个空格,第一个大括号`{`的位置在函数所在行的末尾,同样应该有一个空格。 208 | 209 | 如果一个函数有特别多的参数或者名称很长,应该将其按照`:`来对齐分行显示: 210 | 211 | ```objective-c 212 | -(id)initWithModel:(IPCModle)model 213 | ConnectType:(IPCConnectType)connectType 214 | Resolution:(IPCResolution)resolution 215 | AuthName:(NSString *)authName 216 | Password:(NSString *)password 217 | MAC:(NSString *)mac 218 | AzIp:(NSString *)az_ip 219 | AzDns:(NSString *)az_dns 220 | Token:(NSString *)token 221 | Email:(NSString *)email 222 | Delegate:(id)delegate; 223 | ``` 224 | 225 | 在分行时,如果第一段名称过短,后续名称可以以Tab的长度(4个空格)为单位进行缩进: 226 | 227 | ```objective-c 228 | - (void)short:(GTMFoo *)theFoo 229 | longKeyword:(NSRect)theRect 230 | evenLongerKeyword:(float)theInterval 231 | error:(NSError **)theError { 232 | ... 233 | } 234 | ``` 235 | 236 | 237 | ### 方法调用 238 | 239 | 方法调用的格式可以按照函数的长短来选择写在一行或者分成多行: 240 | 241 | ```objective-c 242 | // 写在一行 243 | [myObject doFooWith:arg1 name:arg2 error:arg3]; 244 | 245 | // 分行写,按照':'对齐 246 | [myObject doFooWith:arg1 247 | name:arg2 248 | error:arg3]; 249 | 250 | // 第一段名称过短的话后续可以进行缩进 251 | [myObj short:arg1 252 | longKeyword:arg2 253 | evenLongerKeyword:arg3 254 | error:arg4]; 255 | ``` 256 | 257 | 以下写法是错误的: 258 | 259 | ```objective-c 260 | // 错误,要么写在一行,要么全部分行 261 | [myObject doFooWith:arg1 name:arg2 262 | error:arg3]; 263 | [myObject doFooWith:arg1 264 | name:arg2 error:arg3]; 265 | 266 | // 错误,按照':'来对齐,而不是关键字 267 | [myObject doFooWith:arg1 268 | name:arg2 269 | error:arg3]; 270 | ``` 271 | 272 | 273 | ## @public和@private标记符 274 | 275 | @public和@private标记符应该以**两个空格**来进行缩进: 276 | 277 | ```objective-c 278 | @interface MyClass : NSObject { 279 | @public 280 | ... 281 | @private 282 | ... 283 | } 284 | @end 285 | ``` 286 | 287 | 288 | ## 协议(Protocols) 289 | 290 | 在书写协议的时候注意用`<>`括起来的协议和类型名之间是没有空格的,比如`IPCConnectHandler()`,这个规则适用所有书写协议的地方,包括函数声明、类声明、实例变量等等: 291 | 292 | ```objective-c 293 | @interface MyProtocoledClass : NSObject { 294 | @private 295 | id _delegate; 296 | } 297 | 298 | - (void)setDelegate:(id)aDelegate; 299 | @end 300 | ``` 301 | 302 | 303 | ###闭包(Blocks) 304 | 305 | 根据block的长度,有不同的书写规则: 306 | 307 | - 较短的block可以写在一行内。 308 | - 如果分行显示的话,block的右括号`}`应该和调用block那行代码的第一个非空字符对齐。 309 | - block内的代码采用**4个空格**的缩进。 310 | - 如果block过于庞大,应该单独声明成一个变量来使用。 311 | - `^`和`(`之间,`^`和`{`之间都没有空格,参数列表的右括号`)`和`{`之间有一个空格。 312 | 313 | ```objective-c 314 | // 较短的block写在一行内 315 | [operation setCompletionBlock:^{ [self onOperationDone]; }]; 316 | 317 | // 分行书写的block,内部使用4空格缩进 318 | [operation setCompletionBlock:^{ 319 | [self.delegate newDataAvailable]; 320 | }]; 321 | 322 | // 使用C语言API调用的block遵循同样的书写规则 323 | dispatch_async(_fileIOQueue, ^{ 324 | NSString* path = [self sessionFilePath]; 325 | if (path) { 326 | // ... 327 | } 328 | }); 329 | 330 | // 较长的block关键字可以缩进后在新行书写,注意block的右括号'}'和调用block那行代码的第一个非空字符对齐 331 | [[SessionService sharedService] 332 | loadWindowWithCompletionBlock:^(SessionWindow *window) { 333 | if (window) { 334 | [self windowDidLoad:window]; 335 | } else { 336 | [self errorLoadingWindow]; 337 | } 338 | }]; 339 | 340 | // 较长的block参数列表同样可以缩进后在新行书写 341 | [[SessionService sharedService] 342 | loadWindowWithCompletionBlock: 343 | ^(SessionWindow *window) { 344 | if (window) { 345 | [self windowDidLoad:window]; 346 | } else { 347 | [self errorLoadingWindow]; 348 | } 349 | }]; 350 | 351 | // 庞大的block应该单独定义成变量使用 352 | void (^largeBlock)(void) = ^{ 353 | // ... 354 | }; 355 | [_operationQueue addOperationWithBlock:largeBlock]; 356 | 357 | // 在一个调用中使用多个block,注意到他们不是像函数那样通过':'对齐的,而是同时进行了4个空格的缩进 358 | [myObject doSomethingWith:arg1 359 | firstBlock:^(Foo *a) { 360 | // ... 361 | } 362 | secondBlock:^(Bar *b) { 363 | // ... 364 | }]; 365 | ``` 366 | 367 | 368 | ## 变量 369 | 370 | 变量尽量以描述性的方式来命名。单个字符的变量命名应该尽量避免,除了在`for()`循环。 371 | 372 | 星号表示变量是指针。例如, `NSString *text` 既不是 `NSString* text` 也不是 `NSString * text`,除了一些特殊情况下常量。 373 | 374 | [私有变量](#private-properties) 应该尽可能代替实例变量的使用。尽管使用实例变量是一种有效的方式,但更偏向于使用属性来保持代码一致性。 375 | 376 | 通过使用'back'属性(_variable,变量名前面有下划线)直接访问实例变量应该尽量避免,除了在初始化方法(`init`, `initWithCoder:`,等…),`dealloc` 方法和自定义的setters和getters。想了解关于如何在初始化方法和dealloc直接使用Accessor方法的更多信息,查看[这里](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmPractical.html#//apple_ref/doc/uid/TP40004447-SW6) 377 | 378 | **应该:** 379 | 380 | ```objc 381 | @interface RWTTutorial : NSObject 382 | 383 | @property (strong, nonatomic) NSString *tutorialName; 384 | 385 | @end 386 | ``` 387 | 388 | **不应该:** 389 | 390 | ```objc 391 | @interface RWTTutorial : NSObject { 392 | NSString *tutorialName; 393 | } 394 | ``` 395 | 396 | 397 | ## 属性特性 398 | 399 | 所有属性特性应该显式地列出来,有助于新手阅读代码。属性特性的顺序应该是storage、atomicity,与在Interface Builder连接UI元素时自动生成代码一致。 400 | 401 | **应该:** 402 | 403 | ```objc 404 | @property (weak, nonatomic) IBOutlet UIView *containerView; 405 | @property (strong, nonatomic) NSString *tutorialName; 406 | ``` 407 | 408 | **不应该:** 409 | 410 | ```objc 411 | @property (nonatomic, weak) IBOutlet UIView *containerView; 412 | @property (nonatomic) NSString *tutorialName; 413 | ``` 414 | 415 | NSString应该使用`copy` 而不是 `strong`的属性特性。 416 | 417 | 为什么?即使你声明一个`NSString`的属性,有人可能传入一个`NSMutableString`的实例,然后在你没有注意的情况下修改它。 418 | 419 | **应该:** 420 | 421 | ```objc 422 | @property (copy, nonatomic) NSString *tutorialName; 423 | ``` 424 | 425 | **不应该:** 426 | 427 | ```objc 428 | @property (strong, nonatomic) NSString *tutorialName; 429 | ``` 430 | 431 | 432 | ## 点符号语法 433 | 434 | 点语法是一种很方便封装访问方法调用的方式。当你使用点语法时,通过使用getter或setter方法,属性仍然被访问或修改。想了解更多,阅读[这里](https://developer.apple.com/library/ios/documentation/cocoa/conceptual/ProgrammingWithObjectiveC/EncapsulatingData/EncapsulatingData.html) 435 | 436 | 点语法应该**总是**被用来访问和修改属性,因为它使代码更加简洁。[]符号更偏向于用在其他例子。 437 | 438 | 439 | 440 | **应该:** 441 | ```objc 442 | NSInteger arrayCount = [self.array count]; 443 | view.backgroundColor = [UIColor orangeColor]; 444 | [UIApplication sharedApplication].delegate; 445 | ``` 446 | 447 | **不应该:** 448 | ```objc 449 | NSInteger arrayCount = self.array.count; 450 | [view setBackgroundColor:[UIColor orangeColor]]; 451 | UIApplication.sharedApplication.delegate; 452 | ``` 453 | 454 | 455 | ## 字面值 456 | 457 | `NSString`,`NSDictionary`,`NSArray`,和 `NSNumber`的字面值应该在创建这些类的不可变实例时被使用。请特别注意`nil`值不能传入`NSArray`和`NSDictionary`字面值,因为这样会导致crash。 458 | 459 | **应该:** 460 | 461 | ```objc 462 | NSArray *names = @[@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul"]; 463 | NSDictionary *productManagers = @{@"iPhone": @"Kate", @"iPad": @"Kamal", @"Mobile Web": @"Bill"}; 464 | NSNumber *shouldUseLiterals = @YES; 465 | NSNumber *buildingStreetNumber = @10018; 466 | ``` 467 | 468 | **不应该:** 469 | 470 | ```objc 471 | NSArray *names = [NSArray arrayWithObjects:@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul", nil]; 472 | NSDictionary *productManagers = [NSDictionary dictionaryWithObjectsAndKeys: @"Kate", @"iPhone", @"Kamal", @"iPad", @"Bill", @"Mobile Web", nil]; 473 | NSNumber *shouldUseLiterals = [NSNumber numberWithBool:YES]; 474 | NSNumber *buildingStreetNumber = [NSNumber numberWithInteger:10018]; 475 | ``` 476 | 477 | 478 | ## 常量 479 | 480 | 常量是容易重复被使用和无需通过查找和代替就能快速修改值。常量应该使用`static`来声明而不是使用`#define`,除非显式地使用宏。 481 | 482 | **应该:** 483 | 484 | ```objc 485 | static NSString * const RWTAboutViewControllerCompanyName = @"RayWenderlich.com"; 486 | 487 | static CGFloat const RWTImageThumbnailHeight = 50.0; 488 | ``` 489 | 490 | **不应该:** 491 | 492 | ```objc 493 | #define CompanyName @"RayWenderlich.com" 494 | 495 | #define thumbnailHeight 2 496 | ``` 497 | 498 | 499 | ## 枚举类型 500 | 501 | 当使用`enum`时,推荐使用新的固定基本类型规格,因为它有更强的类型检查和代码补全。现在SDK有一个宏`NS_ENUM()`来帮助和鼓励你使用固定的基本类型。 502 | 503 | **例如:** 504 | 505 | ```objc 506 | typedef NS_ENUM(NSInteger, RWTLeftMenuTopItemType) { 507 | RWTLeftMenuTopItemMain, 508 | RWTLeftMenuTopItemShows, 509 | RWTLeftMenuTopItemSchedule 510 | }; 511 | ``` 512 | 513 | 你也可以显式地赋值(展示旧的k-style常量定义): 514 | 515 | ```objc 516 | typedef NS_ENUM(NSInteger, RWTGlobalConstants) { 517 | RWTPinSizeMin = 1, 518 | RWTPinSizeMax = 5, 519 | RWTPinCountMin = 100, 520 | RWTPinCountMax = 500, 521 | }; 522 | ``` 523 | 524 | 旧的k-style常量定义应该**避免**除非编写Core Foundation C的代码。 525 | 526 | **不应该:** 527 | 528 | ```objc 529 | enum GlobalConstants { 530 | kMaxPinSize = 5, 531 | kMaxPinCount = 500, 532 | }; 533 | ``` 534 | 535 | 536 | ## Case语句 537 | 538 | 大括号在case语句中并不是必须的,除非编译器强制要求。当一个case语句包含多行代码时,大括号应该加上。 539 | 540 | ```objc 541 | switch (condition) { 542 | case 1: 543 | // ... 544 | break; 545 | case 2: { 546 | // ... 547 | // Multi-line example using braces 548 | break; 549 | } 550 | case 3: 551 | // ... 552 | break; 553 | default: 554 | // ... 555 | break; 556 | } 557 | 558 | ``` 559 | 有很多次,当相同代码被多个cases使用时,一个fall-through应该被使用。一个fall-through就是在case最后移除'break'语句,这样就能够允许执行流程跳转到下一个case值。为了代码更加清晰,一个fall-through需要注释一下。 560 | 561 | 562 | ```objc 563 | switch (condition) { 564 | case 1: 565 | // ** fall-through! ** 566 | case 2: 567 | // code executed for values 1 and 2 568 | break; 569 | default: 570 | // ... 571 | break; 572 | } 573 | 574 | ``` 575 | 576 | 当在switch使用枚举类型时,'default'是不需要的。例如: 577 | 578 | ```objc 579 | RWTLeftMenuTopItemType menuType = RWTLeftMenuTopItemMain; 580 | 581 | switch (menuType) { 582 | case RWTLeftMenuTopItemMain: 583 | // ... 584 | break; 585 | case RWTLeftMenuTopItemShows: 586 | // ... 587 | break; 588 | case RWTLeftMenuTopItemSchedule: 589 | // ... 590 | break; 591 | } 592 | ``` 593 | 594 | 595 | ## 私有属性 596 | 597 | 私有属性应该在类的实现文件中的类扩展(匿名分类)中声明,命名分类(比如`RWTPrivate `或`private`)应该从不使用除非是扩展其他类。匿名分类应该通过使用+Private.h文件的命名规则暴露给测试。 598 | 599 | **例如:** 600 | 601 | ```objc 602 | @interface RWTDetailViewController () 603 | 604 | @property (strong, nonatomic) GADBannerView *googleAdView; 605 | @property (strong, nonatomic) ADBannerView *iAdView; 606 | @property (strong, nonatomic) UIWebView *adXWebView; 607 | 608 | @end 609 | ``` 610 | 611 | 私有控件尽量统一使用懒加载的形式并配合 ({}) 的包装来写。 612 | 613 | **例如:** 614 | 615 | ```objc 616 | 617 | - (UILabel*)pullStateLabel { 618 | if (_pullStateLabel) { 619 | return _pullStateLabel; 620 | } 621 | _pullStateLabel = ({ 622 | UILabel* label = [[UILabel alloc] init]; 623 | label.text = @"test"; 624 | [label sizeToFit]; 625 | label; 626 | }); 627 | return _pullStateLabel; 628 | } 629 | 630 | - (void)loadUI { 631 | [self.view addSubview:self.pullStateLabel]; 632 | } 633 | 634 | ``` 635 | 636 | 637 | ## 布尔值 638 | 639 | Objective-C使用`YES`和`NO`。因为`true`和`false`应该只在CoreFoundation,C或C++代码使用。既然`nil`解析成`NO`,所以没有必要在条件语句比较。不要拿某样东西直接与`YES`比较,因为`YES`被定义为1和一个`BOOL`能被设置为8位。 640 | 641 | 这是为了在不同文件保持一致性和在视觉上更加简洁而考虑。 642 | 643 | **应该:** 644 | 645 | ```objc 646 | if (someObject) {} 647 | if (![anotherObject boolValue]) {} 648 | ``` 649 | 650 | **不应该:** 651 | 652 | ```objc 653 | if (someObject == nil) {} 654 | if ([anotherObject boolValue] == NO) {} 655 | if (isAwesome == YES) {} // Never do this. 656 | if (isAwesome == true) {} // Never do this. 657 | ``` 658 | 659 | 如果`BOOL`属性的名字是一个形容词,属性就能忽略"is"前缀,但要指定get访问器的惯用名称。例如: 660 | 661 | ```objc 662 | @property (assign, getter=isEditable) BOOL editable; 663 | ``` 664 | 665 | 文字和例子从这里引用[Cocoa Naming Guidelines](https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CodingGuidelines/Articles/NamingIvarsAndTypes.html#//apple_ref/doc/uid/20001284-BAJGIIJE) 666 | 667 | 668 | ## 条件语句 669 | 670 | 条件语句主体为了防止出错应该使用大括号包围,即使条件语句主体能够不用大括号编写(如,只用一行代码)。这些错误包括添加第二行代码和期望它成为if语句;还有,[even more dangerous defect](http://programmers.stackexchange.com/a/16530)可能发生在if语句里面一行代码被注释了,然后下一行代码不知不觉地成为if语句的一部分。除此之外,这种风格与其他条件语句的风格保持一致,所以更加容易阅读。 671 | 672 | **应该:** 673 | 674 | ```objc 675 | if (!error) { 676 | return success; 677 | } 678 | ``` 679 | 680 | **不应该:** 681 | 682 | ```objc 683 | if (!error) 684 | return success; 685 | ``` 686 | 687 | 或 688 | 689 | ```objc 690 | if (!error) return success; 691 | ``` 692 | 693 | 694 | ### 三元操作符 695 | 696 | 当需要提高代码的清晰性和简洁性时,三元操作符`?:`才会使用。单个条件求值常常需要它。多个条件求值时,如果使用`if`语句或重构成实例变量时,代码会更加易读。一般来说,最好使用三元操作符是在根据条件来赋值的情况下。 697 | 698 | Non-boolean的变量与某东西比较,加上括号()会提高可读性。如果被比较的变量是boolean类型,那么就不需要括号。 699 | 700 | **应该:** 701 | 702 | ```objc 703 | NSInteger value = 5; 704 | result = (value != 0) ? x : y; 705 | 706 | BOOL isHorizontal = YES; 707 | result = isHorizontal ? x : y; 708 | ``` 709 | 710 | **不应该:** 711 | 712 | ```objc 713 | result = a > b ? x = c > d ? c : d : y; 714 | ``` 715 | 716 | 717 | ## Init方法 718 | 719 | Init方法应该遵循Apple生成代码模板的命名规则。返回类型应该使用`instancetype`而不是`id` 720 | 721 | ```objc 722 | - (instancetype)init { 723 | self = [super init]; 724 | if (self) { 725 | // ... 726 | } 727 | return self; 728 | } 729 | ``` 730 | 731 | 查看关于instancetype的文章[Class Constructor Methods](#class-constructor-methods) 732 | 733 | 734 | ## 类构造方法 735 | 736 | 当类构造方法被使用时,它应该返回类型是`instancetype `而不是`id`。这样确保编译器正确地推断结果类型。 737 | 738 | 739 | ```objc 740 | @interface Airplane 741 | + (instancetype)airplaneWithType:(RWTAirplaneType)type; 742 | @end 743 | ``` 744 | 关于更多instancetype信息,请查看[NSHipster.com](http://nshipster.com/instancetype/) 745 | 746 | 747 | ## CGRect函数 748 | 749 | 当访问`CGRect`里的`x`,`y`,`width`,或 `height`时,应该使用[`CGGeometry`函数](http://developer.apple.com/library/ios/#documentation/graphicsimaging/reference/CGGeometry/Reference/reference.html)而不是直接通过结构体来访问。引用Apple的`CGGeometry `: 750 | 751 | > 在这个参考文档中所有的函数,接受CGRect结构体作为输入,在计算它们结果时隐式地标准化这些rectangles。因此,你的应用程序应该避免直接访问和修改保存在CGRect数据结构中的数据。相反,使用这些函数来操纵rectangles和获取它们的特性。 752 | 753 | 754 | **应该:** 755 | 756 | ```objc 757 | CGRect frame = self.view.frame; 758 | 759 | CGFloat x = CGRectGetMinX(frame); 760 | CGFloat y = CGRectGetMinY(frame); 761 | CGFloat width = CGRectGetWidth(frame); 762 | CGFloat height = CGRectGetHeight(frame); 763 | CGRect frame = CGRectMake(0.0, 0.0, width, height); 764 | ``` 765 | 766 | **不应该:** 767 | 768 | ```objc 769 | CGRect frame = self.view.frame; 770 | 771 | CGFloat x = frame.origin.x; 772 | CGFloat y = frame.origin.y; 773 | CGFloat width = frame.size.width; 774 | CGFloat height = frame.size.height; 775 | CGRect frame = (CGRect){ .origin = CGPointZero, .size = frame.size }; 776 | ``` 777 | 778 | 779 | ## 黄金路径 780 | 781 | 当使用条件语句编码时,左手边的代码应该是"golden" 或 "happy"路径。也就是不要嵌套`if`语句,多个返回语句也是OK。 782 | 783 | **应该:** 784 | 785 | ```objc 786 | - (void)someMethod { 787 | if (![someOther boolValue]) { 788 | return; 789 | } 790 | 791 | // Do something important 792 | } 793 | ``` 794 | 795 | **不应该:** 796 | 797 | ```objc 798 | - (void)someMethod { 799 | if ([someOther boolValue]) { 800 | // Do something important 801 | } 802 | } 803 | ``` 804 | 805 | 806 | ## 错误处理 807 | 808 | 当方法通过引用来返回一个错误参数,判断返回值而不是错误变量。 809 | 810 | **应该:** 811 | ```objc 812 | NSError *error; 813 | if (![self trySomethingWithError:&error]) { 814 | // Handle Error 815 | } 816 | ``` 817 | 818 | **不应该:** 819 | ```objc 820 | NSError *error; 821 | [self trySomethingWithError:&error]; 822 | if (error) { 823 | // Handle Error 824 | } 825 | ``` 826 | 827 | 在成功的情况下,有些Apple的APIs记录垃圾值(garbage values)到错误参数(如果non-NULL),那么判断错误值会导致false负值和crash。 828 | 829 | 830 | ## 单例模式 831 | 832 | 单例对象应该使用线程安全模式来创建共享实例。 833 | 834 | ```objc 835 | + (instancetype)sharedInstance { 836 | static id sharedInstance = nil; 837 | 838 | static dispatch_once_t onceToken; 839 | dispatch_once(&onceToken, ^{ 840 | sharedInstance = [[self alloc] init]; 841 | }); 842 | 843 | return sharedInstance; 844 | } 845 | ``` 846 | 847 | 这会防止[possible and sometimes prolific crashes](http://cocoasamurai.blogspot.com/2011/04/singletons-your-doing-them-wrong.html). 848 | 849 | 850 | ## 换行符 851 | 852 | 换行符是一个很重要的主题,因为它的风格指南主要为了打印和网上的可读性。 853 | 854 | 例如: 855 | 856 | ```objc 857 | self.productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers]; 858 | ``` 859 | 860 | 一行很长的代码应该分成两行代码,下一行用四个空格隔开。 861 | 862 | ```objc 863 | self.productsRequest = [[SKProductsRequest alloc] 864 | initWithProductIdentifiers:productIdentifiers]; 865 | ``` 866 | 867 | 868 | ## Xcode工程 869 | 870 | 物理文件应该与Xcode工程文件保持同步来避免文件扩张。任何Xcode分组的创建应该在文件系统的文件体现。代码不仅是根据**类型**来分组,而且还可以根据**功能**来分组,这样代码更加清晰。 871 | 872 | 尽可能在target的Build Settings打开"Treat Warnings as Errors,和启用以下[additional warnings](http://boredzo.org/blog/archives/2009-11-07/warnings)。如果你需要忽略特殊的警告,使用 [Clang's pragma feature](http://clang.llvm.org/docs/UsersManual.html#controlling-diagnostics-via-pragmas)。 873 | -------------------------------------------------------------------------------- /python/README.md: -------------------------------------------------------------------------------- 1 | - 2 | -------------------------------------------------------------------------------- /python/django/README.md: -------------------------------------------------------------------------------- 1 | - 2 | --------------------------------------------------------------------------------