├── Practical_ramda.md ├── readme.md ├── linux.md ├── optimize.md ├── UI_Advice_Specification.md ├── ITmanagement.md ├── Chrome_Dev_Tools.md ├── user_babel6.md ├── Javascript & Node Tests Framework.md ├── github.md ├── rxjs.md ├── docker.md ├── FP Javscript Study.md ├── less.md ├── test.md ├── haskell_study.md ├── HTML_and_CSS_Style_Guide.md ├── go.md ├── FE_knowledge.md ├── JavaScript_Style_Guide.md └── Typescript.md /Practical_ramda.md: -------------------------------------------------------------------------------- 1 | # 实用的第二代函数式编程库 ramda(翻译) 2 | 3 | > 原文:http://www.macwright.org/2015/08/27/practical-ramda.html 4 | 5 | [Ramda](http://www.macwright.org/2015/08/27/practical-ramda.html) 是Javascript第二代函数式编程库。我们在[Mapbox Studio](https://www.mapbox.com/maps/#mapbox-gl)开发中使用它发挥了很大的作用。考虑到函数式编程库通常是记录与数学(或抽象)的例子, -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | 2 | # 项目分享 3 | 4 | - [开源一个基于vue+redux项目(删减版)](https://github.com/sean-mo/GM_VUE) 5 | - [移动端HTML5活动页 - 2014](https://github.com/sean-mo/lottery) 6 | - [基于infernoJS+webpack2的微信页面](https://github.com/sean-mo/inferno-mobile) 7 | 8 | # 学习笔记 9 | 10 | - [docker](https://github.com/sean-mo/docs/blob/master/docker.md) 11 | - [Rxjs](https://github.com/sean-mo/docs/blob/master/rxjs.md) 12 | - [liunx](https://github.com/sean-mo/docs/blob/master/linux.md) 13 | - [Go](https://github.com/sean-mo/docs/blob/master/go.md) 14 | - [管理知识](https://github.com/sean-mo/docs/blob/master/ITmanagement.md) 15 | 16 | # 2015年 17 | 18 | - [haskell趣学指南小结](https://github.com/sean-mo/docs/blob/master/haskell_study.md)(2015/12/05-12/06) 19 | - [Javascript函数式编程小结](https://github.com/sean-mo/docs/blob/master/FP%20Javscript%20Study.md)(2015-12-05 21:00-23:30) 20 | - [babel6使用总结](https://github.com/sean-mo/docs/blob/master/user_babel6.md) (2015-12-05 18:00-20:00) 21 | - [javascript & node 前端测试框架](https://github.com/sean-mo/docs/blob/master/Javascript%20%26%20Node%20Tests%20Framework.md)(2015-11-30) 22 | 23 | 24 | # 项目积累 25 | 26 | [Vue GM项目优化方案](https://github.com/sean-mo/docs/blob/master/optimize.md)(2015-12-07) -------------------------------------------------------------------------------- /linux.md: -------------------------------------------------------------------------------- 1 | # 基础命令 2 | 3 | > pietty 处理SSH服务 4 | 5 | - `who`: 谁在线 6 | - `netstat -a`: 网络联机状态 7 | - `ps aux`: 查看后台运行的程序 8 | - `shutdown` 关机(需要root权限) 9 | - `reboot/halt/poweroff`:重启(需要root权限) 10 | - `sync`: 将内存数据同步写入磁盘(每次关机,重启前,避免数据丢失,该命令待定) 11 | 12 | ## 权限 13 | > `r`:可读 `w`:可写 `x` 可执行 `-` 文件(首)或没有权限 14 | 15 | - 前三代表`所有者权限` 16 | - 中三`所属用户组权限` 17 | - 后三 `其他人对此文件或目录权限` 18 | 19 | - 第一个是 `d` 代表目录 `-` 代表文件 `I` 连接文件 `b`: 设备可供存储的设备 `c`串行端口(如鼠标、键盘) 20 | 21 | ``` 22 | -rwx rwx --- 23 | drwx --- --- 24 | ``` 25 | 26 | > 改变文件属性和权限 27 | 28 | *注意,如果某目录不具有x权限,则无法访问和提示没有权限,开启r权限,同时应开启x权限 29 | 30 | - `chgrp`: 改变文件所属用户组 31 | - `chown`: 改变文件所有者 32 | - `chmod`: 改变文件的权限 33 | 34 | ``` 35 | chgrp users install .log 更改文件属于users组 (-R 递归) 36 | chown bin install.log 更改文件属于bin帐号 37 | 38 | ``` 39 | 40 | ## 文件夹作用 41 | 42 | - /bin: 重要执行文件,存放单用户维护模式下还能够操作的命令 43 | - /boot:启动文件夹 44 | - /dev: 设备与接口 45 | - /home: 用户主文件夹 46 | - /lib: 执行文件及内核所需函数库或模块 47 | - /media:媒介(可删除的设备)如软盘、光盘 48 | - /mnt: 用途与/media雷同,目前暂时用于挂载 49 | - /opt: 第三方软件放置目录 50 | - /sbin: 开机过程所需的,以及还原系统所需的命令 51 | - /srv: 网络服务所需的数据目录 52 | - /tmp: 临时文件夹 53 | - /sys /proc: 虚拟系统文件夹、内核相关的,已加载的内核和检测的设备信息,不占硬盘空间 54 | - /etc: 配置文件 55 | - /etc/init.d: 各种软件的启动脚本 56 | - /usr: 操作系统软件资源的目录,不是用户数据 57 | - /usr/bin/: 绝大部分用户可能用到的命令(与开机过程无关) 58 | - /usr/include: C/C++的头文件 59 | - /usr/lib/: 各应用软件的函数库,以及不被一般用户惯用的执行文件或脚本。 60 | - /usr/local/: 系统管理员在本机自行安装的软件,一般建议安装在此文件夹 61 | - /usr/sbin/: 非系统所需的常用命令 62 | - /usr/share: 共享文件夹 63 | - /usr/src: 源码一般建议放此处,内核源码放在/usr/src/liunx 64 | - /var: 系统运行后渐渐占用硬盘容量的目录,针对常态变动文件,包括缓存、登录文件、以及某些软件运行所产生的文件。例如MYSQL数据库文件 65 | - /var/log/: 日志 66 | - /var/run/: 某些服务启动后,将PID放置 的目录 67 | - /var/spool: 排队等待的数据 68 | 69 | # 高性能 Linux 70 | 71 | ## 安全运维 72 | 73 | - 删除不必要的用户组和用户(userdel/ 74 | - 停止不必要的服务,只保留系统运行时和应用所需的服务,例如httped -------------------------------------------------------------------------------- /optimize.md: -------------------------------------------------------------------------------- 1 | # 项目优化 2 | 3 | ## Global State 4 | 5 | app 6 | 7 | - `title` 8 | - `lang` => `language` => { `name`, `pack` } 9 | - `GameID` 10 | - `Role` 11 | - `views` 12 | - `MenuRole` 13 | - `menuId` 14 | - `GameList` 15 | - `currentUserId` 16 | 17 | server 18 | 19 | - `APPKEY` 20 | - `TOKEN` 21 | - `serverURL` 22 | - `testURL` 23 | - `prdURL` 24 | - `wayURL` 25 | 26 | permission 27 | 28 | - `hasManager` 29 | - `hasOPADMIN` 30 | - `hasUserManager` 31 | - `hasSysConfPerm` 32 | 33 | cache 34 | 35 | - `cacheTemplate` => `CacheTemplateList` 36 | - `cacheConfig` => `CacheConfigList` 37 | - `cacheZone` => `CacheServerList` 38 | - `cacheWay` => `CacheWayList` 39 | - `cacheSelect` => `CacheSelect` => 下拉菜单 40 | - `cacheOptions` => `CacheOptions` => 枚举内容LIST 41 | 42 | ## Vue Redux 数据流程优化 43 | 44 | > 项目引入Redux优化整个数据流程,保持单向数据流可追溯。需要开发基于Vue的Redux插件,才能够有效确保系统数据流的稳定性。为此规范了项目规范及约束内容。 45 | 46 | **组件重新划分** 47 | 48 | - 智能组件(smart Component):提供数据、函数方法(Actions)等业务支撑。 49 | - 机械组件(machine Component):处理智能组件传递的数据和函数方法,完成数据展示和控制业务 50 | 51 | **组件区分和描述** 52 | 53 | > 智能组件需提供 $SmartRedux 的 Vue属性,即vm.$SmartRedux。其包含以下内容: 54 | 55 | - State: 当前状态,其值为一个Object。由于vue本身不具备react的diff dom。故此vue-redux会逐个做对比,若有不同,则自动启用vm.$set更新该值。组件初始化时,会自动将State合并至vm.$data里。 56 | - Actions:智能函数,提供具有store.dispatch的actions函数,用于操纵数据流 57 | - 注意:初始化时,即vm.created,会使用store.subscribe绑定到$updateStore方法里,即store有更新时,自动更新到$updateStore。当组件初销毁时,即vm.destroyed,会使用unsubscribe解除绑定. 58 | 59 | > 机械组件 60 | 61 | - 允许内部有自己可控制的事件、函数及私有变量。 62 | - 涉及Redux数据流时,数据及函数actions须走离它最近的父级智能组件。禁止直接从Store获取数据。 63 | - 涉及Global State,可通常ConfigStore.getStore获取。 64 | 65 | > 约束规范 66 | 67 | - 禁用`vm.inherit`继承父级数据,原有项目存在多处继承,必须移除,仅能通过智能组件传递数据 68 | - 一切涉及业务数据(例如全局数据、数据库、环境数据等等)必须走redux的actions、Reducer、middleware 69 | - 按需加载设置分割点时,禁用以机械组件为分割点,必须以智能组件做为分割点。为防止加载失败,需设置fallback备用方案。 70 | -------------------------------------------------------------------------------- /UI_Advice_Specification.md: -------------------------------------------------------------------------------- 1 | # UI稿规范建议 2 | 3 | ## PS 分组 4 | > 提供标准化、组件化、通用化、粒度更细的PS分组 5 | 6 | #### 推荐分组 7 | - 素材:如图标、LOGO、特殊字体、动画、人物等带透明背景的等 8 | - 结构:全站导航、头部、尾部、轮播等 9 | - 页面:首页、日报、统计等 10 | - 组件:按钮、弹层、警告、提示、气泡等 11 | - 业务:部门筛选框等存在复用的业务模块 12 | 13 | 14 | ## 标注 15 | > 提供等比、相对、精确、统一的尺寸标注 16 | 17 | - 针对可复用的组件单独拎出来进行标注:图标、按钮、弹层、文本框、部门筛选框等等 18 | - 标注的尺寸包括:宽高度、内外边距离、边框大小、文字大小、字体、颜色、交互与事件状态(如hover)等 19 | - 标注单位:固定尺寸采用像素PX、非固定尺寸提供百分比 20 | - 标注误差:适配不同的浏览器、操作系统及设备会产生误差,请提供允许误差尺寸的范围(针对可复用的组件、页面结构复杂的样式) 21 | - 自适应标注:针对可复用的组件和页面模块可能会在不同尺寸容器存在自适应状态,请提供一致的尺寸或者自适应后的尺寸标注 22 | 23 | 24 | ## 图标 25 | 26 | - 图标素材尽量来源于iconfont等webfont平台,同时标注图标名称方便查询。针对不同色值的形状相同的图标,建议直接webFont平台获取。 27 | - 图标若带有阴影、透明背影、复杂的渐变效果,尽量额外提供PNG,以确保得到最佳效果。 28 | - 图标必须统一尺寸和颜色:如大号、小号、普通,或者20 * 20、40 * 40等。不同分辨率及设备仅使用1-2种尺寸的图标体积。 29 | 30 | ## 字体/字号 31 | 32 | - 中文字体:微软雅黑、萍方(PingFang SC) 33 | - 英文字体:Helvetica Neue、Arial、sans-serif 34 | - 特殊字体:可以通过WebFont平台([有字库](http://www.youziku.com/)、[方正](http://www.foundertype.com/)、[Google字体](http://www.google.com/fonts))付费或使用免费的字体 35 | - 字号:建议使用默认12、14、16、18、20等双数字号,避免可能产生模糊的边缘 36 | - 粗体:默认200、粗体700,特别加重900 37 | - 将全站字号字体单独标识出来:如H1(主标题):40px,H2(次标题):30px,H6(小标题):16px,普通文字:12px,重点文字:14px 38 | 39 | ## 颜色 40 | 41 | - 颜色分类:主色(1、2、3)、次色(1、2)、普通文字色、链接色、状态色(hover等)、边框色等 42 | - 推荐部分颜色:#33333、#666666、#999999、#CCCCCC、#E1E1E1、#DDDDDD、#F5F5F5等等 43 | - 颜色参考网站:https://www.mapbox.com/base/styling/color/ 44 | 45 | ## 组件 46 | 47 | > 提供标准、一致、尺寸分类的,界面组件都单独拎出来统一尺寸、颜色等 48 | 49 | - 按钮:按大小(大、小、普通)、按功能(确定、取消、工具栏、菜单等)、按颜色等对按钮进行统一分类 50 | - 表单:输入框、单选、多选、下拉、标签、验证等 51 | - 时间:日期选择、时间选择、日期+时间选择等 52 | - 开关、级联选择、树形、日历、气泡、弹层、表格等 53 | 54 | 55 | ## 参考网站: 56 | 57 | - mapbox: https://www.mapbox.com/base/ 58 | - 蚂蚁金融UI组件: http://ant.design 59 | - bootstrap: http://www.bootcss.com/ 60 | - 知乎:https://www.zhihu.com/question/29936125 61 | - 花瓣:http://huaban.com/pins/603658986/ -------------------------------------------------------------------------------- /ITmanagement.md: -------------------------------------------------------------------------------- 1 | # 管理及架构 2 | 3 | ## 敏捷开发 4 | 5 | > 加粗黑字为左项,“高于”的右边是右项。右项在任何时候都很重要,但是左项要比右项更重要 6 | 7 | * **个体和互动** 高于 流程和工具 8 | * **工作的软件** 高于 详尽的文档 9 | * **客户合作** 高于 合同谈判 10 | * **响应变化** 高于 遵循计划 11 | 12 | > 站立会让每个人得到充分的授权,自主发挥,保持focus进度,展示成功,寻求支援 13 | 14 | * 通过持续不断地**及早交付**有价值的软件使客户满意 15 | * **为了客户的竞争优势**,敏捷过程掌控变化 16 | * **经常地交付可工作的软件**,相隔几星期或一两个月,倾向于采取较短的周期 17 | * **业务人员和开发人员必须相互合作**,项目中的每一天都不例外 18 | * **激发个体的斗志,以他们为核心搭建项目**。提供所需的环境和支援,辅以信任,从而达成目标。 19 | * 传递信息效果最好效率也最高的方式是**面对面的交谈** 20 | * **可工作的软件**是进度的首要度量标准 21 | * 敏捷过程倡导**可持续开发**。责任人、开发人员和用户要能够共同维持其步调稳定延续 22 | * 以**简洁**为本,它是极力减少不必要工作量的艺术。 23 | * 最好的架构、需求和设计出自自**组织团队** 24 | * 团队**定期地反思如何能提高成效**,并依此调整自身的举止表现 25 | 26 | > 敏捷的首要目标是**交付**,从零碎的功能到完整的产品 27 | 28 | * 定期观察团队的表现 29 | * 选择适合团队的实践 30 | * 每天站立会议,当开发放缓时,应隔1-2天或者更换形式 31 | * 每2周一次冲刺,冲刺结束后展示交付和安排下次冲刺(小目标) 32 | * 每月重复一次,或者总结 33 | * 当开发放缓,应随时调整团队最佳实践 34 | 35 | ## 管理杂谈 36 | 37 | > 对团队伤害最小的人裁掉 38 | 39 | * 分摊式裁人: 分摊到每个team一定摊人比例,大公司惯用此类手法裁人 40 | * 运动式裁人:高层更迭的时候,伐同异党的时候 41 | * 系统性裁人:裁掉战略性计划之外的人,或者是绩效差的部门 42 | 43 | > 混日子的程序员 44 | 45 | * 瞒天过海:上下班时间刚好碰到,周六日过来看下同事 46 | * 无中生有: 假装每个项目都聊一下,实际上没干什么活,邮件里扯谈 47 | * 混水摸鱼,偷梁换柱: 把水搅浑,你谈体系结构,我谈性能优化,牵着你的鼻子走 48 | * 隔岸观火: 每没每了的开会,没有解决方案,没有执行计划的会议,让大家继续争议细节,回不到实际的问题上 49 | * 树上开花:学会营造大阵势,将一切风险和问题都放大,一个月看不到成果的,想办法让其膨胀到半年,一年,越难追踪越好 50 | 51 | > 工作的轻重缓急 52 | 53 | * 重要且紧急:分配工作、产品讨论会、完成交接工作 54 | * 重要不紧急:监控工作进度、组织每周例会、学习新技术、学习管理知识、培养员工 55 | * 紧急不重要:参与客户会议 56 | * 不紧急不重要:整理周报、参加项目会议 57 | 58 | #### 重点 59 | 60 | * 掌握团队成员的工作状态,了解每个人的工作才能够更好的做出评估,多观察形成自己的评判是**日常工作的首要任务** 61 | * 如果你不能测量,便无法管理 62 | * 观察的目的是了解,不是监控。观察还有助于你发现谁在什么时候需要帮助 63 | * 一对一谈话 64 | * 解决优先级最高的问题单 65 | 66 | > 技术与管理的平衡 67 | >> 管理者自己具备技术能力,才能甄别每个人的能力,将他们合理配置,并辅以合适的培训和指导。如果不懂技术,便无法有效地分配任务,和团队成员沟通,了解他们的状态,也无法有效地和其它团队合作。 68 | 69 | * 花时间阅读别人的代码,但最好交给技术专家去做,无法一个人寻找代码的每一处风险 70 | * 将最擅长的技术传授给团队,使其成为团队最擅长的 71 | * 团队最不擅长的要找到人弥补,找不到则亲自处理 72 | * 团队的问题就是你的问题,搞不定就是你的责任 73 | * 观察是反馈的前提,不能够光看表面和数据,要看实际情况,总体的评估个人能力,简单的工作能够快速反馈,和复杂的工作快速反馈的评估是不一样的 74 | 75 | > 工资,奖金和股票期权 76 | >> 为每个人创建一个叫『花名册』 77 | 78 | ❏ 基本信息 79 | 80 | ❏ 技术能力 81 | 82 | ❏ 闪光点/值得表彰的事情 83 | 84 | ❏ 参与的项目状况 85 | 86 | ❏ 擅长/不擅长 87 | 88 | ❏ 1:1谈话记录 89 | 90 | ❏ 需要提醒ta的地方 91 | 92 | ❏ 其它 93 | 94 | * 私交:如果你没有建立起个人关系,你也仅仅只是个头衔 95 | * 判断是否超额完成目标 96 | * 激励机制 97 | * 裁人的同时,需要帮助他寻找更好的工作机会,让他感激你 98 | 99 | > 跟进: 制定一份清晰的行动纲领 100 | 101 | * 会议达成了什么方案 102 | * 谁来负责其中的哪个部分 103 | * 截止日期是什么 104 | * 中间的里程碑/检查点都在什么时候 105 | * 方案如何完成 106 | * 需要什么资源 107 | * 由谁来负责筹备这些资源 108 | * 中间检查点、里程碑的备选方案和解决方案 109 | * 风险控制 110 | * 沟通关键点和障碍点 111 | * 邮件中的各种纪要分类管理 112 | 113 | > 激励 114 | 115 | * 管理者的作用主要不在约束,也就是「管」,而在于调节,也就是「理」 116 | * 分配有挑战的活,提高各种收入,晋升,不定期的奖金,公开场合去表扬 117 | * 拿SMART原则来说,就是:具体的,可测量的,有困难但可以克服的,有明确截止日期的,并且是结果导向的。 118 | * 管理者自己以身作则,身先士卒。 -------------------------------------------------------------------------------- /Chrome_Dev_Tools.md: -------------------------------------------------------------------------------- 1 | # Chrome Dev Tools 2 | 3 | > 基本console 4 | 5 | 1、console.log 用于输出普通信息 6 | 7 | 2、console.info 用于输出提示性信息 8 | 9 | 3、console.error用于输出错误信息 10 | 11 | 4、console.warn用于输出警示信息 12 | 13 | 5、console.debug用于输出调试信息 14 | 15 | 6、console.dirxml输出某个node结点的信息 16 | 17 | > 断言 18 | 19 | 假如childNodes数量小于500,则断言 20 | 21 | ``` 22 | console.assert(list.childNodes.length < 500, ‘Node Count is > 500’) 23 | ``` 24 | > 分组 25 | 26 | - 显示分组,允许嵌套 27 | 28 | ``` 29 | console.group("Task Group"); // 可以用groupCollapsed进行拆叠 30 | console.log("Starting Sub Task A"); 31 | console.group("Task Group A"); 32 | console.log("Task Stage 1 is completed"); 33 | console.log("Task Stage 2 is completed"); 34 | console.log("Task Stage 3 is completed"); 35 | console.groupEnd(); 36 | console.groupEnd(); 37 | ``` 38 | 39 | - 可以代替console.group,用于分组折叠 40 | 41 | ``` 42 | console.group("Task Group"); // 可以用groupCollapsed进行拆叠 43 | console.log("Starting Sub Task A"); 44 | console.groupCollapsed("Task Group A"); 45 | console.log("Task Stage 1 is completed"); 46 | console.log("Task Stage 2 is completed"); 47 | console.log("Task Stage 3 is completed"); 48 | console.groupEnd(); 49 | 50 | ``` 51 | > 格式化 52 | 53 | **console.log(“‘%s’”, xx)** 54 | 55 | ``` 56 | %s 字符串 57 | %d 或 %i 整型 58 | %f 浮点 59 | %o 将值格式化为可张开的DOM对象 console.dir 也可以 60 | %O 将值格式化可张开的JAVASCRIPT对象 61 | %c 第二个参数传进去css样式 62 | ///传样式 63 | console.log("%cThis will be formatted with large, blue text", "color: blue; font-size: x-large”); 64 | ``` 65 | 66 | > 测试耗时 67 | 68 | ``` 69 | console.time('key') 70 | var a = 1; 71 | console.timeEnd('key') 72 | ``` 73 | 74 | > 统计代码执行的次数 75 | 76 | ``` 77 | function myfun(){ 78 | // 其它函数... 79 | console.count('myfun被执行的次数'); 80 | } 81 | myfun(); 82 | ``` 83 | > 打印对象或DOM 84 | 85 | ``` 86 | console.dir({ 87 | a: '1', 88 | b: '2', 89 | c: true, 90 | myfunc: function(){ 91 | console.log('xxx') 92 | } 93 | }) 94 | ``` 95 | > 设置条件断点 96 | 97 | 可以设置条件断点 http://www.randomthink.net/blog/2012/11/breakpoint-actions-in-javascript/ 98 | 99 | 即在sources里设置断点后,编辑断点,输入表达式,如 abc = 2; 100 | 101 | 命令行api:https://developer.chrome.com/devtools/docs/commandline-api 102 | 103 | 104 | > $: 支持Jquery的$ 105 | 106 | getEventListeners(object): 获取对象的事件 107 | 108 | > monitor(function) 监视函数的输入参数和返回值 109 | 110 | function add(x,y) => x + y; 111 | monitor(add); 112 | unmonitor(add) 113 | 114 | > 监控对象事件 115 | 116 | - monitorEvents(object[, events]) 117 | - monitorEvents(windows, [‘resize’, ’scroll’]) 118 | - unmonitorEvent(object) 119 | 120 | 事件 支持事件 121 | mouse "click", "dblclick", "mousedown", "mouseeenter", "mouseleave", "mousemove", "mouseout", "mouseover", "mouseup", "mouseleave", "mousewheel" 122 | key "keydown", "keyup", "keypress", "textInput" 123 | touch "touchstart", "touchmove", "touchend", "touchcancel" 124 | control "resize", "scroll", "zoom", "focus", "blur", "select", "change", "submit", “reset" 125 | 126 | > 获取当前CPU消耗的propfile 127 | 128 | - propfile(‘x’) 129 | - propfileEnd(‘x’) 130 | 131 | > 表格化显示 132 | 133 | ``` 134 | table(data[, columns]) data可以是个JSON对象 135 | var names = [ {x:1,y:2}, {x:3, y:6}] 136 | table(names) 137 | ``` 138 | > 开发流程 139 | 140 | ``` 141 | cmd + o 文件查找 142 | cmd + f 文本查找 143 | cmd + shift + o 函数查找 144 | cmd + g 跳至行号 145 | ``` 146 | 147 | > “开发者工具”也会保存本地文件的所有修改记录。如果你编辑了一个脚本或者样式并进行了保存,那么可以在Sources面板里的文件名上单击右键(或者在源代码区域),然后选择“本地修改(Local modifications)”来查看历史记录。 148 | 出现的本地修改(Local modifications)面板将会显示: 149 | 150 | 修改内容的差异; 151 | 修改的时间; 152 | 修改的文件名和一些操作链接。 153 | 154 | “还原(revert)”链接将会还原所有对文件的修改至初始状态,并删除历史记录。 155 | “恢复原始内容(apply original content)”将会完成相同的动作,但是会在视图里保留历史记录,以便于还原某个特定的修改。 156 | 最后,“应用修改的内容(apply revision content)”将会使在某个时间发生的修改生效。 157 | 158 | 可以右键设置DOM断点break on 当该dom发生变化的时候自动断点 159 | subtree modification 当子树被修改 160 | attribute … 当属性被修改 161 | node remove 当结点被删除 162 | 163 | ## 使用 Chrome 远程调试 Android 设备 164 | https://github.com/sean-mo/CN-Chrome-DevTools/blob/master/md/Use-Tools/remote-debugging.md 165 | 166 | -------------------------------------------------------------------------------- /user_babel6.md: -------------------------------------------------------------------------------- 1 | # 使用Babel6总结 2 | 3 | babel6,较上一版本,做了很多拆分。把babel做成一个容器,拆分原有的自带ES6的功能,更名为ES2015,估计后续会有ES2016等等。导致很多伸手党、百度党等等纷纷中招,以致出现相当多的坑。硬生生的把我缩回到了babel5.8。在此不推荐大伙在生产环境用。 4 | 5 | ## babel环境安装与配置 6 | 首先,Babel6在npm3.0以前体积相当大(约300M左右),因为依赖库重复安装量太大,建议升级node版本至5.x,npm默认就会升级到3.x。(window安装成功后,每次npm install插件会有相当多的进度条) 7 | 8 | 第一步,安装核心库 9 | 10 | ``` 11 | npm install babel-core --save-dev 12 | ``` 13 | 第二步,安装预设(presets)库 14 | 15 | 1、预装ES2015 16 | 17 | 支持es6语法,具体可看:http://babeljs.io/docs/learn-es2015/ 18 | 19 | ``` 20 | npm install babel-preset-es2015 21 | ``` 22 | 2、安装runtime插件 23 | 24 | 提供ES6类型扩展,如:array.of、json.stringify、object.assign、symbol、iterator等。相关方法可看:阮一峰 ES6入门:http://es6.ruanyifeng.com/ 25 | 26 | FAQ: 27 | 28 | - transform-runtime 内部引用了 babel-runtime,绝大部分扩展出自于babel-runtime 29 | - babel-runtime部署在babel6下,会有引起symbol/async等ES6类型扩展对应的函数缺失的bug,另外:目前最新的babel-runtime6.18及其以前是有问题的(2015.12.5) 30 | 31 | ``` 32 | npm install babel-plugin-transform-runtime babel-runtime 33 | ``` 34 | 3、babel-preset-stage (选择安装) 35 | 36 | 提供ES7功能 37 | 38 | - Stage 0 - 实验阶段 39 | - Stage 1 - 建议阶段 40 | - Stage 2 - 草案阶段 41 | - Stage 3 - 稳定阶段 42 | 43 | 详情可参考:http://babeljs.io/docs/plugins/#transform 44 | 45 | 安装了Stage 0 就包含 1-3阶段所有支持的特性。 46 | 47 | ``` 48 | npm install babel-preset-stage-0 49 | ``` 50 | 51 | 4、react(选装) 52 | 53 | 提供react、jsx支持,目前babel5.x以后都支持jsx和react的编译。(jsx已废弃) 54 | 55 | ``` 56 | npm install babel-preset-react 57 | ``` 58 | 5、babel-loader(选装) 59 | 60 | loader是提供webpack文件解析的外部插件,假如项目使用的是webpack,建议采用这个跑起来 61 | 62 | ``` 63 | npm install babel-loader 64 | ``` 65 | 6、node require hook(选装) 66 | 67 | - node环境支持使用require来解析babel代码。 68 | - 方法是require('babel-register'). 注意:5.x是require('babel/register') 69 | - 需要安装babel-register(6.x),6.0以下无须安装 70 | 71 | ``` 72 | npm install babel-register 73 | ``` 74 | 75 | 7、最后一步,配置当前项目babel环境参数: 76 | 77 | - 项目文件夹新建 .babelrc 文件,输入以下内容: 78 | 79 | ``` 80 | { 81 | "presets": ["es2015", "stage-0", "react"], 82 | "plugins": ["transform-runtime"] 83 | } 84 | ``` 85 | stage-0和react请根据项目需要选装 86 | 87 | 88 | ## 让项目跑起来 89 | 90 | 以下有几种方式可以让项目跑起来 91 | 92 | 1、命令行(cli模式):通过终端,进入项目文件夹,全局安装命令行 93 | 94 | ``` 95 | npm install --global babel-cli 96 | babel script.js // 编译scripts 97 | babel script.js -out-dir dist // 编译scripts到dist文件夹 98 | babel-node test.js // 编译并运行test.js 99 | ``` 100 | 文档:http://babeljs.io/docs/usage/cli/ 101 | 102 | 2、API 103 | 104 | 我们在写gulp插件、webpack loader或者node时,需要使用API进行babel编译 105 | 106 | ``` 107 | var babel = require("babel-core"); 108 | babel.transform(code, [options]) // => { code, map, ast } 109 | var result = babel.transform("code();", options); 110 | result.code; 111 | result.map; 112 | result.ast; 113 | ``` 114 | 115 | 文档:http://babeljs.io/docs/usage/api/ 116 | 117 | 3、Require Hook 118 | 119 | 提供node require babel编译支持 120 | 121 | 文档:http://babeljs.io/docs/usage/require/ 122 | 123 | 4、polyfill(不建议) 124 | 125 | 一般是用到browser(浏览器端),即用script标签引入。babel-node/babel-runtime已经包含了polyfill。不建议采用,因为我们都习惯使用工具(webpack/gulp)编译babel。 126 | 127 | 128 | ## 常见问题 129 | 130 | 1、gulp支持 131 | 132 | ``` 133 | var gulp = require('gulp'); 134 | var babel = require('gulp-babel'); 135 | 136 | gulp.task('default', () => { 137 | return gulp.src('src/app.js') 138 | .pipe(babel({ 139 | presets: ['es2015'] 140 | })) 141 | .pipe(gulp.dest('dist')); 142 | }); 143 | ``` 144 | 参考:https://github.com/babel/gulp-babel 145 | 146 | 2、webpack支持 147 | 148 | ``` 149 | module: { 150 | loaders: [ 151 | { 152 | test: /\.jsx?$/, 153 | exclude: /(node_modules|bower_components)/, 154 | loader: 'babel' // 'babel-loader' is also a legal name to reference 155 | } 156 | ] 157 | } 158 | 159 | ``` 160 | 参考:https://github.com/babel/babel-loader 161 | 162 | 3、__esModule (默认false) 163 | 164 | 代表当前模块是es2015输出的模块,当为true,则export default 就不会输出成object.default 165 | 166 | 示例: 167 | 168 | ``` 169 | add.js 170 | ---- ---- ---- 171 | exports.__esModule = false; 172 | const x = 6, y = 8; 173 | export default x + y 174 | 175 | ---- ---- ---- 176 | 177 | file.js 178 | ---- ---- ---- 179 | 180 | const add = require('./add'); 181 | 182 | // 调用时须 add.default(),若为true,则 add(); 183 | 184 | // 如果使用import,则不新增default。 185 | 186 | import add from './add'; 187 | add(); // 14; 188 | ---- ---- ---- 189 | 190 | ``` 191 | 192 | 193 | 194 | 4、更多参考资料: 195 | 196 | - babel实战:https://cnodejs.org/topic/565c65c4b31692e827fdd00c 197 | - babel+webpack+react0.14示例:https://github.com/ruanyf/react-babel-webpack-boilerplate 198 | - react-webpack-babel: https://github.com/alicoding/react-webpack-babel 199 | - koa-react-full-example:https://github.com/dozoisch/koa-react-full-example 200 | - react如何兼容IE8: https://github.com/xcatliu/react-ie8 201 | 202 | 最后编辑:2015.12.06 203 | 204 | 205 | -------------------------------------------------------------------------------- /Javascript & Node Tests Framework.md: -------------------------------------------------------------------------------- 1 | # javascript & node 前端测试框架 2 | 3 | ## Sinon 4 | 5 | 支持Spy(函数监控&间谍)、Stubs(存根)、mock、Fake Timers等 6 | 7 | - [Sinon官网](http://sinonjs.org/) 8 | - [Unit Test like a Secret Agent with Sinon.js](http://www.elijahmanor.com/unit-test-like-a-secret-agent-with-sinon-js/) 9 | 10 | ## Jasmine 11 | 12 | 13 | [Jasmine官网](http://jasmine.github.io/2.2/introduction.html) 14 | 15 | ``` 16 | expect(a).toBe(); // 相等 17 | expect(false).not.toBe(true); // 不相等 18 | expect(a).toEqual(12); // 值相等(如对象) 19 | expect("foo bar baz").toMatch("bar"); // 正则匹配 20 | expect("foo bar baz").not.toMatch(/quux/); // 正则不匹配 21 | expect(a.foo).toBeDefined(); // 已定义变量 22 | expect(a.bar).not.toBeDefined(); //未定义变量 23 | expect(a.foo).not.toBeUndefined(); //值是未定义 24 | expect(a.bar).toBeUndefined(); //值不是未定义 25 | expect(a).toBeNull(); // 是空值 26 | expect(foo).not.toBeNull(); // 不是空值 27 | expect(foo).toBeTruthy(); // 是布尔值true 28 | expect(a).not.toBeTruthy(); // 不是布尔值 29 | expect(a).toBeFalsy(); // 是布尔值假值 30 | expect(foo).not.toBeFalsy(); // 非布尔值假值 31 | expect(a).toContain("bar"); // 数组包含 bar 32 | expect(a).not.toContain("quux"); // 数组不包含 bar 33 | expect(e).toBeLessThan(pi); // e小于pi 34 | expect(pi).not.toBeLessThan(e); // pi 不小于 e 35 | expect(pi).toBeGreaterThan(e); // pi 大于 e 36 | expect(e).not.toBeGreaterThan(pi); // e 不大于 pi 37 | expect(foo).not.toThrow(); // 函数 不抛出异常 38 | expect(bar).toThrow(); // 函数抛出异常 39 | expect(foo).toThrowError("foo bar baz"); // new TypeError('foo bar baz') 40 | expect(foo).toThrowError(/bar/); // 函数是否包含特定的异常 41 | expect(foo).toThrowError(TypeError); 42 | expect(foo).toThrowError(TypeError, "foo bar baz"); 43 | expect(foo).toEqual(1); // 函数是否包含值 44 | expect(true).toEqual(true); 45 | beforeEach(function() { // 函数调用之前(安装前)初始 46 | foo += 1; 47 | }); 48 | 49 | afterEach(function() { // 函数调用之后(卸载) 50 | foo = 0; 51 | }); 52 | beforeAll(function() {// 函数全局调用之前(安装前) 53 | foo = 1; 54 | }); 55 | afterAll(function() {// 函数全局调用之后(卸载) 56 | 57 | foo = 0; 58 | }); 59 | ``` 60 | 61 | ## Mocha should 62 | 63 | [Mocha官网](http://mochajs.org/) 64 | 65 | ``` 66 | 同步CODE: 67 | describe('Array', function() { 68 | describe('#indexOf()', function() { 69 | it('should return -1 when the value is not present', function() { 70 | [1,2,3].indexOf(5).should.equal(-1); 71 | [1,2,3].indexOf(0).should.equal(-1); 72 | }); 73 | }); 74 | }); 75 | 76 | 异步CODE: 77 | describe('User', function() { 78 | describe('#save()', function() { 79 | it('should save without error', function(done) { 80 | var user = new User('Luna'); 81 | user.save(function(err) { 82 | if (err) throw err; 83 | done(); 84 | }); 85 | }); 86 | }); 87 | }); 88 | 89 | 也可以用done实现接收一个error 90 | describe('User', function() { 91 | describe('#save()', function() { 92 | it('should save without error', function(done) { 93 | var user = new User('Luna'); 94 | user.save(done); 95 | }); 96 | }); 97 | }); 98 | 99 | should.js: http://shouldjs.github.io/#assertion-iterator 100 | expect.js: https://github.com/mjackson/expect 101 | 102 | 103 | ``` 104 | 105 | [Should](https://github.com/tj/should.js) 106 | 107 | ## supertest 108 | 支持HTTP测试 109 | 110 | [supertest官网](https://github.com/visionmedia/supertest) 111 | 112 | ``` 113 | 不使用任何测试框架: 114 | var request = require('supertest') 115 | , express = require('express'); 116 | 117 | var app = express(); 118 | 119 | app.get('/user', function(req, res){ 120 | res.send(200, { name: 'tobi' }); 121 | }); 122 | 123 | request(app) 124 | .get('/user') 125 | .expect('Content-Type', /json/) 126 | .expect('Content-Length', '20') 127 | .expect(200) 128 | .end(function(err, res){ 129 | if (err) throw err; 130 | }); 131 | 132 | 使用mocha框架: 133 | describe('GET /user', function(){ 134 | it('respond with json', function(done){ 135 | request(app) 136 | .get('/user') 137 | .set('Accept', 'application/json') 138 | .expect('Content-Type', /json/) 139 | .expect(200, done); 140 | }) 141 | }) 142 | 143 | 失败断言: 144 | describe('GET /users', function(){ 145 | it('respond with json', function(done){ 146 | request(app) 147 | .get('/user') 148 | .set('Accept', 'application/json') 149 | .expect(200) 150 | .end(function(err, res){ 151 | if (err) return done(err); 152 | done(); 153 | }); 154 | }); 155 | }); 156 | 157 | 预期断言: 158 | describe('GET /user', function(){ 159 | it('user.name should be an case-insensitive match for "tobi"', function(done){ 160 | request(app) 161 | .get('/user') 162 | .set('Accept', 'application/json') 163 | .expect(function(res) { 164 | res.body.id = 'some fixed id'; 165 | res.body.name = res.body.name.toUpperCase(); 166 | }) 167 | .expect(200, { 168 | id: 'some fixed id', 169 | name: 'TOBI' 170 | }, done); 171 | }); 172 | }); 173 | 174 | 多文件上传断言: 175 | request(app) 176 | .post('/') 177 | .field('name', 'my awesome avatar') 178 | .attach('avatar', 'test/fixtures/homeboy.jpg') 179 | ... 180 | 181 | 182 | ``` 183 | 184 | ## Karma 185 | 186 | 自动化测试工具 187 | 188 | [官网](http://karma-runner.github.io/) 189 | 190 | ## mock 191 | 192 | [官网](https://github.com/nuysoft/Mock) 193 | -------------------------------------------------------------------------------- /github.md: -------------------------------------------------------------------------------- 1 | # Github 相关 2 | 3 | ### 扩展 4 | 5 | Octotree Sourcegraph 6 | 7 | 8 | ### github Star 9 | 10 | - http://gitconstellation.com 11 | - https://www.gitrep.com 12 | - https://gitter.im/home 13 | 14 | ### gitlab issue 规范 15 | 16 | Labels 17 | 18 | ``` 19 | - bug 错误 20 | - confirmed 证实 21 | - critical 关键 22 | - discussion 讨论 23 | - documentation 文档 24 | - enhancement 增强 25 | - feature 特点 26 | - suggestion 建议 27 | - support 支持 28 | ``` 29 | 30 | 31 | #git 分支 32 | 33 | - `git merge branchName`: 将branchName合并到当前分支(快速合并) 34 | - `git merge —on-ff -m ‘commit text’ branchName`: 将branchName合并到当前分支(禁用快速合并) 35 | 36 | - `git branch` 查看本地分支 37 | - `git branch -a` 查看远程分支 38 | - `git push origin branchName` 创建远程分支(将本地branchName推送到远程) 39 | - `git checkout -b branchName` 40 | - `git branch origin :branchName` 删除远程分支branchName 41 | - `git checkout -b branchName` 创建本地分支 42 | - `git checkout --track origin/dev` 切换到远程dev分支 43 | - `git stash` 保存当前工作现场(即git status是干净的) 44 | - `git stash list` 工作现场list 45 | - `git stash pop` 恢复工作现场,同时从stash删除该工作现场 46 | - `git stash apply` 恢复现场 47 | - `git stash drop` 删除现场 48 | 49 | 安全 50 | - `git fetch origin remote:local` 远程下载远程分支到本地 51 | - `git diff diff local` 对比 52 | - `git merge local` 合并 53 | 54 | - `git diff HEAD — filename ` 查看暂存区内容比较 55 | - `git checkout — filename` 丢弃工作区某个文件的修改 56 | - `git reset HEAD filename` add后进入暂存区,该文件从暂存区退回工作区 57 | 58 | 只能回退本地库,远程库无效 59 | HEAD 指当前版本 HEAD^ 上一版本 60 | - `git rest —hard` HEAD 回退上一版本 61 | - `git rest —hard commitid` 回退到commitId这个版本 62 | - `git reflog` 查看历史操作命令 方便回退到未来的版本 63 | - `git rm` 删除 64 | 65 | - `git tag` 打版本标签 66 | 67 | 68 | ### 更多命令 69 | 70 | ``` 71 | git branch 查看本地所有分支 72 | git status 查看当前状态 73 | git commit 提交 74 | git branch -a 查看所有的分支 75 | git branch -r 查看远程所有分支 76 | git commit -am "init" 提交并且加注释 77 | git remote add origin git@192.168.1.119:ndshow 78 | git push origin master 将文件给推到服务器上 79 | git remote show origin 显示远程库origin里的资源 80 | git push origin master:develop 81 | git push origin master:hb-dev 将本地库与服务器上的库进行关联 82 | git checkout --track origin/dev 切换到远程dev分支 83 | git branch -D master develop 删除本地库develop 84 | git checkout -b dev 建立一个新的本地分支dev 85 | git merge origin/dev 将分支dev与当前分支进行合并 86 | git checkout dev 切换到本地dev分支 87 | git remote show 查看远程库 88 | git add . 89 | git rm 文件名(包括路径) 从git中删除指定文件 90 | git clone git://github.com/schacon/grit.git 从服务器上将代码给拉下来 91 | git config --list 看所有用户 92 | git ls-files 看已经被提交的 93 | git rm [file name] 删除一个文件 94 | git commit -a 提交当前repos的所有的改变 95 | git add [file name] 添加一个文件到git index 96 | git commit -v 当你用-v参数的时候可以看commit的差异 97 | git commit -m "This is the message describing the commit" 添加commit信息 98 | git commit -a -a是代表add,把所有的change加到git index里然后再commit 99 | git commit -a -v 一般提交命令 100 | git log 看你commit的日志 101 | git diff 查看尚未暂存的更新 102 | git rm a.a 移除文件(从暂存区和工作区中删除) 103 | git rm --cached a.a 移除文件(只从暂存区中删除) 104 | git commit -m "remove" 移除文件(从Git中删除) 105 | git rm -f a.a 强行移除修改后文件(从暂存区和工作区中删除) 106 | git diff --cached 或 $ git diff --staged 查看尚未提交的更新 107 | git stash push 将文件给push到一个临时空间中 108 | git stash pop 将文件从临时空间pop下来 109 | --------------------------------------------------------- 110 | git remote add origin git@github.com:username/Hello-World.git 111 | git push origin master 将本地项目给提交到服务器中 112 | ----------------------------------------------------------- 113 | git pull 本地与服务器端同步 114 | ----------------------------------------------------------------- 115 | git push (远程仓库名) (分支名) 将本地分支推送到服务器上去。 116 | git push origin serverfix:awesomebranch 117 | ------------------------------------------------------------------ 118 | git fetch 相当于是从远程获取最新版本到本地,不会自动merge 119 | git commit -a -m "log_message" (-a是提交所有改动,-m是加入log信息) 本地修改同步至服务器端 : 120 | I 121 | ``` 122 | 123 | # Git Submodule 124 | 125 | ``` 126 | // 添加子模块 127 | git submodule add git://github.com/felixge/node-mysql.git deps/mysql 128 | // 获取依赖模块 129 | git submodule init 130 | // 更新依赖模块 131 | $ git submodule update 132 | // 获取 status 状态 133 | git submodule status 134 | // recursive 选项 自动初始化并更新仓库中的每一个子模块 135 | git clone --recursive https://github.com/chaconinc/MainProject 136 | // remote Git 将会进入子模块然后抓取并更新 137 | git submodule update --remote 138 | // -merge 服务器上的这个子模块有一个改动并且它被合并了进来 139 | git submodule update --remote --merge 140 | // rebase 本地做了更改时上游也有一个改动,我们需要将它并入本地。 141 | git submodule update --remote --rebase 142 | // 推送到主项目前检查所有子模块是否已推送 143 | git push --recurse-submodules=check 144 | // 尝试 推送子模块 那个子模块因为某些原因推送失败,主项目也会推送失败 145 | git push --recurse-submodules=on-demand 146 | // 在每一个子模块中运行任意命令 147 | git submodule foreach 'git checkout -b featureA' 148 | git submodule foreach 'git stash' 149 | git submodule foreach 'git diff' 150 | ``` 151 | 152 | 153 | ## SSHKEY生成 154 | 155 | > 生成一个SSHKEY 156 | 157 | ``` 158 | ssh-keygen -t rsa -C "xx@ooo.com" 159 | ``` 160 | > 会提示输入生成KEY文件的位置,一般我们都是多个sshkey,所以需要输入完整的路径,同时加_gitname。如 xx公司,则id_rsa_xx 161 | 162 | > Enter file in which to save the key (/Users/moruhang/.ssh/id_rsa) 163 | 164 | ``` 165 | /Users/yourname/.ssh/id_rsa_xx 166 | ``` 167 | * 剩下就可以一路回车,之后查看,显示密钥串 168 | 169 | ``` 170 | cat ~/.ssh/id_rsa_xx.pub 171 | ``` 172 | > 密钥串会显示类似下面的内容,请复制它们到gitlab或者github的SSHKEY里创建 173 | 174 | ``` 175 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDmXVj4KCtMbvW17UUw+Yef2HjMoIrzpSV5RwRiEMFJtBArAzTRxY12OadNhJCKDJ4W1SKWK3Ji3pyX/Gm+MW/4flbAwmHNmilI8aSIXryy0MzHj9JKPrTJmXrdVGpncIiH8DdAaCOrZRmrBkH/54EkBcGev2xpbZJWpO23iLRJciu1UlaOESw8ertqokF5m4b+lYTQmk+pMBkNXYCa0qJxZsc7QkaPHmYsC2mAd/YitkdH43zxmtrHwzRAEieIYQsQZdpxjSWHlpykrj7dWKSHSACaHSWQrVSpjCkRBy8DJ5QAH39CUu8XVSr3O8P4+nv6oMPZJC4j3xIiZHIJMxw9 xx@ooo.com 176 | 177 | ``` 178 | > 在~/.ssh/下创建一个config, 如下 179 | 180 | ``` 181 | # gitlab 182 | Host gitlab.oooo.org 183 | HostName gitlab.oooo.org 184 | IdentityFile ~/.ssh/id_rsa_xx 185 | 186 | # github 187 | Host github.com 188 | HostName github.com 189 | IdentityFile ~/.ssh/id_rsa_github 190 | 191 | ``` 192 | > 验证是否成功 193 | 194 | ``` 195 | // xx@ooo.com 为你的帐号 196 | ssh -t xx@ooo.com 197 | ``` 198 | ### 参考文档: 199 | 200 | git submodule: https://git-scm.com/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E5%AD%90%E6%A8%A1%E5%9D%97 201 | -------------------------------------------------------------------------------- /rxjs.md: -------------------------------------------------------------------------------- 1 | # RxJs 学习笔记 2 | 3 | # 概述 4 | 5 | 事件总线、点击事件都是异步流。开发者可以观测这些异步流,并调用特定的逻辑对它们进行处理。使用Reactive如同开挂:你可以创建点击、悬停之类的任意流。通常流廉价(点击一下就出来一个)而无处不在,种类丰富多样:变量,用户输入,属性,缓存,数据结构等等都可以产生流。举例来说:微博回文(译者注:比如你关注的微博更新了)和点击事件都是流:你可以监听流并调用特定的逻辑对它们进行处理。 6 | 7 | 基于流的概念,Reactive赋予了你一系列神奇的函数工具集,使用他们可以合并、创建、过滤这些流。 一个流或者一系列流可以作为另一个流的输入。你可以_合并_ 8 | 两个流,从一堆流中_过滤_你真正感兴趣的那一些,将值从一个流_映射_到另一个流。 9 | 10 | 流是包含了有时序,正在进行事件的序列,可以发射(emmit)值(某种类型)、错误、完成信号。流在包含按钮的浏览器窗口被关闭时发出完成信号。 11 | 12 | 我们异步地捕获发射的事件,定义一系列函数在值被发射后,在错误被发射后,在完成信号被发射后执行。有时,我们忽略对错误,完成信号地处理,仅仅关注对值的处理。对流进行监听,通常称为订阅,处理流的函数是观测者,流是被观测的主体。这就是观测者设计模式。 13 | 14 | 15 | 16 | 中文简述:http://hao.jser.com/archive/9081/?utm_source=tuicool&utm_medium=referral 17 | RxJava概念描述:http://gank.io/post/560e15be2dca930e00da1083?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io 18 | 19 | ## Observable:可观察者,即被观察者 20 | > 暴露异步和基于事件的推动基于数据源,可观测序列可观测的抽象,代表一个数据源可以观察到,同时推送数据到感兴趣的队列(推送到Observer)。 21 | > 可创建或查询Observeable序列 22 | 23 | ``` 24 | // 推送订阅消息 25 | Observable.prototype.subscribe = observer => { ... } 26 | // observer: { onNext: Function onError: Function onCompleted: function } 27 | ``` 28 | #### Cold HOT 描述 29 | http://tankcong.com/2015/12/02/Cold-Hot-Observables/ 30 | 31 | - Cold Observable:冷序列订阅的值是不共享的,被订阅时才被动触发。(异步) 32 | - HOT Observable:热序列订阅的值是在用户间共享的,主动发送到订阅,不管是否有订阅(同步)。也不管订阅何时开始,持续发送订阅。就如一场足球直播循环,第10分钟进入,就从第10分钟看起 33 | 34 | ### 函数介绍 35 | 36 | #### Rx.Observable.create 37 | > 创建一个可观测序列(Observable),并提供订阅方法,返回observer 38 | 39 | ``` 40 | var source = Rx.Observable.create(observer => { 41 | // Yield a single value and complete 42 | observer.onNext(42); 43 | observer.onCompleted(); 44 | 45 | // Any cleanup logic might go here 46 | return () => console.log('disposed') 47 | }); 48 | 49 | var subscription = source.subscribe( 50 | x => console.log('onNext: %s', x), 51 | e => console.log('onError: %s', e), 52 | () => console.log('onCompleted')); 53 | 54 | // => onNext: 42 55 | // => onCompleted 56 | 57 | subscription.dispose(); 58 | 59 | ``` 60 | 61 | 62 | 63 | ## Observer:观察者 64 | > 代表一个观察者寄存器订阅一个感兴趣的人,观察者支持Next/Error/Complete 65 | 66 | ``` 67 | // 提供当前的通知值的函数 68 | Observer.prototype.onNext = value => { ... }; 69 | // 提供一个错误处理函数 70 | Observer.prototype.onError = error => { ... }; 71 | // 提供一个完成发送函数 72 | Observer.prototype.onCompleted = () => { ... }; 73 | ``` 74 | ### 函数介绍 75 | 76 | #### 创建一个observer 77 | 78 | ``` 79 | // Creates an observable sequence of 5 integers, starting from 1 80 | var source = Rx.Observable.range(1, 5); 81 | 82 | // Create observer 83 | var observer = Rx.Observer.create( 84 | x => console.log('onNext: %s', x), 85 | e => console.log('onError: %s', e), 86 | () => console.log('onCompleted')); 87 | 88 | // Prints out each item 89 | var subscription = source.subscribe(observer); 90 | 91 | // => onNext: 1 92 | // => onNext: 2 93 | // => onNext: 3 94 | // => onNext: 4 95 | // => onNext: 5 96 | // => onCompleted 97 | ``` 98 | 99 | ## Subjects 100 | 101 | > 继承于 Observable 和 Observer, 均可使用它们的方法 102 | 103 | - AsyncSubject: oncomplete通知前的最后一个值或错误收到通过OnError,发送给所有订阅的观察家。 104 | - BehaviorSubject:代表一个值随着时间变化而改变。 105 | - ReplaySubject:参数如果为2,则返回缓存最后2条数据 106 | - subject: 107 | 108 | ``` 109 | // Every second 110 | var source = Rx.Observable.interval(1000); 111 | 112 | var subject = new Rx.Subject(); 113 | 114 | var subSource = source.subscribe(subject); 115 | 116 | var subSubject1 = subject.subscribe( 117 | x => console.log('Value published to observer #1: ' + x), 118 | e => console.log('onError: ' + e.message), 119 | () => console.log('onCompleted')); 120 | 121 | var subSubject2 = subject.subscribe( 122 | x => console.log('Value published to observer #2: ' + x), 123 | e => console.log('onError: ' + e.message), 124 | () => console.log('onCompleted')); 125 | 126 | setTimeout(() => { 127 | // Clean up 128 | subject.onCompleted(); 129 | subSubject1.dispose(); 130 | subSubject2.dispose(); 131 | }, 5000); 132 | 133 | // => Value published to observer #1: 0 134 | // => Value published to observer #2: 0 135 | // => Value published to observer #1: 1 136 | // => Value published to observer #2: 1 137 | // => Value published to observer #1: 2 138 | // => Value published to observer #2: 2 139 | // => Value published to observer #1: 3 140 | // => Value published to observer #2: 3 141 | // => onCompleted 142 | // => onCompleted 143 | ``` 144 | 145 | ## 运算符分类 146 | 147 | ### @ 创建 observable sequence 148 | ##### Rx.Observable.create(subscribe) 149 | 150 | 创建一个流 151 | 152 | ##### Rx.Observable.defer(observableFactory) 153 | 154 | 创建一个工厂函数流(流嵌套流) 155 | 156 | ##### Rx.Observable.defer(observableFactory) 157 | 158 | 创建一个工厂函数流(流嵌套流) 159 | 160 | ### @ 将事件或异步模式转换为可观测序列(observable sequence) 161 | - from 162 | - fromArray 163 | - fromCallback 164 | - fromNodeCallback 165 | - fromEvent 166 | - fromEventPattern 167 | - fromPromise 168 | - of 169 | - toArray 170 | - toMap 171 | - toPromise 172 | - toSet 173 | 174 | ### @ 合并多个Observable为单个Observable(observable sequence) 175 | 176 | - amb:返回最快的序列 177 | - combineLatest(...args, [resultSelector]):合并多个source,如果不指定resultSelecotr则返回一个数组列表 178 | - concat:合并流,例如concat(a,b),则将b的结果追加到a后面 179 | - startWith:往流前面添加值,类似于before 180 | - merge:合并流。例如merge([1,2,3],[4,5,6])=[1,4,2,5,3,6]。按时序合并 181 | - mergeAll: 合并多个流为一个流 182 | - repeat 183 | - withLatestFrom:与combineLatest类似,区别在于A.widthLastestFrom(B),CombineLatest(A,B) 184 | - zip: 合并多个流的值,提供resultSelector来返回结果 185 | - scan:累计 186 | - pairs: 将对象转成数组 187 | 188 | ### @ 基于时间的运算符 189 | 190 | - throttle: 返回指定时间内第一项的反射值 191 | - debounce: 忽略指定时间呢的操作 192 | - delay: 延迟 193 | - interval 194 | - timer:延时多久后执后事件,第2个参数如果指定,则代表每隔第2参数执行一次,第3个参数为超时时间,例如timer(5, 100) 即5秒后,每隔100秒执行一次 195 | - sample: 间隔几秒(参数可为Observable) 196 | - timeout:设置一个超时时间,如果超时则返回错误 197 | - timestamp:返回的对象新增时间戳 198 | 199 | ### @ 过滤和选择值的观察序列 200 | - concatMap/selectConcat:类似于map,返回一个Observable序列的映射。能保证顺序,最终使用concat来合并 201 | 202 | ``` 203 | var source = Rx.Observable.range(0, 5) 204 | .concatMap(function (x, i) { 205 | return Rx.Observable 206 | .interval(100) 207 | .take(x).map(function() { return i; }); 208 | }); 209 | 210 | ``` 211 | - filter/where: 过滤 212 | - flatMap/selectMany: 和concatMap类似,建议采用flatMap。相当于先map再mergeAll。最终使用merge合并 213 | - flatMapLatest: 返回最近一个的序列,如time A,time B。则返回time A 214 | - skip:跳过上一条返回的指定几条数量数据观察流,只返回剩余的流,即跳过前几条,返回后几条 215 | - skipLast:反向跳过上一条返回指定的几条数量数据观察流,即跳过后几条,返回前几条 216 | - skipUntil: 跳过指定Observable触发的值 217 | - skipWhile: 跳过指定条件 218 | - take: 保留前几项 219 | - takeLast:保留最后一项 220 | - takeWhile: 保留指定条件指定项 221 | - takeUntil: 保留指定Observable触发的值 222 | 223 | ### @ 分组 224 | 225 | - buffer: 指定observable指定分组 226 | 227 | ### @命令 228 | - do: 执行一次observer 229 | - doWhile: 执行条件 230 | 231 | ### @ 错误操作 232 | 233 | - catch/onErrorResumeNext: 忽略序列中的错误,优选择catch 234 | - finally: 终止事件流 235 | - retry: 重试次数 236 | - retryWhen: 重试条件 237 | 238 | 239 | 240 | ## 核心库 241 | 242 | ### Observable Methods 243 | 244 | #### Rx.Observable.amb(...args) 245 | 246 | 执行时序序列中最快的一条流 247 | 248 | #### Rx.Observable.catch(...args) 249 | 250 | 忽略时序序列中错误的流,继续执行下一个流 251 | 252 | #### Rx.Observable.concat(...args) 253 | 254 | 合并流 `Rx.Observable.concat(source1, source2);` 255 | 256 | #### Rx.Observable.create(subscribe) 257 | 258 | 创建一个流 259 | 260 | #### Rx.Observable.defer(observableFactory) ??? 261 | 262 | 创建一个工厂函数流(流嵌套流) 263 | 264 | #### Rx.Observable.empty() 265 | 266 | 创建空流 267 | 268 | #### Rx.Observable.from(iterable, [mapFn], [thisArg], [scheduler]) 269 | 270 | 通过Array/Set/Map/String 创建一个Observable 序列 271 | 272 | #### Rx.Observable.generate(initialState, condition, iterate, resultSelector, [scheduler]) 273 | 274 | 生成一个以initialState初始值的序列流 275 | 276 | #### Rx.Observable.return(value, [scheduler]) 277 | #### Rx.Observable.just(value, [scheduler]) 278 | 279 | return与just功能一样,返回一个固定值的流 280 | 281 | #### Rx.Observable.merge([scheduler], ...args) 282 | 283 | 合并流 284 | 285 | #### Rx.Observable.mergeDelayError(...args) 286 | 287 | 合并流,确保流中的错误不影响流的序列,延迟最后显示错误 288 | 289 | #### Rx.Observable.never() 290 | 291 | 返回一个永远不会被终止的流 292 | 293 | #### Rx.Observable.of(...args) 294 | 295 | 转换参数为流 296 | 297 | #### Rx.Observable.onErrorResumeNext(...args) 298 | 299 | 无视错误执行数据流,即使有错也不会显示 300 | 301 | #### Rx.Observable.pairs(obj, [scheduler]) 302 | 303 | 将对象转换成数据流,类似of 304 | 305 | #### Rx.Observable.range(start, count, [scheduler]) 306 | 307 | 获得start至count的流 308 | 309 | #### Rx.Observable.repeat(value, [repeatCount], [scheduler]) 310 | 311 | 重复value 312 | 313 | #### Rx.Observable.throw(exception, [scheduler]) 314 | 315 | #### Rx.Observable.throwError(exception, [scheduler]) 316 | 317 | #### Rx.Observable.throwException(exception, [scheduler]) 318 | 319 | 返回异常错误流 320 | 321 | #### Rx.Observable.zip(...args, [resultSelector]) 322 | 323 | 合并指定的序列,通过resultSelector更改合并后的结果 324 | 325 | 326 | # 示例: 327 | 328 | #### 获取点击次数: 329 | 330 | http://jsfiddle.net/staltz/4gGgs/27/ 331 | 332 | 使用buffer/merge/throttle 333 | 334 | ### 合并流 335 | http://jsfiddle.net/staltz/8jFJH/48/ 336 | 337 | combineLatest/merge/startWith -------------------------------------------------------------------------------- /docker.md: -------------------------------------------------------------------------------- 1 | # Docker 2 | 3 | ## 简介 4 | 5 | ### Docker Toolbox 6 | 7 | > [ToolBox](https://www.docker.com/products/docker-toolbox): Windows/OSX 下 Docker 环境及工具的安装包,提供以下工具的安装: 8 | 9 | - Docker Client 10 | - Docker Machine 11 | - Docker Compose (Mac only) 12 | - Docker Kitematic 13 | - VirtualBox 14 | 15 | ### Kitematic 16 | > [Kitematic](https://docs.docker.com/kitematic/userguide/) 是 docker GUI工具,可视化创建、启动、删除、数据卷等 17 | 18 | ### Machine 19 | > [Machine](https://docs.docker.com/machine/) 解决的是操作系统异构安装Docker困难的问题,没有Machine的时候,CentOS是一种,Ubuntu又是一种,AWS又是一种。有了Machine,所有的系统都是一样的安装方式。 20 | 21 | ### Compose 22 | > [Compose](https://docs.docker.com/compose/) 解决多容器部署的问题,替代Fig,不支持Windows安装。 23 | 24 | ### Swarm 25 | > [Swarm](https://docs.docker.com/swarm/) 解决Docker集群和调度问题 26 | 27 | ## 镜像 28 | 29 | ### 获取镜像 30 | 31 | CMD: `docker pull name[:tag]` 32 | 33 | 示例: 34 | 35 | ``` 36 | // 下载最新版本的镜像,不指定则从registery.hub.docker.com下载 37 | docker pull ubuntu 38 | // 特定版本 39 | docker pull ubuntu:14.04 40 | // 特定仓库 41 | docker pull dl.dockerpool.com:500/ubuntu 42 | ``` 43 | 44 | ### 查看镜像 45 | 46 | CMD: `docker images` 47 | 48 | ### 给镜像打标签 49 | 50 | CMD: `docker tag source targetName[:Tag]` 51 | 52 | 示例: 53 | 54 | ``` 55 | docker tag dl.dockerpool.com:5000/ubuntu:latest ubuntu:latest 56 | ``` 57 | ### 获取镜像元数据 58 | 59 | CMD: `docker inspect [ImageID]` 60 | 61 | 示例:https://hacpai.com/article/1427784659823 62 | 63 | ``` 64 | // 获取某镜像ID的元数据 65 | docker inspect 550degag 66 | // 获取IP地址 67 | docker inspect -f {{.IPAddress}} 68 | // -f是模板 69 | docker inspect -f '{{if ne 0.0 .State.ExitCode }}{{.Name}} {{.State.ExitCode}}{{ end }}' $(docker ps -aq) 70 | // 返回结果: 71 | // /tender_colden 1 72 | // /clever_mcclintock 126 73 | 74 | /grave_bartik 1 75 | ``` 76 | 77 | ### 搜寻镜像 78 | 79 | CMD: `docker search [team]` 80 | 81 | 参数: 82 | 83 | - --automated=false 仅显示自动创建的IMAGE 84 | - --no-trunc=false 输出不截断显示 85 | - -s, --stars=0 仅显示星级评价以上的IMAGE 86 | 87 | 示例: 88 | 89 | ``` 90 | docker search mysql 91 | ``` 92 | 93 | ### 删除镜像 94 | 95 | > 只删除该镜像最新的标签,若只剩下一个标签,则删除整个标签 96 | 97 | CMD: `docker rmi [镜像名/镜像ID]` 98 | 99 | 参数: 100 | 101 | - -f 强制删除镜像(不推荐) 102 | 103 | 示例: 104 | 105 | ``` 106 | docker rmi dl.dockerpool.com:5000/ubuntu 107 | docker rm $(docker ps -q -a) 一次性删除所有的容器 108 | docker rmi $(docker images -q) 一次性删除所有的镜像。 109 | ``` 110 | 111 | ### 查看镜像所有容器 112 | 113 | `docker ps -a` 114 | 115 | ### 创建镜像 116 | 117 | > 镜像创建的三种方法 118 | 119 | - docker commit 120 | - docker build 121 | - DockerFile 122 | 123 | #### Docker Commit 124 | > 基于原有的镜像创建新的容器。当容器运行时,发生改变,可以exit容器,提交镜像 125 | 126 | ``` 127 | // aoct/apache2: 创建的镜像名 / 614122c0aabb: 基于的容器ID 128 | docker commit 614122c0aabb aoct/apache2 129 | // -m 为注释, --author为作者 130 | docker commit -m='A new image' --author='Aomine' 614122c0aabb aoct/apache2 131 | ``` 132 | 133 | ### 提交镜像 134 | 135 | > 设置push的仓库和位置 136 | 137 | `docker push user/test:latest` 138 | 139 | ### 镜像导入与导出 140 | 141 | ``` 142 | // 镜像导入 143 | docker load < node.tar 144 | // 镜像导出 145 | docker save > node.tar 146 | ``` 147 | 148 | ## 容器 149 | 150 | 151 | 参数: 152 | 153 | - -t 启动一个bash终端 154 | - -i 让容器标准输入保持OPEN状态 155 | - -d 守护状态运行(即在后台运行) 156 | - -p 5000:5000 设置宿主端口:容器端口的映射 157 | - -P 指定一个容器端口(随机分配) 158 | - -v source:dockerTarget 设置数据映射到本地 159 | - --name web 指定容器的名称为web 160 | 161 | ``` 162 | // OSX 容器路径映射 163 | docker run -v /Users/:/ ... 164 | // WINDOW 容器路径映射 c:\Users 165 | docker run -v /c/Users/:/ 166 | ``` 167 | 168 | ``` 169 | // 创建一个停止状态的容器 170 | docker create -it ubuntu:lastest 171 | // 创建一个新容并启动一个容器并运行代码 172 | docker run -it ubuntu /bin/echo 'hello world' 173 | // 获取容器ce5输出信息 174 | docker logs ce5 175 | // 终止容器ce5 176 | docker stop ce5 177 | // 终止容器10秒后发信息到容器 178 | docker stop ce5 --time=10 179 | // 查看终止的容器 180 | docker ps -a -q 181 | // 启动容器 182 | docker start ce5 183 | // 重启容器 184 | docker restart ce5 185 | // 进入容器,会阻塞所有窗体的进程 186 | docker attach ce5 187 | // 进入容器,并启动一个命令 188 | docker exec -ti 243c32535da7 /bin/bash 189 | // 删除终止容器 190 | docker rm ce5 191 | // 删除容器 192 | // -v, --volumes=false 删除挂载的数据卷 193 | // -f, --force=false 强行删除运行中的容器 194 | // -l, --link=false 删除容器连接,但保留容器 195 | docker kill ce5 196 | // 导出容器 197 | docker export ce5 > ce5.tar 198 | // 导入容器 会丢失所有历史和元数据 199 | cat ubuntu.tar | sudo docker import - test/ubuntu:v1.0 200 | docker import http://example.com/exampleimage.tgz example/imagerepo 201 | // 登录仓库 202 | docker login 203 | // 创建私有仓库 204 | docker run -d -p 5000:5000 registry 205 | docker run -d -p 5000:5000 -v /opt/data/registry:/tmp/registry registry 206 | ``` 207 | 208 | ### 数据容量 209 | 210 | 深入理解docker volume: 211 | 212 | - http://dockone.io/article/128 213 | - http://dockone.io/article/129 214 | 215 | 假如挂载的时候,不指定宿主机的地址,则自动挂载到/var/lib/docker/vfs/dir,以下是容器/data挂到本地 216 | 217 | ##### 如果挂载的主机卷非本地卷,即sys(mac),则有不会成功挂载到主机上 218 | 219 | `docker run -it -v /data ce5` 220 | 221 | ``` 222 | // 创建一个数据卷容器,并设置数据内容存储在容器的/dbData里 223 | docker run -it -v /dbData --name dbdata ubuntu 224 | // --volumes-from 挂载dbdata,启动容器时,会找到dbdata这个目录 225 | docker run -it --volumes-from dbdata --name db1 ubuntu 226 | docker run -it --volumes-from dbdata --name db2 ubuntu 227 | // 也可以从db1位置挂载到db3 228 | docker run -d --name db3 --volumes-from db1 ce5 229 | docker rm -v dbdata 删除指定数据卷。 230 | ``` 231 | 232 | ### 端口 233 | 234 | ip:hostPort:containerPort 235 | ip::containerPort 236 | hostPort:containerPort 237 | 238 | ``` 239 | // 查看ce5 5000的映射端口 240 | docker port ce5 5000 241 | 242 | docker run -it -p 127.0.0.0:5000:5000 243 | docker run -it -p 127.0.0.0::5000 244 | docker run -it -p 5000:5000 245 | ``` 246 | 247 | ### 容器间通信 248 | 249 | --name web: 自定义名称 250 | 251 | ``` 252 | // 容器终止后自动删除容器 253 | docker run --rm -ti ce5 254 | ``` 255 | 256 | ### 容器连接 257 | > 使用 `--link` 参数,可以让容器之间安全的交互 258 | 259 | ``` 260 | // 使用方法: name 代表要链接的容器名,alias是这个链接的别名 261 | --link name:alias 262 | // 示例 启动容器,让db连接到web,同时执行 env 263 | docker run --link db:db -P -d --name=web webapp ecnode env 264 | 265 | ``` 266 | - docker会通过两种方式公开连接信息 267 | - 自动添加hosts文件信息到父容器,即db连接到web,则提供 268 | `172.17.0.5 db` 269 | - 添加以`DB_`开头的的环境变量,通过`env`访问 270 | 271 | ``` 272 | DB_NAME=/web2/db 273 | DB_PORT.... 274 | .... 275 | ``` 276 | 277 | ## Docker node_modules 保存到镜像中 278 | 279 | - NODE_PATH 280 | - Ln -s 软链接 281 | - `node_modules` 安装到code的父级,如 282 | app/node_modules 283 | app/src/code 284 | - add . /src -> npm i -> docker -v . src 285 | 286 | 现成示例: 287 | 288 | - https://github.com/b00giZm/docker-compose-nodejs-examples 289 | 290 | - NPM & Docker: Sharing volumes: http://www.saulshanabrook.com/npm-docker-sharing-volumes/ 291 | 292 | - [rapid local development vagrant docker node](http://kevzettler.com/programming/2015/06/07/rapid_local_development_vagrant_docker_node.html) 293 | 294 | - http://codeflippa.com/questions/30043872/docker-compose-node-modules-not-present-in-a-volume-after-npm-install-succeeds 295 | 296 | - http://stackoverflow.com/questions/30925521/node-js-docker-compose-node-modules-disappears 297 | 298 | 299 | ## Docker Machine 300 | 301 | ### 使用的先决条件(二选一) 302 | 303 | - 安装[virtualBox](https://www.virtualbox.org/wiki/Downloads) 304 | - 通过安装[toolBox](https://www.docker.com/products/docker-toolbox),将自动安装virtualBox. 305 | 306 | ``` 307 | // 查看HOST 308 | docker-machine ls 309 | // 创建一个default的host 310 | docker-machine create --driver virtualbox default 311 | // 查看ec-crm环境配置 312 | docker-machine env ec-crm 313 | // 连接到ec-crm的shell 314 | eval "$(docker-machine env ec-crm)" 315 | // 停止 316 | docker-machine stop ec-crm 317 | // 启动 318 | docker-machine start ec-crm 319 | // machine间文件传输 320 | docker-machine scp [machine:][path] [machine:][path] 321 | // 更多命令 322 | - `docker-machine config` 323 | - `docker-machine env` 324 | - `docker-machine inspect` 325 | - `docker-machine ip` 326 | - `docker-machine kill` 327 | - `docker-machine provision` 328 | - `docker-machine regenerate-certs` 329 | - `docker-machine restart` 330 | - `docker-machine ssh` 331 | - `docker-machine rm` 332 | - `docker-machine start` 333 | - `docker-machine status` 334 | - `docker-machine stop` 335 | - `docker-machine upgrade` 336 | - `docker-machine url` 337 | ``` 338 | 339 | ### Docker Compose 340 | 341 | - http://dockone.io/article/332 342 | - http://debugo.com/docker-compose/ 343 | 344 | 安装: 345 | 346 | - `curl -L https://github.com/docker/compose/releases/download/1.6.2/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose` 347 | - $ `chmod +x /usr/local/bin/docker-compose` 348 | 349 | ``` 350 | 351 | build 构建或重建服务 352 | help 命令帮助 353 | kill 杀掉容器 354 | logs 显示容器的输出内容 355 | port 打印绑定的开放端口 356 | ps 显示容器 357 | pull 拉取服务镜像 358 | restart 重启服务 359 | rm 删除停止的容器 360 | run 运行一个一次性命令 361 | scale 设置服务的容器数目 362 | start 开启服务 363 | stop 停止服务 364 | up 创建并启动容器 365 | 366 | Usage: 367 | docker-compose [-f=...] [options] [COMMAND] [ARGS...] 368 | docker-compose -h|--help 369 | 370 | Options: 371 | -f, --file FILE Specify an alternate compose file (default: docker-compose.yml) 372 | -p, --project-name NAME Specify an alternate project name (default: directory name) 373 | --verbose Show more output 374 | -v, --version Print version and exit 375 | 376 | Commands: 377 | build Build or rebuild services 378 | config Validate and view the compose file 379 | create Create services 380 | down Stop and remove containers, networks, images, and volumes 381 | events Receive real time events from containers 382 | help Get help on a command 383 | kill Kill containers 384 | logs View output from containers 385 | pause Pause services 386 | port Print the public port for a port binding 387 | ps List containers 388 | pull Pulls service images 389 | restart Restart services 390 | rm Remove stopped containers 391 | run Run a one-off command 392 | scale Set number of containers for a service 393 | start Start services 394 | stop Stop services 395 | unpause Unpause services 396 | up Create and start containers 397 | version Show the Docker-Compose version information 398 | 399 | ``` 400 | -------------------------------------------------------------------------------- /FP Javscript Study.md: -------------------------------------------------------------------------------- 1 | # 前言 2 | 3 | 本文是Javascript函数式编程指南的学习总结及知识点,讲的是JS编程的另一种范式函数式编程。 4 | 5 | 6 | ## 我们在做什么: 7 | 8 | 简单的示例: 9 | 10 | ``` 11 | // 原有代码 12 | add(multiply(flock_b, add(flock_a, flock_c)), multiply(flock_a, flock_b)); 13 | 14 | // 应用同一律,去掉多余的加法操作(add(flock_a, flock_c) == flock_a) 15 | add(multiply(flock_b, flock_a), multiply(flock_a, flock_b)); 16 | 17 | // 再应用分配律 18 | multiply(flock_b, add(flock_a, flock_a)); 19 | 20 | ``` 21 | 22 | ## 函数是一等公民 23 | 24 | ``` 25 | // 太傻了 26 | var getServerStuff = function(callback){ 27 | return ajaxCall(function(json){ 28 | return callback(json); 29 | }); 30 | }; 31 | 32 | // 这才像样 33 | var getServerStuff = ajaxCall; 34 | 35 | ``` 36 | 37 | ### 纯函数 38 | 39 | > 纯函数是这样一种函数,即相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用。 40 | 41 | 副作用可能包括... 42 | 43 | - 更改文件系统 44 | - 往数据库插入记录 45 | - 发送一个 http 请求 46 | - 可变数据 47 | - 打印/log 48 | - 获取用户输入 49 | - DOM 查询 50 | - 访问系统状态 51 | - 函数内部变量引用外部变量导致不可预料的变化 52 | 53 | 追求纯函数 54 | 55 | 可缓存性(Cacheable) 56 | 57 | ``` 58 | var memoize = function(f) { 59 | var cache = {}; 60 | 61 | return function() { 62 | var arg_str = JSON.stringify(arguments); 63 | cache[arg_str] = cache[arg_str] || f.apply(f, arguments); 64 | return cache[arg_str]; 65 | }; 66 | }; 67 | // 值得注意的一点是,可以通过延迟执行的方式把不纯的函数转换为纯函数 68 | var pureHttpCall = memoize(function(url, params){ 69 | return function() { return $.getJSON(url, params); } 70 | }); 71 | 72 | ``` 73 | 74 | 可移植性/自文档化(Portable / Self-Documenting) 75 | 76 | > 纯函数的依赖很明确,因此更易于观察和理解. 77 | > 78 | > “面向对象语言的问题是,它们永远都要随身携带那些隐式的环境。你只需要一个香蕉,但却得到一个拿着香蕉的大猩猩...以及整个丛林”。 79 | 80 | ``` 81 | // 不纯的 82 | var signUp = function(attrs) { 83 | var user = saveUser(attrs); 84 | welcomeUser(user); 85 | }; 86 | 87 | // 纯的 88 | var signUp = function(Db, Email, attrs) { 89 | return function() { 90 | var user = saveUser(Db, attrs); 91 | welcomeUser(Email, user); 92 | }; 93 | }; 94 | ``` 95 | 96 | 可测试性(Testable) 97 | 98 | > Quickcheck——一个为函数式环境量身定制的测试工具。 99 | 100 | 合理性(Reasonable) 101 | 102 | > 引用透明性(referential transparency)由于纯函数总是能够根据相同的输入返回相同的输出,所以它们就能够保证总是返回同一个结果,这也就保证了引用透明性 103 | 104 | > “等式推导”:一对一替换 105 | 106 | 并行代码: 107 | 108 | > 可使用webworker,并行执行代码,因为没有共享内存竞争。(考虑非纯函数复杂度不建议采用) 109 | 110 | ## 柯里化 Curry 111 | 112 | ``` 113 | var add = function(x) { 114 | return function(y) { 115 | return x + y; 116 | }; 117 | }; 118 | 119 | var increment = add(1); 120 | var addTen = add(10); 121 | 122 | increment(2); 123 | // 3 124 | 125 | addTen(2); 126 | // 12 127 | 128 | var curry = require('lodash').curry; 129 | 130 | var match = curry(function(what, str) { 131 | return str.match(what); 132 | }); 133 | 134 | match(/\s+/g, "hello world"); 135 | // [ ' ' ] 136 | 137 | match(/\s+/g)("hello world"); 138 | // [ ' ' ] 139 | 140 | var hasSpaces = match(/\s+/g); 141 | // function(x) { return x.match(/\s+/g) } 142 | 143 | hasSpaces("hello world"); 144 | // [ ' ' ] 145 | ``` 146 | 147 | 推荐函数式编程库: 148 | 149 | - https://github.com/lodash/lodash-fp 150 | - http://ramdajs.com/ 151 | 152 | ## Compose 函数组合 153 | 154 | ``` 155 | var compose = function(f,g) { 156 | return function(x) { 157 | return f(g(x)); 158 | }; 159 | }; 160 | // f 和 g 都是函数,x 是在它们之间通过“管道”传输的值。 161 | 162 | var toUpperCase = function(x) { return x.toUpperCase(); }; 163 | var exclaim = function(x) { return x + '!'; }; 164 | var shout = compose(exclaim, toUpperCase); 165 | shout("send in the clowns"); 166 | //=> "SEND IN THE CLOWNS!" 167 | 168 | ``` 169 | 170 | > 结合律,符合结合律意味着不管你是把 g 和 h 分到一组,还是把 f 和 g 分到一组都不重要 171 | 172 | ``` 173 | // 结合律(associativity) 174 | var associative = compose(f, compose(g, h)) == compose(compose(f, g), h); 175 | // true 176 | 177 | compose(toUpperCase, compose(head, reverse)); 178 | // 或者 179 | compose(compose(toUpperCase, head), reverse); 180 | 181 | // map 的组合律 182 | var law = compose(map(f), map(g)) == map(compose(f, g)); 183 | ``` 184 | 185 | > pointfree 模式指的是,永远不必说出你的数据。只关注函数组合(参数),不关注函数输入值 186 | 187 | ``` 188 | // 非 pointfree,因为提到了数据:word 189 | var snakeCase = function (word) { 190 | return word.toLowerCase().replace(/\s+/ig, '_'); 191 | }; 192 | 193 | // pointfree 194 | var snakeCase = compose(replace(/\s+/ig, '_'), toLowerCase); 195 | 196 | snakeCase('word xx') // word_xx 197 | ``` 198 | 199 | > debug 组合的一个常见错误是,在没有局部调用之前,就组合类似 map 这样接受两个参数的函数 200 | 201 | 202 | ``` 203 | var trace = curry(function(tag, x){ 204 | console.log(tag, x); 205 | return x; 206 | }); 207 | 208 | var dasherize = compose(join('-'), toLower, trace("after split"), split(' '), replace(/\s{2,}/ig, ' ')); 209 | // after split [ 'The', 'world', 'is', 'a', 'vampire' ] 210 | ``` 211 | > 范畴学(category theory)是数学中的一个抽象分支,能够形式化诸如集合论(set theory)、类型论(type theory)、群论(group theory)以及逻辑学(logic)等数学分支中的一些概念。范畴学主要处理对象(object)、态射(morphism)和变化式(transformation) 212 | 213 | - 对象的搜集 214 | - 态射的搜集: 态射是标准的、普通的纯函数。 215 | - 态射的组合: compose 函数是符合结合律的组合 216 | - identity 这个独特的态射: 217 | 218 | > 所有的一元函数(unary function)(一元函数:只接受一个参数的函数) f 都成立 219 | 220 | ``` 221 | // identity 222 | compose(id, f) == compose(f, id) == f; 223 | // true 224 | ``` 225 | 226 | > 示例: 227 | 228 | ``` 229 | var images = _.compose(_.map(img), srcs); 230 | var renderImages = _.compose(Impure.setHtml("body"), images); 231 | var app = _.compose(Impure.getJSON(renderImages), url); 232 | 233 | ``` 234 | 235 | ## Hindley-Milner 类型签名 236 | 237 | > 最后一个是返回类型,前2个是接收参数。如下,接收regex,str,返回[string] 238 | 239 | ``` 240 | // match :: Regex -> String -> [String] 241 | var match = curry(function(reg, s){ 242 | return s.match(reg); 243 | }); 244 | 245 | ``` 246 | > 接收一个参数,返回一个函数(接收一个字符,返回一个字符数组) 247 | 248 | ``` 249 | // match :: Regex -> (String -> [String]) 250 | var match = curry(function(reg, s){ 251 | return s.match(reg); 252 | }); 253 | 254 | // onHoliday :: String -> [String] 255 | var onHoliday = match(/holiday/ig); 256 | 257 | // 更复杂推导 258 | // replace :: Regex -> (String -> (String -> String)) 259 | var replace = curry(function(reg, sub, s){ 260 | return s.replace(reg, sub); 261 | }); 262 | ``` 263 | 264 | > 缩小可能性范围 parametricity: 即缩小描述范围 265 | 266 | ``` 267 | // head :: [a] -> a 268 | // reverse :: [a] -> [a] 269 | 270 | ``` 271 | 272 | > 自由定理 free theorems: 即只选择性描述 273 | 274 | ``` 275 | // head :: [a] -> a 276 | compose(f, head) == compose(head, map(f)); 277 | 278 | // filter :: (a -> Bool) -> [a] -> [a] 279 | compose(map(f), filter(compose(p, f))) == compose(filter(p), map(f)); 280 | ``` 281 | > 类型约束 282 | 283 | ``` 284 | // sort :: Ord a => [a] -> [a] 285 | // assertEqual :: (Eq a, Show a) => a -> a -> Assertion 286 | 287 | // 两个约束:Eq 和 Show。它们保证了我们可以检查不同的 a 是否相等,并在有不相等的情况下打印出其中的差异 288 | 289 | ``` 290 | 291 | ## 函数容器与错误处理 292 | 293 | ``` 294 | var Container = function(x) { 295 | this.__value = x; 296 | } 297 | Container.of = function(x) { return new Container(x); }; 298 | ``` 299 | 300 | > functor 是实现了 map 函数并遵守一些特定规则的容器类型。通常我们称它为 Identity 301 | 302 | ``` 303 | // (a -> b) -> Container a -> Container b 304 | Container.prototype.map = function(f){ 305 | return Container.of(f(this.__value)) 306 | } 307 | Container.of(2).map(function(two){ return two + 2 }) 308 | //=> Container(4) 309 | Container.of("flamethrowers").map(function(s){ return s.toUpperCase() }) 310 | //=> Container("FLAMETHROWERS") 311 | Container.of("bombs").map(concat(' away')).map(_.prop('length')) 312 | //=> Container(10) 313 | ``` 314 | > maybe,其实就是第一个参数为值,把值往后面边函数参数传。前提是该值可能传入null等意外值,需对值进行处理 315 | 316 | ``` 317 | // map :: Functor f => (a -> b) -> f a -> f b 318 | var map = curry(function(f, any_functor_at_all) { 319 | return any_functor_at_all.map(f); 320 | }); 321 | 322 | // withdraw :: Number -> Account -> Maybe(Account) 323 | var withdraw = curry(function(amount, account) { 324 | return account.balance >= amount ? 325 | Maybe.of({balance: account.balance - amount}) : 326 | Maybe.of(null); 327 | }); 328 | 329 | var Maybe = function(x) { 330 | this.__value = x; 331 | } 332 | 333 | Maybe.of = function(x) { 334 | return new Maybe(x); 335 | } 336 | 337 | Maybe.prototype.isNothing = function() { 338 | return (this.__value === null || this.__value === undefined); 339 | } 340 | 341 | Maybe.prototype.map = function(f) { 342 | return this.isNothing() ? Maybe.of(null) : Maybe.of(f(this.__value)); 343 | } 344 | 345 | Maybe.of(null).map(match(/a/ig)); 346 | //=> Maybe(null) 347 | 348 | Maybe.of({name: "Boris"}).map(_.prop("age")).map(add(10)); 349 | 350 | ``` 351 | 352 | > 释放容器的值 353 | 354 | ``` 355 | // maybe :: b -> (a -> b) -> Maybe a -> b 356 | var maybe = curry(function(x, f, m) { 357 | return m.isNothing() ? x : f(m.__value); 358 | }); 359 | 360 | // getTwenty :: Account -> String 361 | var getTwenty = compose( 362 | maybe("You're broke!", finishTransaction), withdraw(20) 363 | ); 364 | 365 | getTwenty({ balance: 200.00}); 366 | // "Your balance is $180.00" 367 | 368 | getTwenty({ balance: 10.00}); 369 | // "You're broke!" 370 | ``` 371 | 372 | > 错误处理: 用left来处理错误,用right返回结果 373 | 374 | ``` 375 | Left.prototype.map = function(f) { 376 | // 返回自身,如果是函数名为error,则相当于返回error('xxxx') = left('xxxx') 377 | return this; 378 | } 379 | 380 | Right.prototype.map = function(f) { 381 | return Right.of(f(this.__value)); 382 | } 383 | // getAge :: Date -> User -> Either(String, Number) 384 | var getAge = curry(function(now, user) { 385 | var birthdate = moment(user.birthdate, 'YYYY-MM-DD'); 386 | if(!birthdate.isValid()) return Left.of("Birth date could not be parsed"); 387 | return Right.of(now.diff(birthdate, 'years')); 388 | }); 389 | 390 | getAge(moment(), {birthdate: '2005-12-12'}); 391 | // Right(9) 392 | 393 | getAge(moment(), {birthdate: '20010704'}); 394 | // Left("Birth date could not be parsed") 395 | ``` 396 | 397 | > 通俗点来讲,一个函数在调用的时候,如果被 map 包裹了,那么它就会从一个非 functor 函数转换为一个 functor 函数。我们把这个过程叫做 lift。 398 | 399 | ``` 400 | // either :: (a -> c) -> (b -> c) -> Either a b -> c 401 | var either = curry(function(f, g, e) { 402 | switch(e.constructor) { 403 | case Left: return f(e.__value); 404 | case Right: return g(e.__value); 405 | } 406 | }); 407 | ``` 408 | 409 | > iO functor 410 | 411 | ``` 412 | var IO = function(f) { 413 | this.__value = f; 414 | } 415 | 416 | IO.of = function(x) { 417 | return new IO(function() { 418 | return x; 419 | }); 420 | } 421 | 422 | IO.prototype.map = function(f) { 423 | return new IO(_.compose(f, this.__value)); 424 | } 425 | 426 | // io_window_ :: IO Window 返回 io.__value = function(){ return window } 427 | var io_window = new IO(function(){ return window; }); 428 | 429 | 430 | // this.__value(fun(window)) 431 | io_window.map(function(win){ return win.innerWidth }); 432 | // IO(1430) 433 | // 434 | 435 | io_window.map(_.prop('location')).map(_.prop('href')).map(split('/')); 436 | // IO(["http:", "", "localhost:8000", "blog", "posts"]) 437 | 438 | 439 | // $ :: String -> IO [DOM] 440 | var $ = function(selector) { 441 | return new IO(function(){ return document.querySelectorAll(selector); }); 442 | } 443 | 444 | $('#myDiv').map(head).map(function(div){ return div.innerHTML; }); 445 | // IO('I am some inner html') 446 | ``` 447 | 448 | ## Monad 449 | 450 | > pointed functor 是实现了 of 方法的 functor。“默认最小化上下文”这个术语可能不够精确,但是却很好地传达了这种理念:我们希望容器类型里的任意值都能发生 lift,然后像所有的 functor 那样再 map 出去。 451 | 452 | ``` 453 | O.of("tetris").map(concat(" master")); 454 | // IO("tetris master") 455 | 456 | Maybe.of(1336).map(add(1)); 457 | // Maybe(1337) 458 | 459 | Task.of([{id: 2}, {id: 3}]).map(_.prop('id')); 460 | // Task([2,3]) 461 | 462 | Either.of("The past, present and future walk into a bar...").map( 463 | concat("it was tense.") 464 | ); 465 | // Right("The past, present and future walk into a bar...it was tense.") 466 | ``` 467 | 468 | 469 | ## applicative functor 470 | 471 | ``` 472 | // 这样是行不通的,因为 2 和 3 都藏在瓶子里。 473 | add(Container.of(2), Container.of(3)); 474 | //NaN 475 | 476 | // 使用可靠的 map 函数试试 477 | var container_of_add_2 = map(add, Container.of(2)); 478 | // Container(add(2)) 479 | 480 | Container.prototype.ap = function(other_container) { 481 | return other_container.map(this.__value); 482 | } 483 | 484 | Container.of(add(2)).ap(Container.of(3)); 485 | // Container(5) 486 | 487 | // all together now 488 | Container.of(2).map(add).ap(Container.of(3)); 489 | // Container(5) 490 | 491 | ``` 492 | 493 | 494 | 2015-12-05: Monad functor 实在是太复杂了,冷静后再多看几遍 495 | -------------------------------------------------------------------------------- /less.md: -------------------------------------------------------------------------------- 1 | 2 | ## 编码规范 3 | 4 | ### BEM 规范 5 | > BEM代表块(Block),元素(Element),修饰符(Modifier)。 6 | > [进入相关介绍](http://www.w3cplus.com/css/bem-definitions.html) 7 | 8 | - 块:块代表一个组件,可以简单的或者复合的 9 | - 元素:是块的一部分,代表一个组件的功能,依赖于块中才能够有意义 10 | - 修饰符:代表对块进行修饰的状态样式,例如hover,current,selected等 11 | 12 | **注意:BEM最多只能有B+E+M三级,不能出现 B+E+E+..+E+M 超长class名** 13 | 14 | #### 常见的BEM规则 15 | 16 | ``` 17 | .block {} 18 | .block__element {} 19 | .block--modifier {} 20 | .block-name--element-name {} 21 | .blockName-elementName {} 22 | .blockName-elementName--modifierName {} 23 | .block-name--modifier-name {} 24 | .block-name__element-name {} 25 | .block-name__element-name--modifier-name {} 26 | ``` 27 | 28 | #### LESS的BEM写法 29 | > 例如是菜单项 30 | 31 | ``` 32 | // html 33 | 38 | 39 | // css 40 | .menu { 41 | display: block; 42 | } 43 | 44 | .menu__item { 45 | display: inline-block; 46 | line-height: 30px; 47 | width: 100px; 48 | } 49 | 50 | .menu--horizontal { 51 | width: 100%; 52 | height: 30px; 53 | } 54 | 55 | .menu--vertical { 56 | width: 100px; 57 | height: 100%; 58 | } 59 | 60 | .menu--horizontal .menu__item { 61 | border-right: 1px solid #e5e5e5; 62 | text-align: center; 63 | } 64 | 65 | .menu--vertical .menu__item { 66 | border-bottom: 1px solid #e5e5e5; 67 | text-align: left; 68 | } 69 | 70 | // less 71 | .menu { 72 | display: block; 73 | 74 | &__item { 75 | display: inline-block; 76 | line-height: 30px; 77 | width: 100px; 78 | } 79 | 80 | &--horizontal { 81 | width: 100%; 82 | height: 30px; 83 | } 84 | 85 | &--vertical { 86 | width: 100px; 87 | height: 100%; 88 | } 89 | } 90 | ``` 91 | 92 | ## 组件规范 93 | 94 | ### 按钮规范 95 | > 根据常见的按钮尺寸、类型、状态等规范样式,规则: button-element 96 | 97 | - 尺寸: 98 | - 大号按钮(btn-large) 99 | - 中号默认按钮(btn) 100 | - 小号按钮(btn-small) 101 | - 类型 102 | - 默认(btn) 103 | - 首选(btn-primary) 104 | - 成功(btn-success) 105 | - 一般信息(btn-info) 106 | - 警告(btn-warning) 107 | - 危险(btn-danger) 108 | - 链接(btn-link) 109 | - 虚线按钮(btn-dashed) 110 | - 幽灵按钮(btn-ghost) 111 | - 加载按钮(btn-loading) 112 | - 组合按钮 113 | btn-group 114 | - 状态 115 | - 图标按钮(btn-icon-only) 116 | - 图文混合按钮(btn-icon): btn-icon-left(左) btn-icon-right(右) 117 | - 激活(btn-active) 118 | - 禁用(btn-disabled) 119 | 120 | ### 图片规范 121 | > 根据常见的图片布局方式、形状等规范样式,规则: img--modifier 122 | 123 | - 布局方式 124 | - 响应式:(img-responsive) 125 | 126 | - 形状 127 | - 圆角: (img-rounded) 128 | - 圆形:(img-circle) 129 | - 缩略图:(img-thumbnail) 130 | 131 | ### 导航条规范 132 | > 根据常见的导航条形状、颜色等规范样式,规则: nav--modifier 133 | 134 | - 形状 135 | - 默认(nav) 136 | - 胶囊式 (nav--pills) 137 | - 垂直胶囊式 (nav--stacked) 138 | - 两端对齐 (nav--justified) 139 | - 下拉菜单 (nav--tabs) 140 | 141 | - 颜色 142 | - 反色(nav--inverse) 143 | 144 | ### 分页规范 145 | > 根据常见的分页尺寸、功能等规范样式,规则: pagination--modifier 146 | 147 | - 尺寸 148 | - 默认(pagination) 149 | - 大(pagination--lg) 150 | - 小(pagination--sm) 151 | - 功能 152 | - 当前(pagination--active) 153 | - 不可用(pagination--disabled) 154 | 155 | ### 进度条规范 156 | > 根据常见的进度条状态、样式等规范样式,规则: progress--modifier 157 | 158 | - 情景 159 | - 默认(progress-bar) 160 | - 成功(progress-bar-success) 161 | - 一般信息(progress-bar-info) 162 | - 警告(progress-bar-warning) 163 | - 危险(progress-bar-danger) 164 | - 样式 165 | - 条纹(progress-bar-striped) 166 | - 动画(progress-bar-striped_active) 167 | 168 | ### 表格 169 | > 根据常见的表格样式,状态等规范样式,规则:table--modifier 170 | 171 | - 样式 172 | - 条纹(table--striped) 173 | - 边框(table--bordered) 174 | - 紧缩(table--condensed) 175 | - 状态 176 | - 鼠标悬停当前(table--active) 177 | - 标识成功或积极的动作(table--success) 178 | - 标识普通的提醒信息的动作(table--info) 179 | - 标识警告或者需要用户注意的动作(table--warning) 180 | - 标识危险或者潜在负面影响的动作(table--danger) 181 | 182 | ### 级联选择 183 | > 根据常见的级联选择样式,状态等规范样式,规则:cascader--modifier 184 | 185 | - 样式 186 | - 默认(cascader--input) 187 | - 下拉列表(cascader--menu) 188 | - 下拉项(cascader-menu-item) 189 | - 状态 190 | - 禁用(cascader-menu-item-disabled) 191 | 192 | ### Checkbox多选框 193 | > 根据常见的Checkbox样式,状态等规范样式,规则:checkbox--modifier 194 | 195 | - 样式 196 | - 默认(checkbox--input) 197 | - Checkbox列表(checkbox-group-item) 198 | - 状态 199 | - 禁用(checkbox--disabled) 200 | - 激活(checkbox--checked) 201 | 202 | ### 日期选择框 203 | > 根据常见的celendar样式,状态等规范样式,规则:celendar--modifier 204 | 205 | - 样式 206 | - 默认(calendar-range-picker) 207 | - 大 (input--lg) 208 | - 小 (input--sm) 209 | - 状态 210 | - 禁用(calendar--disabled) 211 | 212 | ### Input 输入框 213 | > 根据常见的input样式规范样式,规则:input--modifier 214 | 215 | - 样式 216 | - 大(input--lg) 217 | - 默认(input) 218 | - 小(input--sm) 219 | 220 | ### Radio 单选框 221 | > 根据常见的Radio样式,状态等规范样式,规则:radio--modifier 222 | 223 | - 样式 224 | - 默认(radio--input) 225 | - Radio组(radio--group) 226 | - 状态 227 | - 禁用(radio--disabled) 228 | - 激活(radio--checked) 229 | 230 | ## 项目结构 231 | 232 | ### 基本结构 233 | 234 | - config:变量(字体、字体大小、色值等) 235 | - fonts:字体文件 236 | - iconfont:淘宝图标字体 237 | - fontAwesome:Bootstrap图标字体 238 | - lib:外部或第三方样式 239 | - mixins:通用LESS函数库(区分PC、移动端样式) 240 | - modules:模块/页面 241 | - component:组件 242 | 243 | ### 通用组件 244 | 245 | - buttons: 按钮 246 | - form: 表单 247 | - tables:表格 248 | - tabs: 选项卡 249 | - caret:三角 250 | - close: 关闭按钮 251 | - switch: switch 开关 252 | - badge:徽章 253 | - progress:进度条 254 | - modal:弹层 255 | - checkbox:复选框 256 | - dropbox:下拉框 257 | - calendar:日历 258 | - tips:提示(包含正确、错误提示、确认提示等) 259 | - ... 260 | 261 | ### 模块组件 262 | 263 | - member-selector: 部门选择器 264 | - filter-groups: 筛选组 265 | - tip:提示模块:确认提示、选择提示 266 | - talbe:基础表格模块:支持排序、筛选、自定义表头、设置默认展示等 267 | - label:标签模块:包括筛选、设置标签 268 | - switch:视图切换模块 269 | - time:时间筛选项:更新时间 270 | - pagination:分页模块 271 | - ... 272 | 273 | ### 页面组件 274 | 275 | - page-report: 日报 276 | - page-index: 首页 277 | - ... 278 | 279 | ### 通用函数(mixins) 280 | 281 | - size: 设置元素的width/height/line-height的样式 282 | - reset-filter: 重置(取消)滤镜 283 | - Absolute-Center:IE8+ 元素重直居中(绝对定位) 284 | - center-block: 水平居中 285 | - clearfix:清除浮动 286 | - radius: 圆角 287 | - rotate|scale: 变形 288 | - animate:动画 289 | - text-overflow:截断文本 290 | - box-shadow:阴影 291 | - text-shadow:文字阴影 292 | - border:边框 293 | - ... 294 | 295 | ## LESS 特性 296 | > http://lesscss.cn/features/ 297 | 298 | ### 变量(Variables) 299 | 300 | ``` 301 | // Variables 302 | @link-color: #428bca; // sea blue 303 | @link-color-hover: darken(@link-color, 10%); 304 | 305 | // 选择器 306 | @my-selector: banner; 307 | 308 | .@{my-selector} { 309 | font-weight: bold; 310 | line-height: 40px; 311 | margin: 0 auto; 312 | } 313 | 314 | // URLs 315 | @images: "../img"; 316 | 317 | body { 318 | color: #444; 319 | background: url("@{images}/white-sand.png"); 320 | } 321 | 322 | // Import 323 | @themes: "../../src/themes"; 324 | @import "@{themes}/tidal-wave.less"; 325 | 326 | // 属性 327 | @property: color; 328 | 329 | .widget { 330 | @{property}: #0ee; 331 | background-@{property}: #999; 332 | } 333 | ``` 334 | 335 | ### 继承(Extend) 336 | 337 | ``` 338 | nav ul { 339 | &:extend(.inline); 340 | background: blue; 341 | } 342 | 343 | 344 | .big-division, 345 | .big-bag:extend(.bag), 346 | .big-bucket:extend(.bucket) { 347 | // body 348 | } 349 | ``` 350 | 351 | ### 混合 352 | #### 无参 353 | ``` 354 | .border { 355 | border: 1px soid pink; 356 | } 357 | 358 | .box { 359 | .border; 360 | } 361 | ``` 362 | ==> 363 | ```css 364 | .box { 365 | border: 1px soid pink; 366 | } 367 | ``` 368 | 369 | #### 带参数 370 | ```css 371 | .border_02(@border_width) { 372 | border: @border_width solid yellow; 373 | } 374 | .border_03(@border_width:30px) { 375 | border: @border_width solid yellow; 376 | } 377 | 378 | .boxer { 379 | .border_02(20px); 380 | } 381 | .boxer_02 { 382 | .border_03(); 383 | } 384 | ``` 385 | ==> 386 | ``` 387 | .boxer { 388 | border: 20px solid yellow; 389 | } 390 | .boxer_02 { 391 | border: 30px solid yellow; 392 | } 393 | ``` 394 | 395 | ### 匹配 396 | ```css 397 | .triangle(bottom,@w: 5px,@c: #ccc){ 398 | border-width: @w; 399 | border-color: @c transparent transparent transparent; 400 | border-style: solid dashed dashed dashed; 401 | } 402 | .triangle(top,@w: 5px,@c: #ccc){ 403 | border-width: @w; 404 | border-color: transparent transparent @c transparent; 405 | border-style: dashed dashed solid dashed; 406 | } 407 | .triangle(left,@w: 5px,@c: #ccc){ 408 | border-width: @w; 409 | border-color: transparent @c transparent transparent; 410 | border-style: dashed solid dashed dashed; 411 | } 412 | .triangle(right,@w: 5px,@c: #ccc){ 413 | border-width: @w; 414 | border-color: transparent transparent transparent @c; 415 | border-style: dashed dashed dashed solid; 416 | } 417 | .sanjiao { 418 | .triangle(right,10px,#000); 419 | } 420 | 421 | ``` 422 | ==> 423 | ```css 424 | .sanjiao { 425 | border-width: 10px; 426 | border-color: transparent transparent transparent #000000; 427 | border-style: dashed dashed dashed solid; 428 | width: 0; 429 | height: 0; 430 | overflow: hidden; 431 | } 432 | ``` 433 | 434 | ### 运算 435 | ```css 436 | @test-1: 300px; 437 | .box-02{ 438 | background-color: #eee - 10; 439 | width: @test-1 + 200; 440 | height: @test-1 + 100; 441 | } 442 | ``` 443 | ==> 444 | 445 | ```css 446 | .box-02 { 447 | background-color: #e4e4e4; 448 | width: 500px; 449 | height: 400px; 450 | } 451 | ``` 452 | 453 | 454 | ## LESS 常用函数 455 | > http://lesscss.cn/functions/ 456 | 457 | ### 杂项函数 458 | 459 | ``` 460 | // image-size: 获取图片尺寸 461 | Example: image-size("file.png"); 462 | Output: 10px 10px 463 | 464 | // image-width / image-height 465 | Example: image-width("file.png"); 466 | Example: image-height("file.png"); 467 | 468 | // convert:转换单位 469 | Example: 470 | convert(9s, "ms") 471 | convert(14cm, mm) 472 | convert(8, mm) // incompatible unit types 473 | Output: 474 | 9000ms 475 | 140mm 476 | 8 477 | ``` 478 | 479 | ### 列表函数 480 | 481 | ``` 482 | // length: 获取数组长度 483 | @list: "banana", "tomato", "potato", "peach"; 484 | n: length(@list); // 4 485 | 486 | ``` 487 | 488 | ### 类型函数 489 | 490 | ``` 491 | // ispixel:判断是否px单位 492 | Example: 493 | ispixel(#ff0); // false 494 | ispixel(blue); // false 495 | ispixel("string"); // false 496 | ispixel(1234); // false 497 | ispixel(56px); // true 498 | ispixel(7.8%); // false 499 | ispixel(keyword); // false 500 | ispixel(url(...)); // false 501 | 502 | // isem: 判断是否em单位 503 | Example: 504 | isem(#ff0); // false 505 | isem(blue); // false 506 | isem("string"); // false 507 | isem(1234); // false 508 | isem(56px); // false 509 | isem(7.8em); // true 510 | isem(keyword); // false 511 | isem(url(...)); // false 512 | 513 | // ispercentage: 判断是否为比例值 514 | Example: 515 | ispercentage(#ff0); // false 516 | ispercentage(blue); // false 517 | ispercentage("string"); // false 518 | ispercentage(1234); // false 519 | ispercentage(56px); // false 520 | ispercentage(7.8%); // true 521 | ispercentage(keyword); // false 522 | ispercentage(url(...)); // false 523 | 524 | ``` 525 | 526 | ### 颜色定义函数 527 | 528 | ``` 529 | // rgb :转化十六进制颜色值 530 | Example: rgb(90, 129, 32) 531 | Output: #5a8120 532 | ``` 533 | 534 | ### 颜色操作函数 535 | ``` 536 | //mix: 混合颜色 537 | Example:mix(#ff0000, #0000ff, 50%) 538 | Output: #800080 539 | 540 | ``` 541 | 542 | ## 书写规范 543 | - 颜色 544 | - 建议用小写十六进制,可以缩写,不用‘red’来表示颜色 545 | - 字体 546 | - 建议用单引号表示字体 547 | Example:a { font-family: 'Times New Roman', 'Times' } 548 | - 文字粗细 549 | - 建议用合适的数字或者名字书写 550 | Example: 551 | a { font-weight: bold;} 552 | a { font-weight: 200;} 553 | - 数字 554 | - 建议去掉小数点前面的"0" 555 | Example:a {line-height: .5px;} 556 | - 建议小数点后面最多三位数 557 | - 建议值为0的单位默认缺省 558 | Example: a {line-height: 0;} 559 | - 字符串 560 | - 建议默认属性值引号用双引号(“”) 561 | Example: a { content: "x"; } 562 | - 时间 563 | - 建议动画和过度时期少于100ms 564 | - 单位 565 | - 建议单位书写格式为小写 566 | - 缩写 567 | - 建议尽量缩写你的CSS 568 | Example:margin,padding,border-color,border-radius,border-style,border-width... 569 | - 声明 570 | - 建议尽量少用!important属性值 571 | - 建议属性后面直接跟着":",加一个空格然后加上属性值 572 | - 建议一行声明一个属性,并用";"表示结束 573 | - 不允许空属性的样式 574 | - 不允许属性重复声明 575 | - 不允许声明无效的属性 576 | Example: 577 | ``` css 578 | display: inline used with width, height, margin, margin-top, margin-bottom, and float. 579 | display: inline-block used with float. 580 | display: list-item used with vertical-align. 581 | display: block used with vertical-align. 582 | display: flex used with vertical-align. 583 | display: table used with vertical-align. 584 | display: table-* used with margin (and all variants) or float. 585 | display: table-* (except table-cell) used with vertical-align. 586 | position: static used with top, right, bottom, and left. 587 | position: absolute used with float or vertical-align. 588 | position: fixed used with float or vertical-align. 589 | float: left and float: right used with vertical-align. 590 | ``` 591 | 592 | ## 参考链接 593 | 594 | - [A New Front-End Methodology: BEM](https://www.smashingmagazine.com/2012/04/a-new-front-end-methodology-bem/) 595 | - [html-inspector](https://github.com/philipwalton/html-inspector/blob/master/src/rules/convention/bem-conventions.js#L1-L27) 596 | - [suitcss](http://suitcss.github.io/) 597 | - [A BEM syntax with UX in mind](http://montagestudio.com/blog/2013/10/24/BEM-syntax-with-ux-in-mind/) 598 | - [BEM定义](http://www.w3cplus.com/css/bem-definitions.html) 599 | - [ANT DESIGN](http://ant.design/#/components/button) -------------------------------------------------------------------------------- /test.md: -------------------------------------------------------------------------------- 1 | # MacDown 2 | 3 | ![MacDown logo](http://macdown.uranusjr.com/static/base/img/logo-160.png) 4 | 5 | Hello there! I’m **MacDown**, the open source Markdown editor for OS X. 6 | 7 | Let me introduce myself. 8 | 9 | 10 | 11 | ## Markdown and I 12 | 13 | **Markdown** is a plain text formatting syntax created by John Gruber, aiming to provide a easy-to-read and feasible markup. The original Markdown syntax specification can be found [here](http://daringfireball.net/projects/markdown/syntax). 14 | 15 | **MacDown** is created as a simple-to-use editor for Markdown documents. I render your Markdown contents real-time into HTML, and display them in a preview panel. 16 | 17 | ![MacDown Screenshot](http://d.pr/i/10UGP+) 18 | 19 | I support all the original Markdown syntaxes. But I can do so much more! Various popular but non-standard syntaxes can be turned on/off from the [**Markdown** preference pane](#markdown-pane). 20 | 21 | You can specify extra HTML rendering options through the [**Rendering** preference pane](#rendering-pane). 22 | 23 | You can customize the editor window to you liking in the [**Editor** preferences pane](#editor-pane): 24 | 25 | You can configure various application (that's me!) behaviors in the [**General** preference pane](#general-pane). 26 | 27 | ## The Basics 28 | Before I tell you about all the extra syntaxes and capabilities I have, I'll introduce you to the basics of standard markdown. If you already know markdown, and want to jump straight to learning about the fancier things I can do, I suggest you skip to the [**Markdown** preference pane](#markdown-pane). Lets jump right in. 29 | 30 | ### Line Breaks 31 | To force a line break, put two spaces and a newline (return) at the end of the line. 32 | 33 | These lines 34 | won't break 35 | 36 | These lines 37 | will break 38 | 39 | 40 | ### Strong and Emphasize 41 | 42 | **Strong**: `**Strong**` or `__Strong__` (Command-B) 43 | *Emphasize*: `*Emphasize*` or `_Emphasize_`[^emphasize] (Command-I) 44 | 45 | ### Headers (like this one!) 46 | 47 | Header 1 48 | ======== 49 | 50 | Header 2 51 | -------- 52 | 53 | or 54 | 55 | # Header 1 56 | ## Header 2 57 | ### Header 3 58 | #### Header 4 59 | ##### Header 5 60 | ###### Header 6 61 | 62 | 63 | 64 | ### Links and Email 65 | #### Inline 66 | Just put angle brackets around an email and it becomes clickable: 67 | `` 68 | 69 | Same thing with urls: 70 | ` ` 71 | 72 | Perhaps you want to some link text like this: [Macdown Website](http://macdown.uranusjr.com "Title") 73 | `[Macdown Website](http://macdown.uranusjr.com "Title")` (The title is optional) 74 | 75 | 76 | #### Reference style 77 | Sometimes it looks too messy to include big long urls inline, or you want to keep all your urls together. 78 | 79 | Make [a link][arbitrary_id] `[a link][arbitrary_id]` then on it's own line anywhere else in the file: 80 | `[arbitrary_id]: http://macdown.uranusjr.com "Title"` 81 | 82 | If the link text itself would make a good id, you can link [like this][] `[like this][]`, then on it's own line anywhere else in the file: 83 | `[like this]: http://macdown.uranusjr.com` 84 | 85 | [arbitrary_id]: http://macdown.uranusjr.com "Title" 86 | [like this]: http://macdown.uranusjr.com 87 | 88 | 89 | ### Images 90 | #### Inline 91 | `![Alt Image Text](path/or/url/to.jpg "Optional Title")` 92 | #### Reference style 93 | `![Alt Image Text][image-id]` 94 | on it's own line elsewhere: 95 | `[image-id]: path/or/url/to.jpg "Optional Title"` 96 | 97 | 98 | ### Lists 99 | 100 | * Lists must be preceded by a blank line (or block element) 101 | * Unordered lists start each item with a `*` 102 | - `-` works too 103 | * Indent a level to make a nested list 104 | 1. Ordered lists are supported. 105 | 2. Start each item (number-period-space) like `1. ` 106 | 3. It doesn't matter what number you use, I will render them sequentially 107 | 4. So you might want to start each line with `1.` and let me sort it out 108 | 109 | Here is the code: 110 | 111 | ``` 112 | * Lists must be preceded by a blank line (or block element) 113 | * Unordered lists start each item with a `*` 114 | - `-` works too 115 | * Indent a level to make a nested list 116 | 1. Ordered lists are supported. 117 | 2. Start each item (number-period-space) like `1. ` 118 | 42. It doesn't matter what number you use, I will render them sequentially 119 | 1. So you might want to start each line with `1.` and let me sort it out 120 | ``` 121 | 122 | 123 | 124 | ### Block Quote 125 | 126 | > Angle brackets `>` are used for block quotes. 127 | Technically not every line needs to start with a `>` as long as 128 | there are no empty lines between paragraphs. 129 | > Looks kinda ugly though. 130 | > > Block quotes can be nested. 131 | > > > Multiple Levels 132 | > 133 | > Most markdown syntaxes work inside block quotes. 134 | > 135 | > * Lists 136 | > * [Links][arbitrary_id] 137 | > * Etc. 138 | 139 | Here is the code: 140 | 141 | ``` 142 | > Angle brackets `>` are used for block quotes. 143 | Technically not every line needs to start with a `>` as long as 144 | there are no empty lines between paragraphs. 145 | > Looks kinda ugly though. 146 | > > Block quotes can be nested. 147 | > > > Multiple Levels 148 | > 149 | > Most markdown syntaxes work inside block quotes. 150 | > 151 | > * Lists 152 | > * [Links][arbitrary_id] 153 | > * Etc. 154 | ``` 155 | || *Year* || *Temperature (low)* || *Temperature (high)* || 156 | || 1900 || -10 || 25 || 157 | || 1910 || -15 || 30 || 158 | || 1920 || -10 || 32 || 159 | 160 | ### Inline Code 161 | `Inline code` is indicated by surrounding it with backticks: 162 | `` `Inline code` `` 163 | 164 | If your ``code has `backticks` `` that need to be displayed, you can use double backticks: 165 | ```` ``Code with `backticks` `` ```` (mind the spaces preceding the final set of backticks) 166 | 167 | 168 | ### Block Code 169 | If you indent at least four spaces or one tab, I'll display a code block. 170 | 171 | print('This is a code block') 172 | print('The block must be preceded by a blank line') 173 | print('Then indent at least 4 spaces or 1 tab') 174 | print('Nesting does nothing. Your code is displayed Literally') 175 | 176 | I also know how to do something called [Fenced Code Blocks](#fenced-code-block) which I will tell you about later. 177 | 178 | ### Horizontal Rules 179 | If you type three asterisks `***` or three dashes `---` on a line, I'll display a horizontal rule: 180 | 181 | *** 182 | 183 | 184 | ## The Markdown Preference Pane 185 | This is where I keep all preferences related to how I parse markdown into html. 186 | ![Markdown preferences pane](http://d.pr/i/RQEi+) 187 | 188 | ### Document Formatting 189 | The ***Smartypants*** extension automatically transforms straight quotes (`"` and `'`) in your text into typographer’s quotes (`“`, `”`, `‘`, and `’`) according to the context. Very useful if you’re a typography freak like I am. Quote and Smartypants are syntactically incompatible. If both are enabled, Quote takes precedence. 190 | 191 | 192 | ### Block Formatting 193 | 194 | #### Table 195 | 196 | This is a table: 197 | 198 | First Header | Second Header 199 | ------------- | ------------- 200 | Content Cell | Content Cell 201 | Content Cell | Content Cell 202 | 203 | You can align cell contents with syntax like this: 204 | 205 | | Left Aligned | Center Aligned | Right Aligned | 206 | |:------------- |:---------------:| -------------:| 207 | | col 3 is | some wordy text | $1600 | 208 | | col 2 is | centered | $12 | 209 | | zebra stripes | are neat | $1 | 210 | 211 | The left- and right-most pipes (`|`) are only aesthetic, and can be omitted. The spaces don’t matter, either. Alignment depends solely on `:` marks. 212 | 213 | #### Fenced Code Block 214 | 215 | This is a fenced code block: 216 | 217 | ``` 218 | print ('Hello world!)' 219 | ``` 220 | 221 | You can also use waves (`~`) instead of back ticks (`` ` ``): 222 | 223 | ~~~ 224 | print('Hello world!') 225 | ~~~ 226 | 227 | 228 | You can add an optional language ID at the end of the first line. The language ID will only be used to highlight the code inside if you tick the ***Enable highlighting in code blocks*** option. This is what happens if you enable it: 229 | 230 | ![Syntax highlighting example](http://d.pr/i/9HM6+) 231 | 232 | I support many popular languages as well as some generic syntax descriptions that can be used if your language of choice is not supported. See [relevant sections on the official site](http://macdown.uranusjr.com/features/) for a full list of supported syntaxes. 233 | 234 | 235 | ### Inline Formatting 236 | 237 | The following is a list of optional inline markups supported: 238 | 239 | Option name | Markup | Result if enabled | 240 | --------------------|------------------|-----------------------| 241 | Intra-word emphasis | So A\*maz\*ing | So Amazing | 242 | Strikethrough | \~~Much wow\~~ | Much wow | 243 | Underline [^under] | \_So doge\_ | So doge | 244 | Quote [^quote] | \"Such editor\" | Such editor | 245 | Highlight | \==So good\== | So good | 246 | Superscript | hoge\^(fuga) | hogefuga | 247 | Autolink | http://t.co | | 248 | Footnotes | [\^4] and [\^4]: | [^4] and footnote 4 | 249 | 250 | [^4]: You don't have to use a number. Arbitrary things like `[^footy note4]` and `[^footy note4]:` will also work. But they will *render* as numbered footnotes. Also, no need to keep your footnotes in order, I will sort out the order for you so they appear in the same order they were referenced in the text body. You can even keep some footnotes near where you referenced them, and collect others at the bottom of the file in the traditional place for footnotes. 251 | 252 | 253 | 254 | 255 | ## The Rendering Preference Pane 256 | This is where I keep preferences relating to how I render and style the parsed markdown in the preview window. 257 | ![Rendering preferences pane](http://d.pr/i/rT4d+) 258 | 259 | ### CSS 260 | You can choose different css files for me to use to render your html. You can even customize or add your own custom css files. 261 | 262 | ### Syntax Highlighting 263 | You have already seen how I can syntax highlight your fenced code blocks. See the [Fenced Code Block](#fenced-code-block) section if you haven’t! You can also choose different themes for syntax highlighting. 264 | 265 | ### TeX-like Math Syntax 266 | I can also render TeX-like math syntaxes, if you allow me to.[^math] I can do inline math like this: \\( 1 + 1 \\) or this (in MathML): 1+1, and block math: 267 | 268 | \\[ 269 | A^T_S = B 270 | \\] 271 | 272 | or (in MathML) 273 | 274 | 275 | A S T 276 | = 277 | B 278 | 279 | 280 | 281 | 282 | ### Task List Syntax 283 | 1. [x] I can render checkbox list syntax 284 | * [x] I support nesting 285 | * [x] I support ordered *and* unordered lists 286 | 2. [ ] I don't support clicking checkboxes directly in the html window 287 | 288 | 289 | ### Jekyll front-matter 290 | If you like, I can display Jekyll front-matter in a nice table. Just make sure you put the front-matter at the very beginning of the file, and fence it with `---`. For example: 291 | 292 | ``` 293 | --- 294 | title: "Macdown is my friend" 295 | date: 2014-06-06 20:00:00 296 | --- 297 | ``` 298 | 299 | ### Render newline literally 300 | Normally I require you to put two spaces and a newline (aka return) at the end of a line in order to create a line break. If you like, I can render a newline any time you end a line with a newline. However, if you enable this, markdown that looks lovely when I render it might look pretty funky when you let some *other* program render it. 301 | 302 | 303 | 304 | 305 | 306 | ## The General Preferences Pane 307 | 308 | This is where I keep preferences related to application behavior. 309 | ![General preferences pane](http://d.pr/i/rvwu+) 310 | 311 | The General Preferences Pane allows you to tell me how you want me to behave. For example, do you want me to make sure there is a document open when I launch? You can also tell me if I should constantly update the preview window as you type, or wait for you to hit `command-R` instead. Maybe you prefer your editor window on the right? Or to see the word-count as you type. This is also the place to tell me if you are interested in pre-releases of me, or just want to stick to better-tested official releases. 312 | 313 | ## The Editor Preference Pane 314 | This is where I keep preferences related to the behavior and styling of the editing window. 315 | ![Editor preferences pane](http://d.pr/i/6OL5+) 316 | 317 | 318 | ### Styling 319 | 320 | My editor provides syntax highlighting. You can edit the base font and the coloring/sizing theme. I provided some default themes (courtesy of [Mou](http://mouapp.com)’s creator, Chen Luo) if you don’t know where to start. 321 | 322 | You can also edit, or even add new themes if you want to! Just click the ***Reveal*** button, and start moving things around. Remember to use the correct file extension (`.styles`), though. I’m picky about that. 323 | 324 | I offer auto-completion and other functions to ea**====**se your editing experience. If you don’t like it, however, you can turn them off. 325 | 326 | 327 | 328 | 329 | 330 | ## Hack On 331 | 332 | That’s about it. Thanks for listening. I’ll be quiet from now on (unless there’s an update about the app—I’ll remind you for that!). 333 | 334 | Happy writing! 335 | 336 | 337 | [^emphasize]: If **Underlines** is turned on, `_this notation_` will render as underlined instead of emphasized 338 | 339 | [^under]: If **Underline** is disabled `_this_` will be rendered as *emphasized* instead of being underlined. 340 | 341 | [^quote]: **Quote** replaces literal `"` characters with html `` tags. **Quote** and **Smartypants** are syntactically incompatible. If both are enabled, **Quote** takes precedence. Note that **Quote** is different from *blockquote*, which is part of standard Markdown. 342 | 343 | [^math]: Internet connection required. 344 | 345 | 346 | -------------------------------------------------------------------------------- /haskell_study.md: -------------------------------------------------------------------------------- 1 | # haskell 趣学指南小结 2 | 3 | 安装开发环境(mac): 4 | 5 | ``` $ brew install ghc cabal-install ``` 6 | 7 | 进入haskell REPL: `ghci` 8 | 9 | - 帮助:`:?` 10 | - 载入:`:l [filename]` 11 | - 重载:`:reload` 12 | - 退出: `:quit` 13 | ## 函数 14 | 15 | - 内置函数 16 | 17 | `succ` 值加1, `min` 取最小值, `max` 取最大值 18 | 19 | ``` 20 | ghci> succ 9 + max 5 4 + 1 21 | 16 22 | ghci> (succ 9) + (max 5 4) + 1 23 | 16 24 | ``` 25 | - 创建函数 26 | 27 | 1、新建一个文件`doubleUs.hs`,保存以下内容 28 | 29 | ``` 30 | doubleUs x y = x + x + y + y 31 | ``` 32 | 2、装载该文件 `:l filename` 33 | 34 | ``` 35 | :l doubleUs.hs 36 | 37 | ``` 38 | 3、执行函数 39 | 40 | ``` 41 | ghci> doubleUs 4 9 42 | 26 43 | ghci> doubleUs 2.3 34.2 44 | 73.0 45 | ``` 46 | 4、`if`: `doubleSmallNumber x = (if x > 100 then x else x*2) + 1` 47 | 48 | ## List 49 | 50 | - 定义一个LIST:`let lostNumbers = [4,8,15,16,23,48]` 51 | - 字符串是LIST:`['w','o'] ++ ['o','t'] ` 等于 `woot` 52 | - LIST 可以装LIST:`let b = [[1,2,3,4],[5,3,3,3],[1,2,2,3,4],[1,2,3]]` 53 | - LIST可以比较:`[3,2,1] > [2,10,100] ` 值类型必须是一致的 54 | 55 | 常见LIST函数: 56 | 57 | - head 返回首个元素: `head [5,4,3,2,1]` 等于 `5` 58 | - tail 去掉尾部返回: `tail [5,4,3,2,1]` 等于 `[4,3,2,1]` 59 | - last 返回最后元素: `last [5,4,3,2,1]` 等于 `1` 60 | - init 去掉最后元素: `init [5,4,3,2,1]` 等于 `[5,4,3,2]` 61 | - length 返回长度:`length [3,2,5]` 等于 `3` 62 | - null 非空列表:`null []` 等于 `True` 63 | - reverse 反转列表:`reverse [3,2,1]` 等于 `[1,2,3]` 64 | - take n 取前n个元素:`take 2 [2,3,5,3]` 等于 `[2,3]` 65 | - minimum 最小值:`minimum [8,4,2,1,5,6]` 等于 `1` 66 | - maximum 最大值: `maximum [1,9,2,3,4]`等于 `9` 67 | - sum 求和:`sum [5,2,1,6,3,2,5,7] ` 等于 `31` 68 | - product 求积: `product [6,2,1,2] ` 等于 `24` 69 | - elem 包含:4 ``` `elem` ``` [3,4,5,6] ` 等于 True 70 | 71 | ## Range 72 | 使用`..`返回自然数、字母范围的LIST 73 | 74 | - `[2,4..20]` `[3,6..20]` `['a'..'z']` `['K'..'Z'] ` 75 | - `take 10 (cycle [1,2,3])` `cycle`生成无限`1,2,3`,`take 10` 只返回前10个 76 | - `replicate 3 10` 返回 `[10 10 10]`:返回3个重复的10。`repeat 5` 则返回无限个5 77 | 78 | ## LIST 理解 79 | 80 | `[x*2 | x <- take 20 (repeat 5) , x*2 >= 2]` [返回值 | 条件1, 条件2] 81 | 82 | 定义函数:odd代表是否奇数 83 | 84 | `boomBangs xs = [ if x < 10 then "BOOM!" else "BANG!" | x <- xs, odd x]` 85 | `boomBangs [7..13]` 86 | 87 | 也可以加多个限制条件。若要达到 10 到 20 间所有不等于 13,15 或 19 的数,可以这样 88 | 89 | `[ x | x <- [10..20], x /= 13, x /= 15, x /= 19` 90 | 91 | 多个参数求积: 92 | 93 | `[ x*y | x <- [2,5,10], y <- [8,10,11]]` 94 | 95 | 更多示例: 96 | 97 | ``` 98 | ghci> let nouns = ["hobo","frog","pope"] 99 | ghci> let adjectives = ["lazy","grouchy","scheming"] 100 | ghci> [adjective ++ " " ++ noun | adjective <- adjectives, noun <- nouns] 101 | ["lazy hobo","lazy frog","lazy pope","grouchy hobo","grouchy frog", "grouchy pope","scheming hobo", 102 | "scheming frog","scheming pope"] 103 | ``` 104 | 定义length, `_`代表不在乎返回值 105 | 106 | ``` 107 | length' xs = sum [1 | _ <- xs] 108 | ``` 109 | 110 | 嵌套: 111 | 112 | ``` 113 | ghci> let xxs = [[1,3,5,2,3,1,2,4,5],[1,2,3,4,5,6,7,8,9],[1,2,4,2,1,6,3,1,3,2,3,6]] 114 | ghci> [ [ x | x <- xs, even x ] | xs <- xxs] 115 | 116 | ``` 117 | 118 | ## 元组 Tuple 119 | 120 | > Tuple 则要求你对需要组合的数据的数目非常的明确,它的型别取决于其中项的数目与其各自的型别。 Tuple 中的项由括号括起,并由逗号隔开。 121 | 122 | **以下元素型别一样,但元素数组不一致,也导致错误的。** 123 | 124 | ``` 125 | [(1, 2), (8, 11, 5), (4, 5)] 126 | ``` 127 | **仅允许二元组使用的函数** 128 | 129 | - fst 返回序列首项: `fst (8,11)` 等于 `8` 130 | - snd 返回序列尾项: `snd ("wow", False)` 等于 `False` 131 | 132 | **zip:将两个数组交叉成一组序地的LIST** 133 | 134 | ``` 135 | ghci> zip [1,2,3,4,5] [5,5,5,5,5] 136 | [(1,5),(2,5),(3,5),(4,5),(5,5)] 137 | ghci> zip [1 .. 5] ["one", "two", "three", "four", "five"] 138 | [(1,"one"),(2,"two"),(3,"three"),(4,"four"),(5,"five")] 139 | ghci> zip [1..] ["apple", "orange", "cherry", "mango"] 140 | [(1,"apple"),(2,"orange"),(3,"cherry"),(4,"mango")] 141 | ``` 142 | **它添加一个限制条件,令其必须为直角三角形。同时也考虑上 b 边要短于斜边,a 边要短于 b 边情况** 143 | 144 | ``` 145 | ghci> let rightTriangles' = [ (a,b,c) | c <- [1..10], b <- [1..c], a <- [1..b], a^2 + b^2 == c^2, a+b+c == 24] 146 | ghci> rightTriangles' 147 | [(6,8,10)] 148 | ``` 149 | 150 | ## 类型 151 | `:t` 检测表达式型别。例:`:t 'a'`. 152 | 153 | 同时也可以查询函数的类型,如 `:t fst` 等于 fst :: (a, b) -> a 154 | 155 | `::` 类型推导, 例: 156 | 157 | ``` 158 | removeNonUppercase :: [Char] -> [Char] 159 | removeNonUppercase st = [ c | c <- st, c `elem` ['A'..'Z']] 160 | addThree :: Int -> Int -> Int -> Int 161 | addThree x y z = x + y + z 162 | ``` 163 | **类型** 164 | 165 | - Int :表示整数。Int 是有界的,也就是说它由上限和下限。对 32 位的机器而言,上限一般是 2147483647,下限是 -2147483648。 166 | - Integer: 无界整数,无上限 167 | - Float: 浮点数 168 | - Double: 双精度 169 | - Bool: 布尔 170 | - Char:字符 171 | 172 | ## TypeClasses 173 | 174 | > 函数型别声明 175 | 176 | ``` 177 | ghci> :t (==) 178 | (==) :: (Eq a) => a -> a -> Bool 179 | ``` 180 | 181 | `Eq` 提供判断相等的接口 `==`和`=/`。如`:t (==)` 182 | 183 | `Ord` 提供比较大小的型别. `compare` 函数取两个 `Ord` 类中的相同型别的值作参数,回传比较的结果。这个结果是如下三种型别之一:GT, LT, EQ。 184 | 185 | ``` 186 | ghci> :t (>) 187 | (>) :: (Ord a) => a -> a -> Bool 188 | ghci> "Abrakadabra" < "Zebra" 189 | True 190 | ghci> "Abrakadabra" `compare` "Zebra" 191 | LT 192 | ghci> 5 >= 2 193 | True 194 | ghci> 5 `compare` 3 195 | GT 196 | ``` 197 | `Show`是字符串表示型别。 198 | 199 | ``` 200 | ghci> show 3 201 | "3" 202 | ghci> show 5.334 203 | "5.334" 204 | ``` 205 | `Read` 与 `Show` 相反,将字符串转成对应的型别。同时必须提供两个参数进行运算(其中一个参数必须是字符型) 206 | 207 | ``` 208 | ghci> read "True" || False 209 | True 210 | ghci> read "8.2" + 3.8 211 | 12.0 212 | ghci> read "5" - 2 213 | 3 214 | ghci> read "[1,2,3,4]" ++ [3] 215 | [1,2,3,4,3] 216 | ``` 217 | 218 | `Enum`是枚举型别(即连续内容)。该 Typeclass 包含的型别有:(), Bool, Char, Ordering, Int, Integer, Float 和 Double。 219 | 220 | ``` 221 | ghci> ['a'..'e'] 222 | "abcde" 223 | ghci> [LT .. GT] 224 | [LT,EQ,GT] 225 | ghci> [3 .. 5] 226 | [3,4,5] 227 | ghci> succ 'B' 228 | 'C' 229 | ``` 230 | `Bounded` 的成员都有一个上限和下限。 231 | 232 | ``` 233 | ghci> minBound :: Int 234 | -2147483648 235 | ghci> maxBound :: Char 236 | '\1114111' 237 | ghci> maxBound :: Bool 238 | True 239 | ghci> minBound :: Bool 240 | False 241 | ghci> maxBound :: (Bool, Int, Char) 242 | (True,2147483647,'\1114111') 243 | ``` 244 | 245 | ## 模式匹配 246 | 247 | > 可代替 `if-else` / `switch` 的模式匹配 248 | 249 | 满足表达式条件,否则 x 250 | 251 | ``` 252 | lucky :: (Integral a) => a -> String 253 | lucky 7 = "LUCKY NUMBER SEVEN!" 254 | lucky x = "Sorry, you're out of luck, pal!" 255 | ``` 256 | 257 | 递归 258 | 259 | ``` 260 | factorial :: (Integral a) => a -> a 261 | factorial 0 = 1 262 | factorial n = n * factorial (n - 1) 263 | ``` 264 | Tuple可以使用模式匹配 265 | 266 | ``` 267 | addVectors :: (Num a) => (a, a) -> (a, a) -> (a, a) 268 | addVectors (x1, y1) (x2, y2) = (x1 + x2, y1 + y2) 269 | addVectors (2,3) (3,6) 270 | (5,9) 271 | ``` 272 | List Comprehension 273 | 274 | ``` 275 | ghci> let xs = [(1,3), (4,3), (2,4), (5,3), (5,6), (3,1)] 276 | ghci> [a+b | (a,b) <- xs] 277 | [4,7,6,8,11,4] 278 | ``` 279 | x:xs 该模型匹配应用很广,特别是递归模型. 280 | 281 | 如:(x:_) = (7,8) _仅能代表一个元素,如(1,3,5) 相当于 x=1, _= (3,5) 282 | 假如长度不确定,则(x:y:[])代表(1,2,...) 283 | 284 | 285 | ``` 286 | head' :: [a] -> a 287 | head' [] = error "Can't call head on an empty list, dummy!" 288 | head' (x:_) = x 289 | ghci> head' [4,5,6] 290 | 4 291 | ghci> head' "Hello" 292 | 'H' 293 | ``` 294 | 多个匹配,一般数组最后都会有个[],如果[1,2,3],则一定会是1:2:3:[] 295 | 296 | ``` 297 | tell :: (Show a) => [a] -> String 298 | tell [] = "The list is empty" 299 | tell (x:[]) = "The list has one element: " ++ show x 300 | tell (x:y:[]) = "The list has two elements: " ++ show x ++ " and " ++ show y 301 | tell (x:y:_) = "This list is long. The first two elements are: " ++ show x ++ " and " ++ show y 302 | *Main> tell [] 303 | "The list is empty" 304 | *Main> tell [1] 305 | "The list has one element: 1" 306 | *Main> tell [1,2,3,5,6] 307 | "This list is long. The first two elements are: 1 and 2" 308 | ``` 309 | 310 | 例如 Length, 结果相当于1+(1+(1+0))。 例如 ham = (_:xs) 相当于 匹配 ham 311 | 312 | ``` 313 | length' :: (Num b) => [a] -> b 314 | length' [] = 0 315 | length' (_:xs) = 1 + length' xs 316 | ``` 317 | `as` 模式,就是将一个名字 @置于模式前,如ok@ 则相当于 ok 则保留整体的引用 318 | 319 | ``` 320 | capital :: String -> String 321 | capital "" = "Empty string, whoops!" 322 | capital all@(x:xs) = "The first letter of " ++ all ++ " is " ++ [x] 323 | 324 | ghci> capital "Dracula" 325 | "The first letter of Dracula is D" 326 | ``` 327 | 328 | ## guard 329 | 330 | > 类型于 `if-else`,不过带条件判断 331 | 332 | ``` 333 | bmiTell :: (RealFloat a) => a -> a -> String 334 | bmiTell weight height 335 | | weight / height ^ 2 <= 18.5 = "You're underweight, you emo, you!" 336 | | weight / height ^ 2 <= 25.0 = "You're supposedly normal. Pffft, I bet you're ugly!" 337 | | weight / height ^ 2 <= 30.0 = "You're fat! Lose some weight, fatty!" 338 | | otherwise = "You're a whale, congratulations!" 339 | ``` 340 | 341 | ## 关键字WHERE 342 | 343 | > 将重复的条件用WHERE代替 344 | 345 | **存在重复的条件** 346 | 347 | ``` 348 | bmiTell :: (RealFloat a) => a -> a -> String 349 | bmiTell weight height 350 | | weight / height ^ 2 <= 18.5 = "You're underweight, you emo, you!" 351 | | weight / height ^ 2 <= 25.0 = "You're supposedly normal. Pffft, I bet you're ugly!" 352 | | weight / height ^ 2 <= 30.0 = "You're fat! Lose some weight, fatty!" 353 | | otherwise = "You're a whale, congratulations!" 354 | ``` 355 | 356 | **WHERE解决重复的条件** 357 | 358 | ``` 359 | bmiTell :: (RealFloat a) => a -> a -> String 360 | bmiTell weight height 361 | | bmi <= skinny = "You're underweight, you emo, you!" 362 | | bmi <= normal = "You're supposedly normal. Pffft, I bet you're ugly!" 363 | | bmi <= fat = "You're fat! Lose some weight, fatty!" 364 | | otherwise = "You're a whale, congratulations!" 365 | where bmi = weight / height ^ 2 366 | skinny = 18.5 367 | normal = 25.0 368 | fat = 30.0 369 | 370 | 也可以改成 371 | where bmi = weight / height ^ 2 372 | (skinny, normal, fat) = (18.5, 25.0, 30.0) 373 | ``` 374 | 375 | 姓名的首字母 376 | 377 | ``` 378 | initials :: String -> String -> String 379 | initials firstname lastname = [f] ++ ". " ++ [l] ++ "." 380 | where (f:_) = firstname 381 | (l:_) = lastname 382 | result: 383 | initials "king" "soft" 384 | "k. s." 385 | ``` 386 | 387 | where 函数 388 | 389 | ``` 390 | calcBmis :: (RealFloat a) => [(a, a)] -> [a] 391 | calcBmis xs = [bmi w h | (w, h) <- xs] 392 | where bmi weight height = weight / height ^ 2 393 | ``` 394 | 395 | ## 关键字 Let 396 | 397 | > let x=5 in x*x 代表定义一个x=5,in即执行 5 * 5 398 | 399 | ``` 400 | ghci> (let a = 100; b = 200; c = 300 in a*b*c, let foo="Hey "; bar = "there!" in foo ++ bar) 401 | (6000000,"Hey there!") 402 | ghci> [let square x = x * x in (square 5, square 3, square 2)] 403 | [(25,9,4)] 404 | ``` 405 | 406 | 可代替 `where` 407 | 408 | ``` 409 | calcBmis :: (RealFloat a) => [(a, a)] -> [a] 410 | calcBmis xs = [bmi | (w, h) <- xs, let bmi = w / h ^ 2] 411 | 412 | ``` 413 | 允许忽略 `in` 414 | 415 | ``` 416 | ghci> let zoot x y z = x * y + z 417 | ghci> zoot 3 9 2 418 | 29 419 | ghci> let boot x y z = x * y + z in boot 3 4 2 420 | 14 421 | ghci> boot 422 | < interactive>:1:0: Not in scope: `boot' 423 | ``` 424 | 425 | ## Case expressions 426 | 427 | case表达式 428 | 429 | ``` 430 | case expression of pattern -> result 431 | pattern -> result 432 | pattern -> result 433 | ... 434 | ``` 435 | 函数参数的模式匹配只能在定义函数时使用,而 case 表达式可以用在任何地方。例如 436 | 437 | ``` 438 | describeList :: [a] -> String 439 | describeList xs = "The list is " ++ case xs of [] -> "empty." 440 | [x] -> "a singleton list." 441 | xs -> "a longer list." 442 | 443 | ``` 444 | 445 | 以下内容也是等价的 446 | 447 | ``` 448 | describeList :: [a] -> String 449 | describeList xs = "The list is " ++ what xs 450 | where what [] = "empty." 451 | what [x] = "a singleton list." 452 | what xs = "a longer list." 453 | ``` 454 | 455 | ## 递归 456 | 457 | 实现 maximum 458 | 459 | ``` 460 | maximum' :: (Ord a) => [a] -> a 461 | maximum' [] = error "maximum of empty list" 462 | maximum' [x] = x 463 | maximum' (x:xs) 464 | | x > maxTail = x 465 | | otherwise = maxTail 466 | where maxTail = maximum' xs 467 | ``` 468 | 469 | ``` 470 | replicate' :: (Num i, Ord i) => i -> a -> [a] 471 | replicate' n x 472 | | n <= 0 = [] 473 | | otherwise = x:replicate' (n-1) x 474 | ``` 475 | 476 | ## 高端函数 477 | 478 | 取一个函数和值,连续调用函数2次运算值 479 | 480 | ``` 481 | applyTwice :: (a -> a) -> a -> a 482 | applyTwice f x = f (f x) 483 | 484 | ghci> applyTwice (+3) 10 485 | 16 486 | ghci> applyTwice (++ " HAHA") "HEY" 487 | "HEY HAHA HAHA" 488 | ghci> applyTwice ("HAHA " ++) "HEY" 489 | "HAHA HAHA HEY" 490 | ghci> applyTwice (multThree 2 2) 9 491 | 144 492 | ghci> applyTwice (3:) [1] 493 | [3,3,1] 494 | ``` 495 | 496 | 取一个函数,2个LIST,函数运算2个LIST 497 | 498 | ``` 499 | zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c] 500 | zipWith' _ [] _ = [] 501 | zipWith' _ _ [] = [] 502 | zipWith' f (x:xs) (y:ys) = f x y : zipWith' f xs ys 503 | 504 | ghci> zipWith' (+) [4,2,5,6] [2,6,2,3] 505 | [6,8,7,9] 506 | ghci> zipWith' max [6,3,2,1] [7,3,1,5] 507 | [7,3,2,5] 508 | ghci> zipWith' (++) ["foo ","bar ","baz "] ["fighters","hoppers","aldrin"] 509 | ["foo fighters","bar hoppers","baz aldrin"] 510 | ghci> zipWith' (*) (replicate 5 2) [1..] 511 | [2,4,6,8,10] 512 | ghci> zipWith' (zipWith' (*)) [[1,2,3],[3,5,6],[2,3,4]] [[3,2,2],[3,4,5],[5,4,3]] 513 | [[3,4,6],[9,20,30],[10,12,12]] 514 | ``` 515 | 516 | ## map 与 filter 517 | 518 | **map** 519 | 520 | ``` 521 | map :: (a -> b) -> [a] -> [b] 522 | map _ [] = [] 523 | map f (x:xs) = f x : map f xs 524 | 525 | ghci> map (+3) [1,5,3,1,6] 526 | [4,8,6,4,9] 527 | ghci> map (++ "!") ["BIFF","BANG","POW"] 528 | ["BIFF!","BANG!","POW!"] 529 | ghci> map (replicate 3) [3..6] 530 | [[3,3,3],[4,4,4],[5,5,5],[6,6,6]] 531 | ghci> map (map (^2)) [[1,2],[3,4,5,6],[7,8]] 532 | [[1,4],[9,16,25,36],[49,64]] 533 | ghci> map fst [(1,2),(3,5),(6,3),(2,6),(2,5)] 534 | [1,3,6,2,2] 535 | ``` 536 | 537 | **filter** 538 | 539 | ``` 540 | filter :: (a -> Bool) -> [a] -> [a] 541 | filter _ [] = [] 542 | filter p (x:xs) 543 | | p x = x : filter p xs 544 | | otherwise = filter p xs 545 | 546 | ghci> filter (>3) [1,5,3,2,1,6,4,3,2,1] 547 | [5,6,4] 548 | ghci> filter (==3) [1,2,3,4,5] 549 | [3] 550 | ghci> filter even [1..10] 551 | [2,4,6,8,10] 552 | ghci> let notNull x = not (null x) in filter notNull [[1,2,3],[],[3,4,5],[2,2],[],[],[]] 553 | [[1,2,3],[3,4,5],[2,2]] 554 | ghci> filter (`elem` ['a'..'z']) "u LaUgH aT mE BeCaUsE I aM diFfeRent" 555 | "uagameasadifeent" 556 | ghci> filter (`elem` ['A'..'Z']) "i lauGh At You BecAuse u r aLL the Same" 557 | "GAYBALLS" 558 | ``` 559 | 560 | ## lambda 561 | 562 | > 其实就是匿名函数 563 | 564 | lambda 是个表达式,因此我们可以任意传递。表达式 (\xs -> length xs > 15) 回传一个函数,它可以告诉我们一个 List 的长度是否大于 15。 565 | 566 | ``` 567 | numLongChains :: Int 568 | numLongChains = length (filter (\xs -> length xs > 15) (map chain [1..100])) 569 | 570 | 571 | flip' :: (a -> b -> c) -> b -> a -> c 572 | flip' f = \x y -> f y x 573 | ``` 574 | 575 | ## 关键字 fold 576 | 577 | ...学习到此:http://learnyoua.haskell.sg/content/zh-cn/ch06/high-order-function.html -------------------------------------------------------------------------------- /HTML_and_CSS_Style_Guide.md: -------------------------------------------------------------------------------- 1 | # HTML and CSS Style Guide 2 | 3 | ## EditorConfig 4 | 5 | 建议选择支持 [EditorConfig](http://editorconfig.org/) 的编辑器,并安装相应插件,在项目根目录建立一个 `.editorconfig` 并插入以下内容: 6 | 7 | ``` 8 | [*.html] 9 | charset = utf-8 10 | end_of_line = lf 11 | insert_final_newline = true 12 | indent_style = space 13 | indent_size = 2 14 | trim_trailing_whitespace = true 15 | 16 | [*.ejs] 17 | charset = utf-8 18 | end_of_line = lf 19 | insert_final_newline = true 20 | indent_style = space 21 | indent_size = 2 22 | trim_trailing_whitespace = true 23 | 24 | [*.jade] 25 | charset = utf-8 26 | end_of_line = lf 27 | insert_final_newline = true 28 | indent_style = space 29 | indent_size = 2 30 | trim_trailing_whitespace = true 31 | 32 | [*.css] 33 | charset = utf-8 34 | end_of_line = lf 35 | insert_final_newline = true 36 | indent_style = space 37 | indent_size = 2 38 | trim_trailing_whitespace = true 39 | 40 | [*.less] 41 | charset = utf-8 42 | end_of_line = lf 43 | insert_final_newline = true 44 | indent_style = space 45 | indent_size = 2 46 | trim_trailing_whitespace = true 47 | ``` 48 | 49 | ## File Naming Conventions 50 | 51 | - 使用半角小写英文字母(单词)和数字 52 | - 使用半角的 `-` 和 `.` 分割单词 53 | - 如非必要,请勿包含版本号 54 | 55 | 示例: 56 | 57 | my-cart.html 58 | style.css 59 | icon-sprites.png 60 | 61 | ## Indentations 62 | 63 | 每一级缩进使用两个空格,请勿混合使用空格和 tab。 64 | 65 | 69 | 70 | body { 71 | color: #333; 72 | } 73 | 74 | ## Cases 75 | 76 | 使用小写编写 HTML 元素名、属性、除 `text/CDATA`、`data-attr` 外的属性的值和 CSS 选择器、属性、除字符串类型外的属性的值。 77 | 78 | 79 | 80 | 81 | body { 82 | background-color: #efefef; 83 | } 84 | 85 | .cash { 86 | &::before { 87 | content: 'CNY'; 88 | } 89 | } 90 | 91 | ## File Encodings 92 | 93 | 确保编辑器 / IDE 使用的编码为 UTF-8 without BOM。如不能确定使用的编辑器 / IDE 的编码,请使用 Sublime Text、WebStorm 等工具,并保持默认编码设置。 94 | 95 | HTML 需确保有以下语句: 96 | 97 | 98 | 99 | CSS 请勿加入以下语句: 100 | 101 | // bad 102 | @charset 'UTF-8'; 103 | 104 | ## Protocols 105 | 106 | 引用 `http`、`https` 资源时尽量使用 `//`。 107 | 108 | // bad 109 | 110 | 111 | body { 112 | background-image: url(http://domain.com/img/main-bg.png); 113 | } 114 | 115 | // good 116 | 117 | 118 | body { 119 | background-image: url(//domain.com/img/main-bg.png); 120 | } 121 | 122 | ## Tag Your Codes 123 | 124 | 使用 `#@tag` 在代码标记行为: 125 | 126 | - `#@todo`:标记需尽快执行的事项 127 | - `#@issue`:标记暂时可不执行的事项 128 | 129 | HTML: 130 | 131 | 132 |
    133 | 134 | 135 |
    136 | 137 | CSS: 138 | 139 | .add-cart-link { 140 | /* #@todo: 添加 IE 8- 支持 */ 141 | opacity: .5; 142 | 143 | &:hover { 144 | opacity: 1; 145 | } 146 | } 147 | 148 | body { 149 | /* #@issue: 只有 WebKit 支持 */ 150 | -webkit-font-smoothing: antialiased; 151 | } 152 | 153 | 使用示例: 154 | 155 | 命令行 + [ag](https://github.com/ggreer/the_silver_searcher) = `ag -i -A 2 -B 2 '#@(todo|issue)'`: 156 | 157 | HTML_and_CSS_Style_Guide.md 158 | 111-使用 `#@tag` 在代码标记行为: 159 | 112- 160 | 113:- `#@todo`:标记需尽快执行的事项 161 | 114:- `#@issue`:标记暂时可不执行的事项 162 | 115- 163 | 116-HTML: 164 | 117- 165 | 118: 166 | 119-
      167 | 120- 168 | 121: 169 | 122-
      170 | 123- 171 | 125- 172 | 126- .add-cart-link { 173 | 127: /* #@todo: 添加 IE 8- 支持 */ 174 | 128- opacity: .5; 175 | 129- 176 | 134- 177 | 135- body { 178 | 136: /* #@issue: 只有 WebKit 支持 */ 179 | 137- -webkit-font-smoothing: antialiased; 180 | 138- } 181 | 182 | ## HTML 183 | 184 | ### Short Doctype 185 | 186 | 使用 `` 作为文档类型。注意大小写。 187 | 188 | // bad 189 | 190 | 191 | 192 | 193 | 194 | // good 195 | 196 | 197 | ### X-UA-Compatible 198 | 199 | 在 `` 加入 `X-UA-Compatible` 字段: 200 | 201 | 202 | 203 | `X-UA-Compatible` 可以让 IE 一直跑在系统里已有的最高文档模式,除非用 DevTool 强制指定文档模式。 204 | 205 | ### Shell Browsers 206 | 207 | 在 `` 加入 `name="renderer"` 字段,详见 [360 v6 浏览器内核控制 Meta 标签说明文档](http://se.360.cn/v6/help/meta.html): 208 | 209 | 210 | 211 | ### DNS Prefetch 212 | 213 | 使用 [DNS Prefetch](http://www.chromium.org/developers/design-documents/dns-prefetching) 来提高性能: 214 | 215 | 216 | 217 | 218 | ### Optional Attributes and Tags 219 | 220 | 省略可省略的闭合标签、属性和属性值(可选): 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 |
        231 |
      • Hell World 232 |

      233 | 234 | ### Quotes 235 | 236 | 使用双引号包裹属性值: 237 | 238 | // bad 239 | 240 | 241 | // good 242 | 243 | 244 | ### Blocks 245 | 246 | 每个块、列表、表格元素之间用空行分隔: 247 | 248 |

      Nice to meet you! 249 | 250 |

        251 |
      • Hello 252 |
      • Feifei 253 |
      254 | 255 | 256 | 257 | 259 |
      Order ID 258 |
      260 | 261 | ### Comments 262 | 263 | - 注释和代码块之间以一行空行分隔 264 | 265 | 示例: 266 | 267 |
      268 | 269 | 270 | 271 |
      272 | 273 | 274 | 278 | 279 | 280 | ### Indentations 281 | 282 | 严格按照层级关系缩进。每级缩进为两个空格。 283 | 284 | // bad 285 | 286 | 287 | 288 | 289 | 290 |
      291 |

      292 |

      293 | 294 | 295 | 296 | // good 297 | 298 | 299 | 300 | 301 | 302 |
      303 |

      304 |

      305 | 306 | 307 | 308 | ### "alt" and "title" Attributes 309 | 310 | 尽量加上 `alt` 和 `title` 属性: 311 | 312 | // bad 313 | Home 314 | 315 | 316 | // good 317 | Home 318 | Site Logo 319 | 320 | ### Entity References 321 | 322 | 在 UTF-8 编码下,可不用字符实体的,一律不用。 323 | 324 | ## LESS 325 | 326 | ### Naming Convensions 327 | 328 | #### General Rules 329 | 330 | - 使用半角小写英文字母和数字(不能以数字开头) 331 | - 使用半角 `-` 分割单词(第三方库保留原命名,无需更改) 332 | - 使用可读性好、描述抽象实体或行为而非样式的命名 333 | - 可在 ID / class 前添加模块化前缀 334 | - 在可以理解且通用的情况下,可缩短 ID 和 Class 的命名 335 | 336 | 示例: 337 | 338 | // bad 339 | .left 340 | .btn-green 341 | .button-large 342 | .ftr 343 | #navigation 344 | 345 | // good 346 | .pull-left 347 | #nav 348 | .btn 349 | .btn-large 350 | .logo 351 | .dropdown-menu-list 352 | 353 | #### Variables 354 | 355 | 在变量后添加类型标识,如 `font-size`、`color`;可同时使用 `light`、`base` 等关键字进一步区分: 356 | 357 | @gray-light: #ccc; 358 | @sans-font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; 359 | @base-font-size: 14px; 360 | @warning-text-color: @danger-light; 361 | @warning-background-color: @danger-light; 362 | @box-border-radius-base: 3px; 363 | 364 | #### Mixins 365 | 366 | Mixin 需以 `()` 结尾;在可通用的场合,可以使用 class 来代替。 367 | 368 | // bad 369 | .borderless { 370 | border: 0 none; 371 | } 372 | 373 | // good 374 | .borderless() { 375 | border: 0 none; 376 | } 377 | 378 | .clearfix { 379 | // ... 380 | } 381 | 382 | .box { 383 | .clearfix; 384 | } 385 | 386 | ### Quotes 387 | 388 | 使用单引号: 389 | 390 | // bad 391 | body { 392 | font-family: "Avenir New"; 393 | } 394 | 395 | // good 396 | body { 397 | font-family: 'Avenir New'; 398 | } 399 | 400 | ### Selectors 401 | 402 | - 优先使用 class 选择器 403 | - ID 选择器用于样式唯一的元素 404 | - 多个选择器需分行写,一行一个,逗号置于行尾,左大括号置于最后一个选择器尾部,并以一个空格隔开 405 | - 避免标签选择器和 ID 或 class 选择器混用 406 | - 避免冗长的选择器,尽量保持在 3~4 个以内 407 | - 避免使用 `*` 408 | 409 | 示例: 410 | 411 | // bad 412 | ul.online-users {} 413 | button.login {} 414 | #send-weibo.btn {} 415 | div.content {} 416 | .common-header .main-nav ui li {} 417 | 418 | [type=text], [type=email] {} 419 | 420 | // good 421 | .main-menu {} 422 | .online-users {} 423 | .btn-login {} 424 | .main-nav li {} 425 | 426 | [type=text], 427 | [type=email] {} 428 | 429 | ### Values and Units 430 | 431 | - 值和单位使用小写; 432 | - 使用简写的十六进制颜色值; 433 | - 省略 `0` 后的单位; 434 | - 如果值小于 `1`,省略小数点前的 `0`。 435 | 436 | 示例: 437 | 438 | /* bad */ 439 | header { 440 | color: #DCDCDC; 441 | font-size: 12PX; 442 | margin: 0px; 443 | opacity: 0.7; 444 | } 445 | 446 | /* good */ 447 | header { 448 | color: #ddd; 449 | font-size: 12px; 450 | margin: 0; 451 | opacity: .7; 452 | } 453 | 454 | ## Commas, Whitespacesm, Colons and Semicolons 455 | 456 | - 逗号置于单词后面,并在逗号后面加入空格 457 | - 冒号需紧跟着属性,和值之间以一个空格分割 458 | - `([` 后不需要插入空格,`)]` 前不需要插入空格 459 | - 请不要省略分号 460 | 461 | 示例: 462 | 463 | // bad 464 | [ type=text ],[type=email]{ 465 | font-family:Avenir,Arial; 466 | font-weight: 400 467 | } 468 | 469 | .btn { 470 | background-image: url( btn-bg.png ); 471 | } 472 | 473 | // good 474 | [type=text], 475 | [type=email] { 476 | font-family: Avenir, Arial; 477 | font-weight: 400; 478 | } 479 | 480 | .btn { 481 | background-image: url(btn-bg.png); 482 | } 483 | 484 | ### Properties 485 | 486 | - 属性以字母顺序排列,前缀不参与排序,但前缀之间需排序,W3C 标准语法放在末尾 487 | - 优先使用属性的简写,但注意避免不必要的复写 488 | - LESS mixin 置于最上,以字母排序 489 | - LESS mixin 若无参数,可省略 `()` 490 | - LESS mixin 和标准 CSS 属性之间以一个空行分割 491 | 492 | 示例: 493 | 494 | // bad 495 | body { 496 | font: 16px/1.5 Arial; 497 | } 498 | 499 | h1 { 500 | font: 16px/1 Avenir, Arial; 501 | } 502 | 503 | nav { 504 | margin-bottom: 10px; 505 | margin-left: 10px; 506 | margin-top: 10px; 507 | } 508 | 509 | div { 510 | width: 100px; 511 | margin: 10px; 512 | box-sizing: border-box; 513 | -webkit-box-sizing: border-box; 514 | -moz-box-sizing: border-box; 515 | .clearfix(); 516 | } 517 | 518 | // good 519 | h1 { 520 | font-family: Avenir, Arial; 521 | line-height: 1; 522 | } 523 | 524 | nav { 525 | margin: 10px 10px 10px 0; 526 | } 527 | 528 | nav li { 529 | padding: 10px; 530 | } 531 | 532 | nav li li { 533 | padding-left: 30px; 534 | } 535 | 536 | div { 537 | .clearfix; 538 | 539 | -moz-box-sizing: border-box; 540 | -webkit-box-sizing: border-box; 541 | box-sizing: border-box; 542 | margin: 10px; 543 | width: 10px; 544 | } 545 | 546 | ### DRY 547 | 548 | - 将可复用部分抽象成基础的变量、class 或 mixin 549 | - 请勿在没有 context 的情况下使用带强烈行为、样式含义的命名,如 `.red`、`.left` 550 | 551 | 示例: 552 | 553 | // bad 554 | .red { 555 | background: red; 556 | } 557 | 558 | .two { 559 | .column { 560 | width: 100% / 2; 561 | } 562 | } 563 | 564 | // right 是状态描述 565 | .right { 566 | float: right; 567 | } 568 | 569 | // good 570 | @text-color-danger: red; 571 | 572 | .pull-right { 573 | float: right; 574 | } 575 | 576 | .sidebar { 577 | &.left { 578 | margin-right: 20px; 579 | } 580 | 581 | &.right { 582 | margin-left: 20px; 583 | margin-right: 0; 584 | } 585 | } 586 | 587 | .text { 588 | &.red { 589 | color: @text-color-danger; 590 | } 591 | 592 | &.text-red { 593 | color: @text-color-danger; 594 | } 595 | } 596 | 597 | .grid { 598 | &.two { 599 | .column { 600 | width: 100% / 2; 601 | } 602 | } 603 | 604 | &.three { 605 | .column { 606 | width: 100% / 3; 607 | } 608 | } 609 | } 610 | 611 | ### Browser Compatibilities 612 | 613 | 使用 [Modernizr](http://modernizr.com/) 和条件注释来区分不同样式。 614 | 615 | 616 | 619 | 622 | 625 | 628 | 629 | .placeholder { 630 | input { 631 | &::-webkit-input-placeholder {} 632 | } 633 | 634 | .placeholder-polyfill { 635 | display: none !important; 636 | } 637 | } 638 | 639 | .no-placeholder { 640 | input {} 641 | 642 | .placeholder-polyfill { 643 | display: block; 644 | } 645 | } 646 | 647 | #### Hacks 648 | 649 | 一般而言,避免使用如下的 hacks: 650 | 651 | - `property: \0`(IE 8 - 10) 652 | - `property: value\9\0`(IE 9 - 10) 653 | - `*property: value`(IE 6 - 7) 654 | - `_property: value`(IE 6) 655 | 656 | 在 IE 6-7 下简短几句属性可解决的问题可以使用 hacks: 657 | 658 | * { 659 | box-sizing: border-box; 660 | *behavior: url(boxsizing.htc); 661 | } 662 | 663 | .clearfix { 664 | *zoom: 1; 665 | } 666 | 667 | ### Comments 668 | 669 | 置于头部的文件描述: 670 | 671 | // 672 | // What is this file used for 673 | // -------------------------------------------------- 674 | // Long description, optional 675 | 676 | 分组代码,示例: 677 | 678 | // Global values 679 | // -------------------------------------------------- 680 | 681 | // Grays 682 | // ------------------------- 683 | // these color values are fixed 684 | 685 | @black-10: darken(#fff, 10%); // #e6e6e6 686 | 687 | @gray-lighter: #eee; 688 | 689 | // Accent colors 690 | // ------------------------- 691 | 692 | @red-light: #c00; 693 | 694 | // Component colors 695 | // ------------------------- 696 | 697 | @success-background-color: @green-light; 698 | 699 | 在定义变量时,如值不能被直观地辨认,在其后使用注释标明实际值,如: 700 | 701 | @black-10: darken(#fff, 10%); // #e6e6e6 702 | 703 | 示例: 704 | 705 | // Imported libraries/frameworks 706 | // -------------------------------------------------- 707 | // Imported libraries/frameworkds should on the top of custom styles 708 | 709 | // Normalize.css provides more robust code than reset.css 710 | @import 'base/normalize' 711 | 712 | // Import Fly UI as base framework 713 | @import 'fly-ui/fly-ui' 714 | 715 | // Custom Styles 716 | @import 'base/variables' 717 | @import 'base/mixins' 718 | 719 | // Page-specified variables 720 | // -------------------------------------------------- 721 | 722 | // This only used in this page 723 | @_blue-lighter: #0084b7; 724 | 725 | // Page-specified rules 726 | // -------------------------------------------------- 727 | 728 | body { 729 | background-color: @gray-lighter; 730 | color: @black-80; 731 | } 732 | 733 | // Reset input fields 734 | // ------------------------- 735 | 736 | input[type="text"], 737 | input[type="password"], 738 | input[type="email"], 739 | textarea { /* … */ } 740 | 741 | 742 | ### Tools 743 | 744 | LESS 会经过自动化工具编译,如果希望手工编译,可以使用以下工具: 745 | 746 | - Linux: [Koala](http://koala-app.com/) 747 | - OS X & Windows: [Prepros](http://alphapixels.com/prepros/), [LiveReload](http://livereload.com/) 748 | - OS X: [CodeKit](http://incident57.com/codekit/), [LESS.app](http://incident57.com/less/), [SimpLESS](http://wearekiss.com/simpless) 749 | - Windows: [SimpLESS](http://wearekiss.com/simpless), [Crunch](http://crunchapp.net/), [Smalify](http://smalify.net/) 750 | 751 | ## Meta 752 | 753 | - 在每个文件的末尾添加一行空行 754 | - 请删除行尾多余的空格 -------------------------------------------------------------------------------- /go.md: -------------------------------------------------------------------------------- 1 | # GO 学习笔记 2 | 3 | # 学习目标 4 | 5 | * 能够快速掌握Go基本语法 6 | * 能够修改、调试Go的项目代码 7 | * 对目前主流框架的情况有所了解及选型 8 | * 能够创建API服务、分布式服务等 9 | * 能够快速部署Go应用程序 10 | 11 | 12 | # 基础语法 13 | 14 | ## 命名 15 | 16 | ### 25个关键字 17 | 18 | ``` 19 | break default func interface select 20 | case defer go map struct 21 | chan else goto package switch 22 | const fallthrough if range type 23 | continue for import return var 24 | ``` 25 | 26 | ### 30+个预定义名字 27 | 28 | ``` 29 | 内建常量: true false iota nil 30 | 31 | 内建类型: int int8 int16 int32 int64 32 | uint uint8 uint16 uint32 uint64 uintptr 33 | float32 float64 complex128 complex64 34 | bool byte rune string error 35 | 36 | 内建函数: make len cap new append copy close delete 37 | complex real imag 38 | panic recove 39 | ``` 40 | 41 | ## 变量声明 42 | 43 | ### 语法 44 | 45 | ``` 46 | var 变量名字 类型 = 表达式 47 | var i, j, k int // int, int, int 48 | var b, f, s = true, 2.3, "four" // bool, float64, string 49 | var f, err = os.Open(name) 50 | ``` 51 | 52 | ### 简短声明 53 | > 变量的类型根据表达式来自动推导, 广泛用于大部分的局部变量的声明和初始化 54 | 55 | ``` 56 | anim := gif.GIF{LoopCount: nframes} 57 | freq := rand.Float64() * 3.0 58 | t := 0.0 59 | i, j := 0, 1 60 | ``` 61 | 62 | ## 指针 63 | 64 | > 那么&x表达式(取x变量的内存地址), 指针对应的数据类型是*int,指针被称之为“指向int类型的指针” 65 | 66 | * 任何类型的指针的零值都是nil 67 | * 表达式也必须能接受&取地址操作 68 | * 指针之间也是可以进行相等测试的,只有当它们指向同一个变量或全部是nil时才相等 69 | * 编译器会自动选择在栈上还是在堆上分配局部变量的存储空间 70 | * new(Type) = &Type{} 71 | 72 | ``` 73 | x := 1 74 | p := &x // p 为 *int 指向 x 的内存地址 75 | fmt.Println(*p) // "1" 76 | *p = 2 // 更改p指向的内存地址的值 to x = 2 77 | fmt.Println(x) // "2" 78 | 79 | var p = f() 80 | 81 | func f() *int { 82 | v := 1 83 | return &v 84 | } 85 | 86 | // 每次调用f函数都将返回不同的结果: 87 | 88 | fmt.Println(f() == f()) // "false" 89 | ``` 90 | 通过指针来更新变量的值,然后返回更新后的值 91 | 92 | ``` 93 | func incr(p *int) int { 94 | *p++ // 非常重要:只是增加p指向的变量的值,并不改变p指针!!! 95 | return *p 96 | } 97 | 98 | v := 1 99 | incr(&v) // side effect: v is now 2 100 | fmt.Println(incr(&v)) // "3" (and v is 3) 101 | ``` 102 | 103 | ## 类型 104 | 105 | > type 类型名字 底层类型 106 | 107 | ``` 108 | type Celsius float64 // 摄氏温度 109 | type Fahrenheit float64 // 华氏温度 110 | 111 | const ( 112 | AbsoluteZeroC Celsius = -273.15 // 绝对零度 113 | FreezingC Celsius = 0 // 结冰点温度 114 | BoilingC Celsius = 100 // 沸水温度 115 | ) 116 | 117 | func CToF(c Celsius) Fahrenheit { return Fahrenheit(c*9/5 + 32) } 118 | 119 | func FToC(f Fahrenheit) Celsius { return Celsius((f - 32) * 5 / 9) } 120 | ``` 121 | 122 | 123 | ## 数据类型 124 | 125 | ### 整数 126 | 127 | ``` 128 | var u uint8 = 255 129 | var i int8 = 127 130 | 131 | ``` 132 | ### 浮点数 133 | ``` 134 | var f float32 = 16777216 // 1 << 24 135 | const e = 2.71828 // (approximately) 136 | const Avogadro = 6.02214129e23 // 阿伏伽德罗常数 137 | const Planck = 6.62606957e-34 // 普朗克常数 138 | ``` 139 | ### 复数 140 | ``` 141 | var x complex128 = complex(1, 2) // 1+2i 142 | var y complex128 = complex(3, 4) // 3+4i 143 | ``` 144 | 145 | ### 布尔型 146 | 147 | ``` 148 | func btoi(b bool) int { 149 | if b { 150 | return 1 151 | } 152 | return 0 153 | } 154 | ``` 155 | 156 | ### 字符串 157 | 158 | ``` 159 | s := "hello, world" 160 | fmt.Println(len(s)) // "12" 161 | fmt.Println(s[0], s[7]) // "104 119" ('h' and 'w') 162 | fmt.Println(s[:5]) // "hello" 163 | fmt.Println(s[7:]) // "world" 164 | fmt.Println(s[:]) // "hello, world" 165 | n := 0 166 | for _, _ = range s { 167 | n++ 168 | } 169 | 170 | // 字符串和Byte切片 171 | // 标准库中有四个包对字符串处理尤为重要:bytes、strings、strconv和unicode包。strings包提供了许多如字符串的查询、替换、比较、截断、拆分和合并等功能。 172 | 173 | ``` 174 | ### 常量 175 | > 和变量声明一样,可以批量声明多个常量 176 | 177 | > 常量声明可以使用iota常量生成器初始化,它用于生成一组以相似规则初始化的常量,但是不用每行都写一遍初始化表达式。在一个const声明语句中,在第一个声明的常量所在的行,iota将会被置为0,然后在每一个有常量声明的行加一。 178 | 179 | ``` 180 | const ( 181 | e = 2.71828182845904523536028747135266249775724709369995957496696763 182 | pi = 3.14159265358979323846264338327950288419716939937510582097494459 183 | ) 184 | 185 | 186 | const ( 187 | a = 1 188 | b 189 | c = 2 190 | d 191 | ) 192 | 193 | 194 | type Weekday int 195 | 196 | const ( 197 | Sunday Weekday = iota 198 | Monday 199 | Tuesday 200 | Wednesday 201 | Thursday 202 | Friday 203 | Saturday 204 | ) 205 | ``` 206 | 207 | ## 复合数据类型 208 | 209 | ### 数组 210 | 211 | ``` 212 | var a [3]int 213 | var q [3]int = [3]int{1, 2, 3} 214 | var r [3]int = [3]int{1, 2} 215 | q := [...]int{1, 2, 3} 216 | q := [3]int{1, 2, 3} 217 | q = [4]int{1, 2, 3, 4} 218 | 219 | a := [2]int{1, 2} 220 | b := [...]int{1, 2} 221 | c := [2]int{1, 3} 222 | 223 | fmt.Println(a == b, a == c, b == c) // "true false false" 224 | d := [3]int{1, 2} 225 | fmt.Println(a == d) // compile error: cannot compare [2]int == [3]int 226 | 227 | // 定义了一个含有100个元素的数组r,最后一个元素被初始化为-1,其它元素都是用0初始化。 228 | r := [...]int{99: -1} 229 | 230 | // print 231 | for i, v := range a { 232 | fmt.Printf("%d %d\n", i, v) 233 | } 234 | 235 | // 打印金钱单位 236 | type Currency int 237 | 238 | const ( 239 | USD Currency = iota // 美元 240 | EUR // 欧元 241 | GBP // 英镑 242 | RMB // 人民币 243 | ) 244 | 245 | symbol := [...]string{USD: "$", EUR: "€", GBP: "£", RMB: "¥"} 246 | 247 | fmt.Println(RMB, symbol[RMB]) // "3 ¥" 248 | 249 | ``` 250 | 251 | ### slice(切片) 252 | > 切片是一个引用类型,对数组一个连续片段的引用 253 | 254 | * 由 len() 函数获取长度 255 | * cap() 可以测量切片最长可以达到多少:它等于切片的长度 + 数组除切片之外的长度,切片 s 来说该不等式永远成立:0 <= len(s) <= cap(s)。 256 | * 多个切片如果表示同一个数组的片段,它们可以共享数 257 | 258 | > 切片初始化 259 | 260 | ``` 261 | var identifier []type(不需要说明长度) 262 | var slice1 []type = arr1[start:end] 263 | // 对于每一个切片(包括 string),以下状态总是成立的 264 | s == s[:i] + s[i:] // i是一个整数且: 0 <= i <= len(s) 265 | len(s) < cap(s) 266 | ``` 267 | 268 | > make() 创建一个切片 269 | > make 接受 2 个参数:元素的类型以及切片的元素个数。 270 | 271 | ``` 272 | var slice1 []type = make([]type, len)。 273 | // 简写 274 | slice1 := make([]type, len) 275 | // 如果你想创建一个 slice1,它不占用整个数组,而只是占用以 len 为个数个项, 276 | slice1 := make([]type, len, cap)。 277 | // make 的使用方式是: 278 | func make([]T, len, cap),其中 cap 是可选参数。 279 | ``` 280 | > 下面两种方法可以生成相同的切片 281 | 282 | ``` 283 | make([]int, 50, 100) 284 | new([100]int)[0:50] 285 | ``` 286 | 287 | * new(T) 为每个新的类型T分配一片内存,初始化为 0 并且返回类型为*T的内存地址:这种方法 返回一个指向类型为 T,值为 0 的地址的指针,它适用于值类型如数组和结构体(参见第 10 章);它相当于 &T{} 288 | * make(T) 返回一个类型为 T 的初始值,它只适用于3种内建的引用类型:切片、map 和 channel 289 | 290 | 291 | #### 切片的追加与复制 292 | 293 | ``` 294 | func main() { 295 | sl_from := []int{1, 2, 3} 296 | sl_to := make([]int, 10) 297 | 298 | n := copy(sl_to, sl_from) // 复制 299 | fmt.Println(sl_to) 300 | fmt.Printf("Copied %d elements\n", n) // n == 3 301 | 302 | sl3 := []int{1, 2, 3} 303 | sl3 = append(sl3, 4, 5, 6) 304 | fmt.Println(sl3) 305 | } 306 | ``` 307 | > 如果 s 的容量不足以存储新增元素,append 会分配新的切片来保证已有切片元素和新增元素的存储 308 | > 如果你想将切片 y 追加到切片 x 后面,只要将第二个参数扩展成一个列表即可:x = append(x, y...) 309 | 310 | 311 | ``` 312 | func append(s[]T, x ...T) []T 313 | ``` 314 | 315 | ### Map 316 | 317 | > Map的创建方式如下: 318 | 319 | ``` 320 | // 语法 321 | make(map[keyType]valueType, initialCapacity) 322 | make(map[keyType]valueType) 323 | map[keyType]valueType{} 324 | map[keyType]valueType{key1: value1, key2: value2, ..., keyN: valueN} 325 | 326 | // 示例 327 | var mapLit map[string]int 328 | var mapCreated map[string]float32 329 | var mapAssigned map[string]int 330 | 331 | mapLit = map[string]int{"one": 1, "two": 2} 332 | mapCreated := make(map[string]float32) 333 | mapAssigned = mapLit 334 | 335 | mapCreated["key1"] = 4.5 336 | mapCreated["key2"] = 3.14159 337 | mapAssigned["two"] = 3 338 | 339 | // 判断是否存在 340 | _, ok := map1[key1] // 如果key1存在则ok == true,否在ok为false 341 | // 混合使用 342 | if _, ok := map1[key1]; ok { 343 | // ... 344 | } 345 | // 删除 346 | delete(map1, key1) 347 | // for-range 用法 348 | for key, value := range map1 { 349 | ... 350 | } 351 | // 切片类型 352 | // Version A: 353 | items := make([]map[int]int, 5) 354 | for i:= range items { 355 | items[i] = make(map[int]int, 1) 356 | items[i][1] = 2 357 | } 358 | fmt.Printf("Version A: Value of items: %v\n", items) 359 | 360 | // Version B: NOT GOOD! 361 | items2 := make([]map[int]int, 5) 362 | for _, item := range items2 { 363 | item = make(map[int]int, 1) // item is only a copy of the slice element. 364 | item[1] = 2 // This 'item' will be lost on the next iteration. 365 | } 366 | fmt.Printf("Version B: Value of items: %v\n", items2) 367 | ``` 368 | 369 | ## 测试工具 370 | 371 | ### wrk 压力测试工具 372 | 373 | #### 参考文章 374 | 375 | - http://blog.csdn.net/alexander_wang_it/article/details/52214558 376 | - http://zjumty.iteye.com/blog/2221040 377 | 378 | ``` 379 | // 用12个线程模拟100个连接持续30s 380 | wrk -t12 -c100 -d30s http://www.163.com 381 | ``` 382 | > 参数描述 383 | 384 | - `-t` 模拟线程数 385 | - `-c` 模拟连接数 386 | - `-T` 模拟超时 387 | - `-d` 持续测试时间 388 | - `--latency` 响应时间分布 389 | - `--script` 加载`lua`脚本 390 | - 创建 `post.lua` 391 | 392 | ``` 393 | wrk.method = "POST" 394 | wrk.body = "foo=bar&baz=quux" 395 | wrk.headers["Content-Type"] = "application/x-www-form-urlencoded" 396 | // 更多属性 397 | local wrk = { 398 | scheme = "http", 399 | host = "localhost", 400 | port = nil, 401 | method = "GET", 402 | path = "/", 403 | headers = {}, 404 | body = nil, 405 | thread = nil 406 | } 407 | ``` 408 | 409 | ``` 410 | // 执行 411 | wrk -t12 -c100 -d30s -T30s --script=post.lua --latency http://www.baidu.com 412 | ``` 413 | 414 | #### Function 415 | ``` 416 | local counter = 1 417 | local threads = {} 418 | 419 | function setup(thread) 420 | thread:set("id", counter) 421 | table.insert(threads, thread) 422 | counter = counter + 1 423 | end 424 | 425 | function init(args) 426 | requests = 0 427 | responses = 0 428 | 429 | local msg = "thread %d created" 430 | print(msg:format(id)) 431 | end 432 | 433 | function request() 434 | requests = requests + 1 435 | return wrk.request() 436 | end 437 | 438 | function response(status, headers, body) 439 | responses = responses + 1 440 | end 441 | 442 | function done(summary, latency, requests) 443 | for index, thread in ipairs(threads) do 444 | local id = thread:get("id") 445 | local requests = thread:get("requests") 446 | local responses = thread:get("responses") 447 | local msg = "thread %d made %d requests and got %d responses" 448 | print(msg:format(id, requests, responses)) 449 | end 450 | end 451 | ``` 452 | #### Instance 453 | #### bash 454 | ``` 455 | #!/bin/bash 456 | 457 | head="threadPool,concurrent,latency.min,latency.max,latency.mean,latency.stdev,upper_50,upper_90,upper_95,upper_99,duration,requests,bytes,errors.connect,errors.read,errors.write,errors.status,errors.timeout,qps,Transfer/sec(KB)" 458 | 459 | ## threadPoolSize 460 | threadPool=100 461 | ## 压测持续时间,单位s、m 462 | duration=1s 463 | ## 每轮压测结束后休息时间 464 | sleepTime=5s 465 | ## 压测路径URL 466 | url="http://localhost:38290/rpc" 467 | 468 | ## 间隔递增并发数 469 | concurrentInterval=12 470 | ## 停止压测并发数,超过此值停止压测 471 | stopConcurrent=1000 472 | # 并发计数器 473 | i=0 474 | 475 | echo $head; 476 | while (($i <= $stopConcurrent)) 477 | do 478 | let i+=concurrentInterval 479 | ret=`wrk -t12 -c$i -d$duration --latency -s post.lua $url | sed -n '$p'` 480 | ret="$threadPool,$i,"$ret; 481 | echo $ret; 482 | sleep $sleepTime 483 | done 484 | ``` 485 | #### post.lua 486 | 487 | ``` 488 | function response(status, headers, body) 489 | --print(body) 490 | end 491 | 492 | function init(args) 493 | for k, v in pairs(args) do 494 | --print(k, v) 495 | end 496 | end 497 | 498 | function done(summary, latency, requests) 499 | print("latency.min,latency.max,latency.mean,latency.stdev,latency:percentile(50.0),latency:percentile(90.0),latency:percentile(95.0),latency:percentile(99.0),summary.duration,summary.requests,summary.bytes,summary.errors.connect,summary.errors.read,summary.errors.write,summary.errors.status,summary.errors.timeout,qps,Transfer/sec(KB)") 500 | print(latency.min/1000 .. "," .. latency.max/1000 .. "," .. latency.mean/1000 .. "," .. latency.stdev/1000 .. "," .. latency:percentile(50.0)/1000 .. "," .. latency:percentile(90.0)/1000 .. "," .. latency:percentile(95.0)/1000 .. "," .. latency:percentile(99.0)/1000 .. "," .. summary.duration/1000000 .. "," .. summary.requests .. "," .. summary.bytes .. "," .. summary.errors.connect.. "," .. summary.errors.read.. "," .. summary.errors.write.. "," .. summary.errors.status.. "," .. summary.errors.timeout .. "," .. summary.requests/(summary.duration/1000000) .. "," .. (summary.bytes/1024)/(summary.duration/1000000)) 501 | end 502 | 503 | function file_exists(file) 504 | local f = io.open(file, "rb") 505 | if f then f:close() end 506 | return f ~= nil 507 | end 508 | 509 | function shuffle(paths) 510 | local j, k 511 | local n = #paths 512 | for i = 1, n do 513 | j, k = math.random(n), math.random(n) 514 | paths[j], paths[k] = paths[k], paths[j] 515 | end 516 | return paths 517 | end 518 | 519 | function non_empty_lines_from(file) 520 | if not file_exists(file) then return {} end 521 | lines = {} 522 | for line in io.lines(file) do 523 | if not (line == '') then 524 | lines[#lines + 1] = line 525 | end 526 | end 527 | return shuffle(lines) 528 | end 529 | 530 | 531 | 532 | function request() 533 | path = paths[counter] 534 | counter = counter + 1 535 | if counter > #paths then 536 | counter = 1 537 | end 538 | 539 | wrk.method = "POST" 540 | wrk.body = path 541 | wrk.headers["Content-Type"] = "application/json;charset=utf-8" 542 | return wrk.format(wrk.method,nil,wrk.headers,wrk.body) 543 | end 544 | 545 | counter = 1 546 | math.randomseed(os.time()) 547 | math.random(); math.random(); math.random() 548 | 549 | paths = non_empty_lines_from("paths.txt") 550 | 551 | if #paths <= 0 then 552 | print("multiplepaths: No paths found. You have to create a file paths.txt with one path per line") 553 | os.exit() 554 | end 555 | print("multiplepaths: Found " .. #paths .. " paths") 556 | ``` 557 | #### paths.txt 558 | 559 | ``` 560 | {"ver":"1.0","soa":{"rpc":"1.2649","req":"86eca521-34ce-4f95-b114-ae966e2e04bc"},"iface":"me.ele.napos.order.proxy.api.TestService","method":"test","args":{},"metas":{}} 561 | {"ver":"1.0","soa":{"rpc":"1.2649","req":"86eca521-34ce-4f95-b114-ae966e2e04bc"},"iface":"me.ele.napos.order.proxy.api.QueryOrderService","method":"getUnprocessedOrders","args":{"restaurantId":"59968"},"metas":{}} 562 | ``` 563 | 564 | ### ApacheBench 565 | 566 | > ab 压力测试工具 567 | 568 | #### 参考资料 569 | - http://www.ha97.com/4617.html 570 | 571 | #### ApacheBench参数说明 572 | 573 | ``` 574 | 格式:ab [options] [http://]hostname[:port]/path 575 | 参数说明: 576 | -n requests Number of requests to perform 577 | //在测试会话中所执行的请求个数(本次测试总共要访问页面的次数)。默认时,仅执行一个请求。 578 | -c concurrency Number of multiple requests to make 579 | //一次产生的请求个数(并发数)。默认是一次一个。 580 | -t timelimit Seconds to max. wait for responses 581 | //测试所进行的最大秒数。其内部隐含值是-n 50000。它可以使对服务器的测试限制在一个固定的总时间以内。默认时,没有时间限制。 582 | -p postfile File containing data to POST 583 | //包含了需要POST的数据的文件,文件格式如“p1=1&p2=2”.使用方法是 -p 111.txt 。 (配合-T) 584 | -T content-type Content-type header for POSTing 585 | //POST数据所使用的Content-type头信息,如 -T “application/x-www-form-urlencoded” 。 (配合-p) 586 | -v verbosity How much troubleshooting info to print 587 | //设置显示信息的详细程度 – 4或更大值会显示头信息, 3或更大值可以显示响应代码(404, 200等), 2或更大值可以显示警告和其他信息。 -V 显示版本号并退出。 588 | -w Print out results in HTML tables 589 | //以HTML表的格式输出结果。默认时,它是白色背景的两列宽度的一张表。 590 | -i Use HEAD instead of GET 591 | // 执行HEAD请求,而不是GET。 592 | -x attributes String to insert as table attributes 593 | -y attributes String to insert as tr attributes 594 | -z attributes String to insert as td or th attributes 595 | -C attribute Add cookie, eg. -C “c1=1234,c2=2,c3=3” (repeatable) 596 | //-C cookie-name=value 对请求附加一个Cookie:行。 其典型形式是name=value的一个参数对。此参数可以重复,用逗号分割。 597 | 提示:可以借助session实现原理传递 JSESSIONID参数, 实现保持会话的功能,如 598 | -C ” c1=1234,c2=2,c3=3, JSESSIONID=FF056CD16DA9D71CB131C1D56F0319F8″ 。 599 | -H attribute Add Arbitrary header line, eg. ‘Accept-Encoding: gzip’ Inserted after all normal header lines. (repeatable) 600 | -A attribute Add Basic WWW Authentication, the attributes 601 | are a colon separated username and password. 602 | -P attribute Add Basic Proxy Authentication, the attributes 603 | are a colon separated username and password. 604 | //-P proxy-auth-username:password 对一个中转代理提供BASIC认证信任。用户名和密码由一个:隔开,并以base64编码形式发送。无论服务器是否需要(即, 是否发送了401认证需求代码),此字符串都会被发送。 605 | -X proxy:port Proxyserver and port number to use 606 | -V Print version number and exit 607 | -k Use HTTP KeepAlive feature 608 | -d Do not show percentiles served table. 609 | -S Do not show confidence estimators and warnings. 610 | -g filename Output collected data to gnuplot format file. 611 | -e filename Output CSV file with percentages served 612 | -h Display usage information (this message) 613 | //-attributes 设置属性的字符串. 缺陷程序中有各种静态声明的固定长度的缓冲区。另外,对命令行参数、服务器的响应头和其他外部输入的解析也很简单,这可能会有不良后果。它没有完整地实现 HTTP/1.x; 仅接受某些’预想’的响应格式。 strstr(3)的频繁使用可能会带来性能问题,即你可能是在测试ab而不是服务器的性能。 614 | 615 | 参数很多,一般我们用 -c 和 -n 参数就可以了。例如: 616 | 617 | # 网址最后一个必须是/ 618 | # ab -c 5000 -n 600 http://www.163.com/ 619 | ``` 620 | 621 | #### 结果分析 622 | 623 | ``` 624 | Document Path: /phpinfo.php 625 | #测试的页面 626 | Document Length: 50797 bytes 627 | #页面大小 628 | 629 | Concurrency Level: 1000 630 | #测试的并发数 631 | Time taken for tests: 11.846 seconds 632 | #整个测试持续的时间 633 | Complete requests: 4000 634 | #完成的请求数量 635 | Failed requests: 0 636 | #失败的请求数量 637 | Write errors: 0 638 | Total transferred: 204586997 bytes 639 | #整个过程中的网络传输量 640 | HTML transferred: 203479961 bytes 641 | #整个过程中的HTML内容传输量 642 | Requests per second: 337.67 [#/sec] (mean) 643 | #最重要的指标之一,相当于LR中的每秒事务数,后面括号中的mean表示这是一个平均值 644 | Time per request: 2961.449 [ms] (mean) 645 | #最重要的指标之二,相当于LR中的平均事务响应时间,后面括号中的mean表示这是一个平均值 646 | Time per request: 2.961 [ms] (mean, across all concurrent requests) 647 | #每个连接请求实际运行时间的平均值 648 | Transfer rate: 16866.07 [Kbytes/sec] received 649 | #平均每秒网络上的流量,可以帮助排除是否存在网络流量过大导致响应时间延长的问题 650 | Connection Times (ms) 651 | min mean[+/-sd] median max 652 | Connect: 0 483 1773.5 11 9052 653 | Processing: 2 556 1459.1 255 11763 654 | Waiting: 1 515 1459.8 220 11756 655 | Total: 139 1039 2296.6 275 11843 656 | #网络上消耗的时间的分解,各项数据的具体算法还不是很清楚 657 | 658 | Percentage of the requests served within a certain time (ms) 659 | 50% 275 660 | 66% 298 661 | 75% 328 662 | 80% 373 663 | 90% 3260 664 | 95% 9075 665 | 98% 9267 666 | 99% 11713 667 | 100% 11843 (longest request) 668 | #整个场景中所有请求的响应情况。在场景中每个请求都有一个响应时间,其中50%的用户响应时间小于275毫秒,66%的用户响应时间小于298毫秒,最大的响应时间小于11843毫秒。对于并发请求,cpu实际上并不是同时处理的,而是按照每个请求获得的时间片逐个轮转处理的,所以基本上第一个Time per request时间约等于第二个Time per request时间乘以并发请求数。 669 | ``` 670 | 671 | # 包管理工具 672 | 673 | ## Glide 674 | 675 | - `glide create / init`: 初始化一个新的项目,创建一个`glide.yml` 676 | - `glide get`: 安装一个或者更多的包到`vendor/`,并添加依赖到 `glide.yml` 677 | - `glide remove / rm ` 678 | 679 | > 参考资料:https://blog.wu-boy.com/2016/05/package-management-for-golang-glide/ 680 | 681 | # 参考资料 682 | 683 | * 学习GO语言:https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/directory.md 684 | * GO语言圣经:https://docs.ruanjiadeng.com/gopl-zh/index.html -------------------------------------------------------------------------------- /FE_knowledge.md: -------------------------------------------------------------------------------- 1 | # 前端知识点 2 | 3 | - `HTML&CSS`:语义化、布局、盒子模型、选择器优先级、flexbox、栅格化、浏览器渲染原理、CSS优化、SEO、移动端(兼容、bug、坑)多屏 4 | - `Javascript`:数据类型、运算符优先级、继承、作用域、Function、DOM、BOM、闭包、异步、原型链、事件、正则、Ajax、路由、模块化、内存泄露、this上下文、按需加载、数组操作、排序、去重、DomReady、ES5方法 5 | - `原理`:angular、vue、react、promise、webview、react-native、HTTP协议(状态码等) 6 | - `性能`:chrome timeline、profile、nodejs等 7 | - `测试`:mocha/jasmine/spy 8 | - `语言`:php、c#、nodejs、java、go、python 9 | - `部署`: linux、docker、nginx、haproxy、lua、LVS 10 | 11 | 12 | # HTML 13 | 14 | ## 语义化 15 | 16 | 书写html尽量使用具有语义化信息的标签,例如header、nav等;有利于DOM结构组织、利于搜索引擎(SEO)理解,提升使用网络获取信息的检验。 17 | 18 | > 自定义标签(元素)方法 19 | 20 | - document.createElement 21 | - [document.registerElement](http://blog.bingo929.com/custom-elements-html-web-components.html) 22 | - `*` 通配符方法: `body > * p {}` 23 | 24 | ``` 25 | 35 | 43 | ``` 44 | 45 | - 使用命名空间 46 | 47 | ``` 48 | 修改标签处的命名空间 49 | 使用类似标签 50 | 使用如下选择器名称进行控制:html5\:section {} 51 | 52 | 53 | ... 54 | 55 | 56 | 57 | .section, section, html5\:section { 58 | display: block; 59 | padding: .5em; 60 | border-left: 3px solid #ddd; 61 | color: #999; 62 | } 63 | ``` 64 | 65 | - 使用框架:react/vue/angular 66 | 67 | ## 布局 68 | 69 | > 固定布局、流式布局、响应式布局、弹性布局、多列布局、栅格化布局 70 | 71 | ### 流式布局:三列布局自适应布局 72 | 73 | ``` 74 | // Dom 结构 75 |
      77 |
      78 | 79 | // style 80 | .left { float: left; width: 100px; } 81 | .right { float: right: width: 100px;} 82 | .center { } 83 | ``` 84 | 85 | 品字布局: 86 | 87 | ``` 88 | // Dom 结构 89 |
      90 |
      91 |
      92 | 93 | // style 94 | .top { width: 100%; clear: both; } 95 | .left { float: left; width: 50%; } 96 | .right { margin-left: 50%; } 97 | ``` 98 | 99 | ### 响应式布局:Media Query 100 | 101 | ### 弹性布局:Flex box 102 | >(IE10+兼容)微信、UC不支持(可使用display:inline-box兼容),优势:设置等高对齐 103 | 104 | http://www.w3cplus.com/css3/a-guide-to-flexbox-new.html 105 | 106 | **兼容性** 107 | 108 | http://www.w3cplus.com/css3/harnessing-flexbox-for-todays-web-apps.html 109 | 110 | ``` 111 | - PC端: 112 | 1、无前缀:Chrome 29+, Firefox 28+, IE 11+, Opera 17+ 113 | 2、需要前缀:Chrome 21+, Safari 6.1+, Opera 15+需要前缀-webkit- 114 | 提示:旧版本的Firefox(22-27)支持除了flex-wrap和flex-flow之外的新语法。Opera (12.1+ - 17+)使用flex可以没有私有前缀,但是中间的15和16版本需要私有前缀。 115 | 116 | - 支持中间语法的浏览器 117 | PC端和移动端:IE10(使用-ms-私有前缀)。 118 | 119 | - 支持旧语法的浏览器 120 | 所有PC端和移动端的浏览器都需要加-webkit-的私有前缀(除了firefox需要使用-moz-前缀)。 121 | 122 | PC端:Firefox 2 – 21, Chrome 4 – 20, Safari 3.1 – 6 123 | 移动端Android 2.1 – 4.3, iOS 3.2 – 6.1, UC browser 9.9 on Android, BlackBerry 7 124 | 125 | - 不支持的浏览器 126 | PC端:IE9和Opera12以下 127 | 移动端Opera Mini 128 | ``` 129 | 130 | ### 多列布局 131 | 132 | #### 相关属性 (IE10+) 133 | 134 | ``` 135 | column-width: 200px; // 每列宽度 136 | column-count: 3; // 每行显示几列 137 | column-gap: 1.5em; // 列间距 138 | 139 | // 列样式 140 | column-rule:3px outset #ff00ff; 141 | column-rule: [width] [style] [color]; 142 | column-rule-style 143 | column-rule-width 144 | column-rule-color 145 | 146 | // 单列跨度:代表该列占整行 147 | column-span: all; or none 148 | 149 | // 截断 150 | break-inside: avoid; 151 | break-before: avoid; 152 | ``` 153 | 154 | ### 栅格化布局(网格布局) 155 | 156 | #### 相关属性 IE10+部分支持,其它浏览器基本不支持,可以借助JS实现(憋足) 157 | 158 | ``` 159 | display: -ms-grid; 160 | -ms-grid-columns: 200px 20px auto 20px 250px; 161 | -ms-grid-rows: auto 1fr; 162 | -ms-grid-column: 1; 163 | -ms-grid-row: 2; 164 | ``` 165 | 166 | ## 选择器优先级 167 | 168 | ``` 169 | 1、!import 170 | 2、inline style 171 | 3、ID 172 | 4、CLASS 173 | 5、元素(标签) 174 | 6、子元素 > (IE7+) 175 | 7、兄弟选择 + (IE7+) 176 | 8、子选择 ~ (IE7+) 177 | 9、属性 178 | 10、伪类 179 | 11、动态伪类 180 | ``` 181 | 182 | ## CSS 渲染原理 183 | 184 | - 通过HTML Parse -> DOM Tree -> attachment 185 | - style sheet -> css parse -> attachment 186 | - attachment -> layout -> Render Tree -> Painting -> display 187 | - 如果DOM变化不影响几何属性,则只会发生repaint(而不是reflow 188 | 189 | ``` 190 | 由于每次reflow都会产生计算消耗,大多数浏览器都通过队列化修改(queuing changes)并批量执行来优化重排过程。 但是,获取布局信息的(返回最新的布局信息)操作会导致队列被强制刷新(浏览器马上执行渲染队列中的‘the pending changes’并触发重排以返回正确值), 191 | 比如: 192 | offset(Top|Left|Width|Height) 193 | scroll(Top|Left|Width|Height) 194 | client(Top|Left|Width|Height) 195 | getComputedStyle() (IE) 196 | ``` 197 | 198 | ``` 199 | repaint(重绘) ,repaint发生更改时,元素的外观被改变,且在没有改变布局的情况下发生,如改变outline,visibility,background color,不会影响到dom结构渲染。 200 | 201 | reflow(渲染),与repaint区别就是他会影响到dom的结构渲染,同时他会触发repaint,他会改变他本身与所有父辈元素(祖先),这种开销是非常昂贵的,导致性能下降是必然的,页面元素越多效果越明显。 202 | 203 | 何时发生: 204 | 205 | 1. DOM元素的添加、修改(内容)、删除( Reflow + Repaint) 206 | 2. 仅修改DOM元素的字体颜色(只有Repaint,因为不需要调整布局) 207 | 3. 应用新的样式或者修改任何影响元素外观的属性 208 | 4. Resize浏览器窗口、滚动页面 209 | 5. 读取元素的某些属性(offsetLeft、offsetTop、offsetHeight、offsetWidth、 scrollTop/Left/Width/Height、clientTop/Left/Width/Height、 getComputedStyle()、currentStyle(in IE)) 210 | 211 | 如何避免: 212 | 213 | 1. 先将元素从document中删除,完成修改后再把元素放回原来的位置 214 | 2. 将元素的display设置为”none”,完成修改后再把display修改为原来的值 215 | 3. 如果需要创建多个DOM节点,可以使用DocumentFragment创建完后一次性的加入document    216 | 217 | var fragment = document.createDocumentFragment(); 218 | fragment.appendChild(document.createTextNode('keenboy test 111')); 219 | fragment.appendChild(document.createElement('br')); 220 | fragment.appendChild(document.createTextNode('keenboy test 222')); 221 | document.body.appendChild(fragment); 222 | 223 | 4. 集中修改样式 224 |   4.1尽可能少的修改元素style上的属性 225 |   4.2尽量通过修改className来修改样式 226 |   4.3通过cssText属性来设置样式值 227 |     element.style.width=”80px”; //reflow 228 |     element.style.height=”90px”; //reflow 229 |     element.style.border=”solid 1px red”; //reflow 230 |     以上就产生多次reflow,调用的越多产生就越多 231 |     element.style.cssText=”width:80px;height:80px;border:solid 1px red;”; //reflow  232 |   4.4缓存Layout属性值 233 |     var left=elem.offsetLeft; 多次使用left也就产生一次reflow 234 |   4.5设置元素的position为absolute或fixed 235 |     元素脱离标准流,也从DOM树结构中脱离出来,在需要reflow时只需要reflow自身与下级元素 236 |   4.6尽量不要用table布局 237 |     table元素一旦触发reflow就会导致table里所有的其它元素 reflow。在适合用table的场合,可以设置table-layout为auto或fixed,这样可以让table一行一行的渲染,这种做法也是为了限制reflow的影响范围 238 |   4.7避免使用expression,他会每次调用都会重新计算一遍(包括加载页面) 239 | ``` 240 | 241 | ## 移动端 242 | 243 | ### 理论 244 | 245 | > 尺寸 246 | 247 | IOS: 248 | 249 | - iphone 4/4s : 640*960 250 | - iphone 5/5s/5c : 640*1136 251 | - iphone 6: 750*1334 252 | - iphone 6plus: 1080 * 1920/1125 * 2001/1242 * 2208 253 | 254 | Android: 255 | 256 | - 480 * 800 257 | - 720 * 1280 258 | - 1080 * 1920 259 | 260 | > 屏幕: 设备像素比 = 物理像素 / 设备独立像素 // 在某一方向上,x方向或者y方向 261 | 262 | - 物理像素 263 | - 设备独立像素:设备宽高为375×667,可以理解为设备独立像素(或css像素) 264 | - 设备像素比 dpr 265 | 266 | ``` 267 | 在javascript中,可以通过window.devicePixelRatio获取到当前设备的dpr。 268 | 269 | IE以及FireFox压根不支持。可能接下来的版本会支持。 270 | Opera桌面浏览器时,即使是视网膜设备,返回的值也是1而不是2. 不过,这个bug在后续的版本中会修复的。 271 | Opera Mobile 10不支持,不过Opera Mobile 12正确支持。 272 | UC总是显示1,不过其viewport属性有些让人费解。 273 | 只有最近的Chrome浏览器才能正确实现该属性。Chrome19返回不准确的1, Chrome22可以正确返回2. 274 | MeeGo WebKit (Nokia N9/N950)就吓人了:当你应用了meta viewport时候(类似),值会从1变成1.5! 275 | 276 | 在css中,可以通过 277 | -webkit-device-pixel-ratio, 278 | -webkit-min-device-pixel-ratio和 279 | -webkit-max-device-pixel-ratio 280 | 进行媒体查询,对不同dpr的设备,做一些样式适配(这里只针对webkit内核的浏览器和webview)。 281 | ``` 282 | > 多屏适配 283 | 284 | 公式: `rem = document.documentElement.clientWidth * dpr / 10` 285 | 286 | font-size示例:http://div.io/topic/1092 287 | 288 | ``` 289 | // Javascript 方式 290 | iphone3gs: 320px / 10 = 32px 291 | iphone4/5: 320px * 2 / 10 = 64px 292 | iphone6: 375px * 2 / 10 = 75px 293 | 294 | // css 方式 295 | html{font-size: 32px;} 296 | //iphone 6 297 | @media (min-device-width : 375px) { 298 | html{font-size: 64px;} 299 | } 300 | // iphone6 plus 301 | @media (min-device-width : 414px) { 302 | html{font-size: 75px;} 303 | } 304 | */ 305 | ``` 306 | 307 | ### 移动端兼容问题 308 | 309 | ``` 310 | 1、tap事件穿透(解决方案:click事件代替, 或者尝试fastclick这个框架) 311 | 2、fixed的元素有input框时在ios上的bug(可以考虑头和底部定高,中间加上一个iScroll的内容区域实现头尾固定,中间内容滑动的UI交互布局); 312 | 3、离线缓存更新成功后必须刷新页面才能显示新的修改(写个全局的方法,监听updateready后,主动帮用户刷新一次页面); 313 | 4、UC浏览器不支持alert(建议用自己通用的弹窗方法); 314 | 5、同样的zepto写的选择器,有时候层级过深在某些浏览器中失效(在节点class和id上命名上合理分配,用常规选择器串); 315 | 6、QQ浏览器SVG失效; 316 | 7、chrome和小米自带的手机浏览器,开发调试时不走代理(可以下载chrome的beta版) 317 | 318 | ======================================================== 319 | 320 | 1、最痛恨的是红米手机,ua返回iphone,需要结合platform判断,但是还不准确,导致需要ios和android区别对待的时候就坑了。 321 | 2、是fixed的问题。这个解决办法是尽量不要用,不过ios7及以下才会出现这个问题。某些情况下红米也会有这个问题。(最近刚刚遇到,已经被坑挂了)。 322 | 3、如果你想要使用css3的动画,那么一定要变着方式使用3d gpu加速的方式,不要试着left,height,width这样的元素进行变换了,android4.4以下版本卡死你。 323 | 4、ios全线点击会有300毫秒延迟,使用fastclick解决。这个插件最良心了。 324 | 5、web app像素眼设计会纠结你1px边框问题。解决办法有相应知乎大牛答过。 325 | 6、qq浏览,uc浏览以及ios的浏览器,滚动时不会触发scroll事件,但会触发touchmove。当停止滚动后会出发scroll。 326 | 7、滚动有iscroll插件,但是还是使用原生的比较好。 327 | 8、meta功能要用好,禁止缩放,缩放比例,屏蔽电话号码等功能很实用。(手机回答就不列举了)。 328 | 9、如果想要像手机淘宝那样的各个平台看起来展示效果一致,那么就使用rem来做单位。 329 | 10、-webkit-tap-highlight-color可以取消点击高亮。 330 | 11、localStorage在浏览器开启无痕模式下ios会抛异常,导致js中断。 331 | 12、一些情况下对非可点击元素监听click事件,ios下不会触发,css增加cursor:pointer就搞定了。当然想要干脆静止点击就是not-allowed。 332 | 13、android4.4以下版本,设置圆角属性需要在直接元素上,向父元素设置圆角并且指定overflow:hidden是不会生效的。 333 | 334 | 1、运营商劫持文件命名(CSS/JS/HTML)如game、ad等 335 | 336 | 337 | scroll元素临界的bug 338 | android screen.w/h 不准 339 | rem不准 340 | scroll时动画失效 341 | animate回调 342 | 最小字号限制 343 | 不同机型全屏自适应 android,ios和native通讯 下载app方案ios,android,微信都不一样 二维码识别 344 | 345 | border-radius不要随便乱用,在很多安卓机型上都会出现锯齿,非常丑 346 | 347 | IOS8 的 webview 加载网页,不支持使用 createElement 创建 video 元素播放 hls 视频流,然后动态指定 parent,会导致 app 崩溃。并且,webview 视频内嵌播放模式下点击全屏按钮会导致 app 崩溃。 348 | 349 | 350 | 351 | 1.iPhone5以上各种应用的webview(例如微信)在遇到有大量图片的页面时会出现图片乱套的情况,6和6plus更为严重,具体表现为各种img和background-image互相错乱,困扰了我们好久,简直是大坑,目前研究出暂时的解决方法是动态给所有用到图片的元素加上-webkit-transform : translate3d(0,0,0) 352 | 进行强制重绘,测试结果对于绝大部分出现问题的机子有效,但仍然有小部分机器还是会出问题,另一种方法是进行懒加载,但是这会降低开发效率,并且对使用background-image的元素比较难实现 353 | 354 | 2.safari(包括OS X、iOS和Windows版)对transform-origin的Z轴判定和其它所有浏览器都不同,设置transform-origin的Z轴会直接产生translateZ的变换,十分不理解,暂无纯css解法 355 | 356 | 3.Android调用重力传感器返回的数据和iOS和Windows Phone上的是相反的…… 357 | 358 | 4.Android微信内置webview对meta viewport的支持有缺陷,当user-scalable=no时,设定width为固定数值(例如640)不会自动缩放,需要用js做一些处理(新版微信已经修正了这个问题) 359 | 360 | 5.Android的各种浏览器都不支持同时播放多个音频,并且通过js连续播放几个音频的时候会出现一些问题 361 | 362 | 363 | 364 | 0. Android 4.x 一些版本 input file 被从底层阉割,文件选择框 打不开也不报错 —— http://www.zhihu.com/question/21452742 365 | 366 | 1. iScroll 5 官方示例 拖到本地开发环境就完全动不了,无论 Chrome 移动设备模拟器 还是 微信内置浏览器 —— 弃之不用,手写下拉刷新…… 367 | 368 | 2. ECharts 的雷达图在 iOS WebView 中拖拽 容易使整个 ViewPort 高宽突破 meta 标签的限制 369 | 370 | 3. Animate.css 中动作幅度较大的动画在用 腾讯 X5 内核的 App 中卡顿明显(鹅厂号称的基于 Chrome 完整内核优化 哪儿去了?MIUI 原生浏览器 都超流畅) 371 | 372 | 4. WebKit 533-(微信、UC v9.8 开发者版)在遍历 带伪元素规则的 CSSStyleRule 对象时,会直接让浏览器崩溃(下断点走单步时基本没事,一释放断点立马崩溃,所以怀疑是栈溢出)—— 检测到老内核 直接跳过那段代码……(对老浏览器砍功能) 373 | 374 | 5. a 标签 href 仅指向一个不存在的 hash,点击回调中根据 hash 内容调用相应功能,并阻止默认行为 —— Android、iOS 上各种 WebKit 浏览器 都会清空历史记录(执行了 a 标签的默认行为),导致单页应用无法回退……(桌面端浏览器 均无此问题) 375 | 376 | 6. 微信 iOS 版 整页切换到 动态生成的表单,其提交按钮会自动点击,导致空白表单提交 —— 自己给 iOS WebKit 补上砍掉的 HTML 5 表单元素验证方法,提交前先验证 HTML 代码中的 required、pattern 属性,否则阻止提交事件 377 | 378 | 379 | 做过html5游戏的人应该深有体会webaudio有多坑android很多设备不支持,ios6和ios8的api不一样。 380 | 我现在的解决办法就是判断ios用webaudio,如果是android就用audio标签,但是还是无法完美解决。 381 | ``` 382 | 383 | #JAVASCRIPT 384 | 385 | ## 数据类型 386 | 387 | > 类型转换 388 | 389 | parseInt: 字符串转换成整数,同时还有基模式,即方法的第二个参数,如果十进制数包含前导0,那么最好采用基数10,这样才不会意外地得到八进制的值,如parseInt(010)=8进制数 390 | 391 | ``` 392 | parseInt("1234blue"); //returns 1234 393 | parseInt("0xA"); //returns 10 394 | parseInt("22.5"); //returns 22 395 | parseInt("blue"); //returns NaN 396 | parseInt("AF", 16); //returns 175 16进制 397 | ``` 398 | 399 | parseFloat:浮点型转换 400 | 401 | ``` 402 | parseFloat("1234blue"); //returns 1234.0 403 | parseFloat("0xA"); //returns NaN 404 | parseFloat("22.5"); //returns 22.5 405 | parseFloat("22.34.5"); //returns 22.34 406 | parseFloat("0908"); //returns 908 407 | parseFloat("blue"); //returns NaN 408 | ``` 409 | 410 | 强制类型转换 411 | 412 | - Boolean(value)——把给定的值转换成Boolean型; 413 | - Number(value)——把给定的值转换成数字(可以是整数或浮点数); 414 | - String(value)——把给定的值转换成字符串。 415 | - Object.prototype.valueOf() 返回对象的原始值 416 | 417 | ``` 418 | var x = { 419 | toString: function () { return "foo"; }, 420 | valueOf: function () { return 42; } 421 | }; 422 | 423 | alert(x); // foo 424 | "x=" + x; // "x=42" 425 | x + "=x"; // "42=x" 426 | x + "1"; // 421 427 | x + 1; // 43 428 | ["x=", x].join(""); // "x=foo" 429 | ``` 430 | 431 | - instanceof 仅限类型比较 432 | 433 | ``` 434 | 1 instanceof Number; //false 435 | new Number(1) instanceof Number; //true 436 | ``` 437 | 438 | ## 表达式&&运算符 439 | 440 | > 运算符优先级与结合性:从左到右,从右到左 441 | > 442 | http://www.cnblogs.com/dolphinX/p/3524977.html 443 | 444 | - typeof 优先级最高 445 | - 赋值运算符的优先级最低 446 | 447 | ``` 448 | !、-(单目减)、++、--、typeof, new, void, delete 2 449 | *、/、% 3 450 | +、- 4 451 | <<、>>、>>> 5 452 | <、<=、<、>= 6 453 | ==、!=、===、!==、 7 454 | & 8 455 | ^ 9 456 | | 10 457 | && 11 458 | || 12 459 | ?: 13 460 | =、+=、-=、*=、/=、%=、<<=、>>=、>>>=、&=、^=、|= 14 461 | ``` 462 | 463 | 由右往左计算的运算符: 464 | 465 | ++、--、-、+、~、!、delete、typeof、void、?:、=赋值 *=、/=、+=、-= 466 | 467 | 由左往右计算的运算符: 468 | 469 | *、/、%、+、-、+、<、<=、>、>=、instanceof、in、==、!=、&&、 || 470 | 471 | ## OOP及其继承 472 | 473 | 1、原型链继承 474 | 475 | ``` 476 | fuction parent(){} 477 | var child = new parent(); 478 | 479 | child.prototype = new parent() 480 | ``` 481 | 482 | 2、构造函数继承 483 | ``` 484 | function parent(){ 485 | this.x = 6; 486 | } 487 | parent.prototype.send = function(){ console.log('s') } 488 | function child(){ 489 | parent.call(this); 490 | console.log(this.send) 491 | } 492 | (){ console.log('s') } 493 | ``` 494 | 3、ES5 Object.create(xx.protype) 495 | 4、ES6 super 496 | 497 | 498 | ## 作用域 499 | > 同名变量的查找顺序,后定义覆盖先定义。原型链是用来查找对象属性的。作用域链是一个对象列表或对象链 500 | 501 | 基础:https://github.com/kuitos/kuitos.github.io/issues/18 502 | 503 | ### setTimeout/setInterval 504 | 505 | - 描述说明:http://www.cnblogs.com/hutaoer/p/3423782.html 506 | - 执行原理:http://www.suchso.com/projecteactual/Javascript-setTimeout-timer.html 507 | 508 | - 默认setTimeout(str)是全局作用域(window/global) 509 | - settimeout延时为0的目标,是为了达到异步任务的作用,而异步会写到队列里所有会执行完同步任务才会去执行队伍里的异步任务 510 | 511 | ``` 512 | alert(1); 513 | setTimeout("alert(2)", 0); 514 | alert(3); 515 | 虽然延时了0ms,但是执行顺序为:1,3,2 516 | ``` 517 | 518 | 一、setTimeout中的延迟执行代码中的this永远都指向window 519 | 520 | 二、setTimeout(this.method, time)这种形式中的this,即上文中提到的第一个this,是根据上下文来判断的,默认为全局作用域,但不一定总是处于全局下,具体问题具体分析。 521 | 522 | 三、setTimeout(匿名函数, time)这种形式下,匿名函数中的变量也需要根据上下文来判断,具体问题具体分析。在这里匿名函数的使用形成了一个闭包,从而能访问到外层函数的局部变量。这样子去理解,我觉得挺好的!只是这种闭包,跟常见的闭包不同,因为函数式放在setTimeout里面 523 | 524 | ## Function 525 | > Function 对象的 length 属性,可以用来统计function的参数个数,例如func(x,y).length = 2 526 | 527 | ## 事件 528 | 529 | > 冒泡和捕获 530 | 531 | - 冒泡从特定事件目标冒泡到document 532 | - 捕获从最不精确的(document)开始捕获,也可以从窗口级别捕获。 533 | - 捕获先于冒泡 534 | - 捕获事件的陷阱和对策:http://hax.iteye.com/blog/162718 不同的浏览器对event Flow定义不一样,有可能导致事件不被捕获或者调用两次的可能。 535 | 536 | ## Ajax 537 | - 创建对象: 538 | 539 | ``` 540 | new XMLHttpRequest(): Chrome, IE7+, Firefox, Safari, and Opera 541 | 542 | new ActiveXObject("Microsoft.XMLHTTP"): IE6 543 | ``` 544 | 545 | - Request 546 | 547 | ``` 548 | // GET PUT POST PATCH DELETE 549 | xhttp.open("GET", "ajax_info.txt", true); // 第三个参数 false代表同步 550 | xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); // 设置头 551 | xhttp.send("fname=Henry&lname=Ford"); // 如果是POST请求,则传入string 552 | ``` 553 | - Response 554 | 555 | ``` 556 | xhr.responseText 557 | xhr.responseXML 558 | xhr.onreadystatechange 559 | xhr.readyState 560 | xhr.status 561 | xhr.responseType 562 | ``` 563 | 564 | ``` 565 | var xhttp, xmlDoc, txt, x, i; 566 | xhttp = new XMLHttpRequest(); 567 | xhttp.onreadystatechange = function() { 568 | if (xhttp.readyState == 4 && xhttp.status == 200) { 569 | xmlDoc = xhttp.responseXML; 570 | txt = ""; 571 | x = xmlDoc.getElementsByTagName("ARTIST"); 572 | for (i = 0; i < x.length; i++) { 573 | txt = txt + x[i].childNodes[0].nodeValue + "
      "; 574 | } 575 | document.getElementById("demo").innerHTML = txt; 576 | } 577 | }; 578 | xhttp.open("GET", "cd_catalog.xml", true); 579 | xhttp.send(); 580 | ``` 581 | XMLHttpRequest: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest 582 | XMLHttpRequest2: http://www.html5rocks.com/zh/tutorials/file/xhr2/ 583 | 584 | 更多: 585 | 586 | ``` 587 | - xhr.overrideMimeType 588 | //overrideMimeType() 必须在 send() 之前设置 589 | xhr.overrideMimeType("text/plain; charset=utf-8"); 590 | 591 | - xhr.abort 中止 592 | 593 | 594 | ``` 595 | ## 跨域 596 | 597 | 跨域:https://segmentfault.com/a/1190000000702539 598 | 599 | 跨域2:https://segmentfault.com/a/1190000000702550 600 | 601 | 总结:http://www.cnblogs.com/rainman/archive/2011/02/20/1959325.html 602 | 603 | - iframe 604 | 605 | - jsonp 606 | 607 | - cors: Access-Control-Allow-Origin 608 | 609 | - domain 610 | 611 | - postmessage html5 612 | 613 | ## DOM操作 614 | 615 | - createElement // 创建元素 616 | - querySelector/querySelectorAll // 查询 617 | - node.parentElement/node.parentNode 618 | - node.children 619 | - node.getElementsByTagName/getElementsByClassName 查询子元素 620 | - el. firstElementChild/lastElementChild 621 | - el. nextElementSibling/previousElementSibling 622 | - el. appendChild/removeChild/replaceChild 623 | - parentElement.insertBefore(newElement, referenceElement) 624 | - el.attributes /// 获取一个{name, value}的数组 625 | - el. getAttribute/setAttribute 626 | - el. hasAttribute/removeAttribute 627 | - el. hasAttributes // 是否有属性设置 628 | - doc. createDocumentFragment 629 | - innerText/innerHTML 630 | - event.target 631 | 632 | ## 数组方法 633 | 634 | - concat 635 | - join 636 | - pop 637 | - push 638 | - reverse 639 | - shift 删除并返回数组的第一个元素 640 | - slice 641 | - sort 642 | - splice 删除元素,并向数组添加新元素。splice(index,howmany,item1,.....,itemX) 643 | - unshift 数组开头添加一个或多个元素 644 | 645 | ## ES5方法 646 | 647 | 更多: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object 648 | 649 | ### Object 650 | 651 | - Object.create(proto[, propertiesObject]) 652 | - `__proto__` 指向其构造器的prototype,即父类的prototype 653 | - Object.defineProperties(obj, props) 654 | 655 | ``` 656 | Object.defineProperties(obj, { 657 | "property1": { 658 | value: true, 659 | writable: true 660 | }, 661 | "property2": { 662 | value: "Hello", 663 | writable: false 664 | } 665 | // etc. etc. 666 | }); 667 | ``` 668 | 669 | - Object.defineProperty(obj, prop, descriptor) 670 | - Object.getOwnPropertyNames 671 | - Object.getOwnPropertyDescriptor 672 | 673 | ## 排序 674 | > 冒泡排序,去重 675 | 676 | - 冒泡排序: 677 | 678 | 1: https://segmentfault.com/a/1190000000471260 679 | 2: http://www.cnblogs.com/georgewing/archive/2008/12/17/1356916.html 680 | 681 | - 快速排序:http://www.ruanyifeng.com/blog/2011/04/quicksort_in_javascript.html 682 | 683 | - 更多排序:http://developer.51cto.com/art/201102/244855.htm 684 | - 去重: http://www.benben.cc/blog/?p=335 685 | - 去重2:http://hao.jser.com/archive/4248/ 686 | 687 | ## 模块化 688 | 689 | > AMD/CMD/UMD: UMD = AMD + CMD 690 | 691 | 692 | ``` 693 | AMD | 速度快 | 会浪费资源 | 预先加载所有的依赖,直到使用的时候才执行 694 | CMD | 只有真正需要才加载依赖 | 直到使用的时候才定义依赖 695 | 696 | CMD推崇依赖就近,可以把依赖写进你的代码中的任意一行,是弱依赖的一种,开发效率为先,性能较差 697 | AMD是依赖前置的,换句话说,在解析和执行当前模块之前,模块作者必须指明当前模块所依赖的模块,是强依赖的一种,预先加载,性能较好 698 | 699 | 目前推崇的是依赖ES6 MODULE/Webpack/Browserify的集成方案去解决,实现按需加载 700 | 701 | ``` 702 | 703 | ## 正则 704 | 705 | ## Promise原理 706 | 707 | 708 | 709 | 710 | -------------------------------------------------------------------------------- /JavaScript_Style_Guide.md: -------------------------------------------------------------------------------- 1 | # JavaScript Style Guide 2 | 3 | ## EditorConfig 4 | 5 | 建议选择支持 [EditorConfig](http://editorconfig.org/) 的编辑器,并安装相应插件,在项目根目录建立一个 `.editconfig` 并插入以下内容: 6 | 7 | ``` 8 | [*.js] 9 | charset = utf-8 10 | end_of_line = lf 11 | insert_final_newline = true 12 | indent_style = space 13 | indent_size = 2 14 | trim_trailing_whitespace = true 15 | ``` 16 | 17 | ## File Encodings 18 | 19 | 确保编辑器 / IDE 使用的编码为 UTF-8 without BOM。如不能确定使用的编辑器 / IDE 的编码,请使用 Sublime Text、WebStorm 等工具,并保持默认编码设置。 20 | 21 | ## Naming Conventions 22 | 23 | ### Files 24 | 25 | - 使用半角小写英文字母(单词)和数字 26 | - 使用半角的 `-` 和 `.` 分割单词 27 | - 如非必要,请勿包含版本号 28 | - 以依赖库作为前缀 29 | - 除了已压缩文件外,请勿添加 `.min` 30 | 31 | 示例: 32 | 33 | proj-core.js 34 | jquery.slideshow.js 35 | 36 | ### Codes 37 | 38 | - 命名需选择有意义的名字,除了默认成俗的名字外,尽量不要使用简写形式 39 | - 虽然 JavaScript 支持 Unicode 字符,但请使用 `[a-zA-Z0-9_\$]` 40 | - 使用[驼峰命名法](http://zh.wikipedia.org/wiki/%E9%A7%9D%E5%B3%B0%E5%BC%8F%E5%A4%A7%E5%B0%8F%E5%AF%AB)(camel case),变量和函数使用小驼峰,如 `getElementById`,类、构造函数和枚举类型使用大驼峰,如 `DocumentFragment` 41 | - 常量所有字母均大写并使用 `_` 分割单词,如 `Number.MAX_VALUE` 42 | - 尊重惯例,避免使用常用库、框架名,如 `$ = {}`、`jQuery = {}`、`_ = {}` 43 | - 使用 `_` 作为私有变量的前缀,如 `_index = arr.indexOf(value)` 44 | - `Boolean` 变量、函数请加上 `is`、`has` 前缀 45 | - 当保存 `this` 对象时,尽量使用可对应上下文的命名,或使用 `_this` 46 | 47 | 示例: 48 | 49 | // bad 50 | valid = true; 51 | k = 'word' 52 | hasAttr = function (e, a) { 53 | var _a = e.getAttribute(a); 54 | return _a === null; 55 | }; 56 | 57 | function user (opts) { 58 | this.name = opts.name; 59 | this.ID = opts.ID; 60 | } 61 | 62 | $form.on('submit', function(){ 63 | var that = this; 64 | }); 65 | 66 | // good 67 | isValid = true; 68 | keyword = 'word'; 69 | hasAttr = function (elem, attr) { 70 | var _attr = elem.getAttribute(attr); 71 | return _attr === null; 72 | } 73 | 74 | function User (options) { 75 | this.name = options.name; 76 | this.id = options.id; 77 | } 78 | 79 | $form.on('submit', function(){ 80 | var rootForm = this; 81 | // or 82 | var _this = this; 83 | }); 84 | 85 | ## Strict Mode 86 | 87 | 建议使用 `'use strict'`。 88 | 89 | 因浏览器端的代码一般会进行合并处理,而 Node.js 端代码则不需要,因此建议分开处理。 90 | 91 | 针对浏览器端的代码: 92 | 93 | ```js 94 | ~function(){ 95 | 'use strict'; 96 | }() 97 | ``` 98 | 99 | 针对 Node.js 端的代码: 100 | 101 | ```js 102 | 'use strict'; 103 | 104 | module.exports = function () {}; 105 | ``` 106 | 107 | **如果使用了 JSHint (和其他工具) 或 ES 6 模式,无需使用 strict mode。** 108 | 109 | ## Commas 110 | 111 | 逗号一律放在后面: 112 | 113 | var foo = 1, 114 | bar = false, 115 | arr = [ 116 | 1, 117 | 2 118 | ], 119 | obj = { 120 | foo: 'foo', 121 | bar: 'bar' 122 | } 123 | ; 124 | 125 | function (argv1, 126 | argv2) {} 127 | 128 | 禁止添加多余的逗号([IE bugs](http://tobyho.com/2014/01/07/js-trailing-comma-remover/)): 129 | 130 | // bad 131 | var obj = { 132 | foo: 'foo', 133 | bar: 'bar', 134 | }; 135 | 136 | ## Whitespaces and Semicolons 137 | 138 | - 请在关键字、变量、操作符等等合适的位置插入空格 139 | - `([{` 后不需要插入空格,`)]}` 前不需要插入空格 140 | - 请不要省略分号 141 | 142 | 示例: 143 | 144 | // good 145 | var a = 'hello', b, c; 146 | 147 | b = function(){ 148 | c.call(data, context); 149 | }; 150 | 151 | c = function (data, context) { 152 | if (data && context) { 153 | // .. 154 | } else { 155 | // .. 156 | } 157 | 158 | switch (context) { 159 | case true: 160 | break; 161 | case false: 162 | break; 163 | default: 164 | // .. 165 | } 166 | }; 167 | 168 | for (var i = 0; i < len; i++) { 169 | // .. 170 | } 171 | 172 | while (true) { 173 | // .. 174 | } 175 | 176 | b = 1 + 2; 177 | c = (b + 1) / 2; 178 | b = (b ? 1 : 2) * 3; 179 | 180 | ## Braces, Brackets and Square Brackets 181 | 182 | 左大括号(花括号)置于行尾,右大括号放在代码块的下一行: 183 | 184 | if (foo) { 185 | // .. 186 | } else { 187 | // .. 188 | } 189 | 190 | foo = { 191 | bar: 1 192 | }; 193 | 194 | bar = function(){ 195 | // .. 196 | }; 197 | 198 | 所有代码块需放在大括号(花括号)内([ES 6 新语法](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/arrow_functions)除外): 199 | 200 | // bad 201 | if (false) return; 202 | 203 | while (true) 204 | fn(); 205 | 206 | arr.map(function (elem) { return elem * 2; }); 207 | 208 | // good 209 | if (false) { 210 | return; 211 | } 212 | 213 | while (true) { 214 | fn(); 215 | } 216 | 217 | arr.map(function (elem) { 218 | return elem * 2; 219 | }); 220 | 221 | [1, 2, 3].map(x => x * 2) // 2, 4, 6 222 | 223 | 括号、方括号通常放置在一行,如果遇到过长需要换行的时候,参考大括号(花括号): 224 | 225 | // good 226 | var foo, bar; 227 | 228 | bar = (foo ? 'foo' : 'bar') + 'foobar'; 229 | 230 | fn( 231 | null, 232 | foo, 233 | bar 234 | ); 235 | 236 | foo = [1, 2, 3]; 237 | 238 | bar = [ 239 | 1, 240 | 2, 241 | 3, 242 | 4 243 | ]; 244 | 245 | ## Indentations 246 | 247 | - 以两个空格为单位 248 | - 多行运算符以第一个运算对象为基准对齐,运算符置于行尾 249 | - 函数参数以第一个参数或左括号为基准对齐 250 | - `if`、`while` 以第一个条件为基准对齐 251 | - `for` 以各个表达式各自对齐 252 | - 多行三元运算符以第一个运算对象为基准对齐,运算符置于行尾 253 | 254 | 示例: 255 | 256 | // good 257 | var str = 'hello' + 258 | ' ' + 259 | 'world' 260 | , foo 261 | ; 262 | 263 | if (typeof str === 'string' && 264 | str == 'hello wolrd') { 265 | } 266 | 267 | foo = function (arg1, arg2, 268 | arg3, arg4, 269 | arg5) { 270 | }; 271 | 272 | var isEmpty = str.length == 0 ? 273 | true : 274 | false; 275 | 276 | $('body').children().first(); 277 | 278 | $('form') 279 | .on('submit', ajaxSubmit) 280 | .find('input') 281 | .on('input', validate) 282 | .end() 283 | .prop('novalidate', true); 284 | 285 | for (var i = 0, 286 | j = 0, 287 | m = 0; 288 | i < 10 && 289 | j < 10 && 290 | m > 0; 291 | i++) {} 292 | 293 | ## Chains 294 | 295 | 链式写法请遵循以下规则: 296 | 297 | - 若链过长,分多行放置,`.` 放在行首 298 | - 按 context 进行缩进 299 | 300 | 示例: 301 | 302 | // good 303 | $form 304 | .prop('novalidate', false) 305 | .find('[type=submit]') 306 | .prop('disabled', true) 307 | .end() 308 | .find('input') 309 | .each(function(){ 310 | // .. 311 | }) 312 | 313 | ## Quotes 314 | 315 | 使用单引号 `'`。 316 | 317 | elem.innerHTML = ''; 318 | 319 | ## Variables 320 | 321 | 使用变量时,请遵循以下规范: 322 | 323 | - 任何变量使用前均需在代码块顶部通过 `var` 声明(循环用的临时变量可例外) 324 | - 如代码块有简单的中断逻辑(如参数数量不符合要求),可将变量声明放置在该逻辑后 325 | - 声明的同时有赋值的变量需独占一行,下一行变量需以第一个变量对齐,并以 `,` 结尾 326 | - 仅有声明并没有赋值的变量放在其他变量后,可写在一行 327 | - 避免直接在函数内调用、设置外部作用域的变量,建议使用参数传入、使用 `return`、`window.varName` 或 `export` 等形式(建议使用 [RequireJS](http://requirejs.org/docs/api.html#define))输出到外部 328 | 329 | 示例: 330 | 331 | // bad 332 | var globalVar; 333 | window.onload = function(){ 334 | var arr = []; 335 | 336 | globalVar = 'global var'; 337 | 338 | var fn = function(){}; 339 | }; 340 | 341 | var fn = function (data) { 342 | var a = 'Hello' 343 | , b, c; 344 | 345 | if (!data) { 346 | return false; 347 | } 348 | }; 349 | 350 | // good 351 | window.onload = function(){ 352 | var arr = [], 353 | globalVar = 'global var', 354 | fn, fetchUser 355 | ; 356 | 357 | for ( var i = 0; i < 10; i++ ) { 358 | arr.push( i ); 359 | } 360 | 361 | fn = function(){}; 362 | 363 | window.globalVar = globalVar; 364 | }; 365 | 366 | var fn = function (data) { 367 | if (!data) { 368 | return false; 369 | } 370 | 371 | var a = 'Hello' 372 | , b, c; 373 | 374 | return data; 375 | }; 376 | 377 | window.fn = fn(); 378 | 379 | // better 380 | var promise = new Promise(function(resolve, reject){ 381 | window.onload = function(){ 382 | resolve(data); 383 | } 384 | }); 385 | 386 | promise.then(function (data) { 387 | }); 388 | 389 | window.onload = function () { 390 | $(window).trigger('windowDidLoad', data); 391 | }; 392 | 393 | $(window).on('windowDidLoad', function (event, data) { 394 | }); 395 | 396 | ## Types 397 | 398 | 优先使用原始类型(primitive type): 399 | 400 | // bad 401 | function (data) { 402 | var isValid = new Boolean(data); 403 | 404 | if (isValid) {} // always be true 405 | } 406 | 407 | var foo, bar; 408 | foo = new String('Hello'); 409 | bar = new String('World'); 410 | foo + bar; // 'HelloWorld' 411 | 412 | // good 413 | function (data) { 414 | var isValid = Boolean(data); 415 | 416 | if (isValid) {} 417 | } 418 | 419 | var foo, bar; 420 | foo = 'Hello'; 421 | bar = 'World'; 422 | 423 | 优先使用字面量(Literal): 424 | 425 | // bad 426 | var arr = new Array(1, 2), 427 | foo = new Object() 428 | ; 429 | 430 | foo.bar = 'foobar'; 431 | 432 | // good 433 | var arr = [1, 2], 434 | foo = { 435 | bar: 'foobar' 436 | } 437 | ; 438 | 439 | 在操作变量时,对变量进行类型转换: 440 | 441 | // bad 442 | var foo = 123, 443 | bar = '321' 444 | ; 445 | 446 | foo + bar; // '123321' 447 | 448 | // good 449 | foo + parseInt(bar, 10); // 444 450 | 451 | ## Numbers 452 | 453 | 仅使用 `parseInt`、`Math.floor`、`Number` 等方式转换数字;可指定进制的,请勿省略进制: 454 | 455 | // bad 456 | parseInt(foo); 457 | bar = +foo; 458 | bar = new Number(foo); 459 | bar = foo >> 0; 460 | 461 | // good 462 | parseInt(foo, 10); 463 | bar = Number(bar); 464 | 465 | 位运算:因为 JavaScript 数字是 64-bit([doc](http://es5.github.io/#x4.3.19)),位运算只会返回 32-bit([doc](http://es5.github.io/#x11.7))。而性能上,Chrome、Firefox、IE 10 中会有明显提升([jsPerf](http://jsperf.com/coercion-vs-casting/11))。因此虽然不推荐使用位运算,但如有特殊原因,可以使用,并注明原因: 466 | 467 | // good 468 | // due to XXX browser / platform / reason, use bitshift instead 469 | var foo = bar >> 0; 470 | 471 | ## Objects 472 | 473 | - 使用字面量 474 | - 不要使用保留字 475 | - 使用可读性好的同义词 476 | - 使用点 `.` 访问属性,当属性名为变量时使用 `[]` 477 | 478 | 示例: 479 | 480 | // bad 481 | var foo = new Object(); 482 | foo['klass'] = 'human'; 483 | foo['private] = true; 484 | 485 | if (foo['klass']) {} 486 | 487 | // good 488 | var foo = { 489 | type: 'human', 490 | hidden: true 491 | }; 492 | 493 | if (foo.type) {} 494 | 495 | function getProp (prop) { 496 | return this[prop]; 497 | } 498 | 499 | getProp.call(foo, 'hidden'); 500 | 501 | ### Accessors 502 | 503 | 存取器使用 `addVal`、`removeVal`、`getVal`、`setVal`、`hasVal`、`isVal` 来命名;`hasVal` 和 `isVal` 是只读,只返回 `Boolean` 值。 504 | 505 | // bad 506 | user.age(); 507 | user.age(20); 508 | user.car(BMW); 509 | 510 | if (user.kid()) {} 511 | 512 | // good 513 | user.getAge(); 514 | user.getAge(20); 515 | user.addCar(BMW) 516 | 517 | if (user.isKid()) {} 518 | 519 | if (user.hasCar(carName)) {} 520 | 521 | 可以为对象创建统一的 `get()`、`set()`. 522 | 523 | function User () {} 524 | 525 | User.prototype.get = function (key) { 526 | return this[key]; 527 | }; 528 | 529 | User.prototype.set = function (key, value) { 530 | this[key] = value; 531 | }; 532 | 533 | ## Arrays 534 | 535 | - 使用字面量 536 | - 添加、删除数组元素时,尽量使用 `push`、`pop`、`splice` 等函数 537 | - 遍历数组时尽量使用 `forEach`、`map` 等函数(对于低版本浏览器,可使用 [lodash](http://lodash.com/) 或 [polyfill](https://github.com/es-shims/es5-shim)) 538 | 539 | 示例: 540 | 541 | // bad 542 | var foo = new Array(1, 2); 543 | foo[foo.length] = 3; 544 | 545 | // not recommended 546 | for (var i = 0, len = foo.length; i < len; i++) { 547 | foo[i]; 548 | } 549 | 550 | // good 551 | var foo [1, 2]; 552 | foo.push(3); 553 | 554 | // recommended 555 | foo.forEach(function (item) { 556 | item; 557 | }); 558 | 559 | _(foo).forEach(function (item) { 560 | item 561 | }); 562 | 563 | 建议尽量使用 lodash 进行数组遍历,一般情况,性能比起原生的要快,具体见 [fast.js](https://github.com/codemix/fast.js) 的[对比数据](https://github.com/codemix/fast.js/blob/master/README.md#example-benchmark-output): 564 | 565 | ``` 566 | Native .forEach() vs fast.forEach() (1000 items) 567 | ✓ Array::forEach() x 34,398 ops/sec ±0.99% (90 runs sampled) 568 | ✓ fast.forEach() x 93,377 ops/sec ±0.90% (87 runs sampled) 569 | ✓ fast.forEach() v0.0.2a x 93,894 ops/sec ±1.42% (85 runs sampled) 570 | ✓ fast.forEach() v0.0.1 x 98,412 ops/sec ±1.58% (91 runs sampled) 571 | ✓ fast.forEach() v0.0.0 x 66,422 ops/sec ±1.30% (87 runs sampled) 572 | ✓ underscore.forEach() x 33,300 ops/sec ±1.30% (82 runs sampled) 573 | ✓ lodash.forEach() x 95,146 ops/sec ±1.18% (88 runs sampled) 574 | 575 | Native .map() vs fast.map() (1000 items) 576 | ✓ Array::map() x 32,971 ops/sec ±1.47% (87 runs sampled) 577 | ✓ fast.map() x 95,182 ops/sec ±1.51% (82 runs sampled) 578 | ✓ fast.map() v0.0.2a x 99,468 ops/sec ±1.32% (91 runs sampled) 579 | ✓ fast.map() v0.0.1 x 99,425 ops/sec ±1.66% (91 runs sampled) 580 | ✓ fast.map() v0.0.0 x 67,765 ops/sec ±1.60% (84 runs sampled) 581 | ✓ underscore.map() x 32,942 ops/sec ±1.45% (92 runs sampled) 582 | ✓ lodash.map() x 77,364 ops/sec ±1.42% (89 runs sampled) 583 | ``` 584 | 585 | 至 fast.js、underscore.js 和 lodash 的关系: 586 | 587 | - fast.js 缺少很多 lodash 有的接口,如 `_.merge`、`_.is*`,因此不建议使用 fast.js 588 | - lodash 拥有 underscore.js 的接口,同时也更快、也扩展了更多的接口,因此不建议使用 underscore.js 589 | - 如果 lodash 的性能不符合项目需求,理应重新审视需求,换用更高效的实现,而不是靠某个库 590 | 591 | ## Strings 592 | 593 | - 使用字面量 594 | - 连接字符串请使用 `+` 595 | - 字符串过长可使用多个字符串字面量分行并使用 `+` 或数组 `join` 连接 596 | 597 | 示例: 598 | 599 | // bad 600 | var foo = new String('bar'); 601 | 602 | var longString = 'Quick fox jumps over the lazy dog. Quick fox jumps over the lazy dog. Quick fox jumps over the lazy dog.' 603 | 604 | var longString = 'Quick fox jumps over the lazy dog. \ 605 | Quick fox jumps over the lazy dog. \ 606 | Quick fox jumps over the lazy dog.' 607 | 608 | // good 609 | var foo = 'bar'; 610 | 611 | var longString = 'Quick fox jumps over the lazy dog. ' + 612 | 'Quick fox jumps over the lazy dog. ' + 613 | 'Quick fox jumps over the lazy dog.'; 614 | 615 | var lists = []; // lots of '
    • ' 616 | ul.innerHTML = lists 617 | .map(function (elem, index) { 618 | elem += 'list #' + index; 619 | }) 620 | .join(''); 621 | 622 | ### `RegExp.exec` vs `String.match` 623 | 624 | [Regular Expressions in JavaScript, part 2](http://james.padolsey.com/javascript/regular-expressions-in-javascript-part-2/) 625 | 626 | ```js 627 | var s = 'test test test test', 628 | re = /t(e)(s)t/g; 629 | 630 | console.log(s.match(re)); // ["test", "test", "test", "test"] 631 | 632 | console.log(re.exec(s)); // ["test", "e", "s", index: 0, input: "test test test test"] 633 | ``` 634 | 635 | ## Functions 636 | 637 | - 除类、构造函数外,请勿使用 `function name () {}` 方式声明函数 638 | - 使用函数表达式时可同时命名匿名函数,以便调试 639 | - 声明同时执行的函数用 `~function(){}();` 方式书写 640 | - 函数参数部分(括号部分)需以一空格和 `function`、`function_name` 和花括号分隔开;无参数的匿名函数可省略空格 641 | - 参数若分多行,以第一个参数为基准对齐,一行一组,每组尽量保持数量一致 642 | 643 | 示例: 644 | 645 | // good 646 | var foo = function () {} 647 | , bar = function bar() {} 648 | ; 649 | 650 | ~function(){}(); 651 | 652 | // other style samples 653 | foo = function(){}; 654 | 655 | foo = function (argv) {}; 656 | 657 | foo = function (argv1, argv2, 658 | argv3, argv4, 659 | argv5) { 660 | }; 661 | 662 | foo = function foo () {}; 663 | 664 | foo = function foo (argv) {}; 665 | 666 | ### Arguments 667 | 668 | 在声明函数的时候,可以通过对象传递参数,一方面可以减少判断参数数量的问题,一方面利于日后扩展用。 669 | 670 | function request (data) { 671 | var url = '//domain.com/default_url'; 672 | if (data.url) { 673 | url = encodeURIComponent(data.url); 674 | } 675 | if (data.json) { 676 | } 677 | } 678 | 679 | request({ 680 | url: '//domain.com/users/1', 681 | json: { 682 | id: 1 683 | } 684 | }); 685 | 686 | request({ 687 | json: { 688 | id: 1 689 | } 690 | }); 691 | 692 | $elem.trigger('updateProfile', { 693 | id: 1, 694 | name: 'John Doe' 695 | }); 696 | 697 | $elem.on('updateProfile', function (event, profile) { 698 | request({ 699 | url: '//domain.com/users/' + profile.id, 700 | json: profile 701 | }); 702 | }); 703 | 704 | ## Modules 705 | 706 | - module 需要包裹在 `~function(){}()` 里 707 | - module 的文件需以小驼峰书写,并放置在同名目录中 708 | - 提供 `noConflict` 接口 709 | 710 | 示例: 711 | 712 | ~function(global){ 713 | var previousSlideshow = global.slideshow, 714 | slideshow = function slideshow () {} 715 | ; 716 | 717 | slideshow.noConflict = function noConflict () { 718 | global.slideshow = previousSlideshow; 719 | return slideshow; 720 | }; 721 | 722 | global.slideshow = slideshow; 723 | }(this); 724 | 725 | ## Scoping 726 | 727 | - 代码块(一般以文件为单位)需包裹在一个 `function` 或 `define`(如 [RequireJS](http://requirejs.org/)) 728 | - 变量在使用前必须先声明 729 | 730 | 示例: 731 | 732 | // foo.js 733 | ~function(){ 734 | }(); 735 | 736 | // bar.js 737 | ~function(){ 738 | }(); 739 | 740 | // define.js 741 | define('module/name', function(){ 742 | }); 743 | 744 | ## Eval 745 | 746 | 除编写类、插件库外,请勿使用 `eval()`。 747 | 748 | ## With 749 | 750 | 为了保持代码清晰,任何时候都不要使用 `with(){}`。 751 | 752 | ## Exceptions 753 | 754 | `try..catch` 有严重性能问题([jsPerf](http://jsperf.com/try-catch-error-perf/64)),因此不建议过度依赖 `try..catch` 进行异常处理。注:ES6 promise 会将异常作为 `reject` 处理,所以在编码时多加注意。 755 | 756 | // bad 757 | try { 758 | // do something 759 | } catch (ex) { 760 | logging(ex.message, new Date()); 761 | alert('Sorry, there is some wrong, please contact admin!'); 762 | } 763 | 764 | // good 765 | if (error) { 766 | logging(ex.message, new Date()); 767 | alert('Sorry, there is some wrong, please contact admin!'); 768 | } 769 | 770 | ## Constructors and Prototypes 771 | 772 | 避免修改内置对象(如 `String`)的 `prototype`,只有基础级别的 API 才允许修改 `prototype`: 773 | 774 | /** 775 | * An alias of String.indexOf, but returns true / false 776 | * @param {Any} str A string representing the value to search for; 777 | * basicly, JS will use `toString` to convert passed argument to string 778 | * @return {Boolean} A boolean presenting search result 779 | */ 780 | if (!String.prototype.contains) { 781 | String.prototype.contains = function (str) { 782 | return this.indexOf(str) > -1; 783 | } 784 | } 785 | 786 | 创建构造函数时,使用单独赋值的形式,不要直接传递对象: 787 | 788 | // bad 789 | function User () {} 790 | 791 | User.prototype = { 792 | get: getter, 793 | set: setter 794 | }; 795 | 796 | // good 797 | function User () {} 798 | 799 | User.prototype.get = getter; 800 | User.prototype.set = setter; 801 | 802 | ## Namespaces 803 | 804 | 适当使用命名空间分隔代码块(建议结合 [RequireJS](http://requirejs.org/docs/api.html) 解决依赖问题): 805 | 806 | TopNS.user.signIn = function(){}; 807 | TopNS.admin.onlineUsers = function(){}; 808 | 809 | define('User', function(){ 810 | return { 811 | signIn: function(){} 812 | }; 813 | }); 814 | 815 | require(['User'], function (User) { 816 | User.signIn(); 817 | }); 818 | 819 | ## Conditional Expressions and Comparisons 820 | 821 | - 尽量使用 `===`、`!==` 822 | - 尽量使用 `!` 来做 `true` / `fasle` 判断 823 | - 多使用内置或第三方函数检测实例,如 `Array.isArray`、`_.isString` 824 | 825 | 示例: 826 | 827 | // bad 828 | if (arr.length == 3) {} 829 | 830 | if (isValid != true) {} 831 | 832 | if (arr instanceof Array) {} 833 | 834 | // good 835 | if (arr.length === 3) {} 836 | 837 | if (!isValid) {} 838 | 839 | if (Array.isArray(arr)) {} 840 | 841 | ## Comments 842 | 843 | 对代码使用 [JSDoc](http://en.wikipedia.org/wiki/JSDoc) 风格对代码注释: 844 | 845 | /** 846 | * An alias of String.indexOf, but returns true / false 847 | * @param {Any} str A string representing the value to search for; 848 | * basicly, JS VM will use `toString` to convert 849 | * passed argument to string 850 | * @return {Boolean} A boolean presenting search result 851 | */ 852 | if (!String.prototype.contains) { 853 | String.prototype.contains = function (str) { 854 | return this.indexOf(str) > -1; 855 | } 856 | } 857 | 858 | 文件头部: 859 | 860 | /** 861 | * 关于当前文件的描述和用途。 862 | * 是否有依赖项 863 | */ 864 | 865 | 每个注释和代码段之间以一个空行进行分割: 866 | 867 | /** 868 | * 获知设备屏幕数据 869 | * 主要用于判断和解决 retina 屏的问题 870 | */ 871 | devicePixelRatio = window.devicePixelRatio || 'unknown', 872 | screenPixelDepth = screen.pixelDepth || 'unknown', 873 | screenColorDepth = screen.colorDepth || 'unknown', 874 | 875 | // 判断 JS 引擎 876 | querySelector = !!document.querySelector; // IE 7 或以下为 false 877 | localStorage = !!window.localStorage; // IE 7 或以下为 false 878 | arrayFilter = 'filter' in Array.prototype; // IE 8 或以下为 false 879 | classList = 'classList' in document.getElementsByTagName('head')[0]; // IE 9 或以下为 false 880 | // 若都支持,则说明 JS 为现代引擎 881 | 882 | platform = navigator.platform; 883 | 884 | ## Tag Your Codes 885 | 886 | 使用 `#@tag` 在代码标记行为: 887 | 888 | - `#@todo`:标记需尽快执行的事项 889 | - `#@issue`:标记暂时可不执行的事项 890 | 891 | 示例: 892 | 893 | // #@issue auto calculate height according to its content 894 | elem.height(200); 895 | 896 | // #@todo should use promise instead callback 897 | var request = function (callback) { 898 | $.ajax({ 899 | success: function (data) { 900 | callback.success.call(this, data); 901 | } 902 | }); 903 | }; 904 | 905 | 使用示例: 906 | 907 | 命令行 + [ag](https://github.com/ggreer/the_silver_searcher) = `ag -i -A 2 -B 2 '#@(todo|issue)'`: 908 | 909 | JavaScript_Style_Guide.md 910 | 839-使用 `#@tag` 在代码标记行为: 911 | 840- 912 | 841:- `#@todo`:标记需尽快执行的事项 913 | 842:- `#@issue`:标记暂时可不执行的事项 914 | 843- 915 | 844-示例: 916 | 845- 917 | 846: // #@issue auto calculate height according to its content 918 | 847- elem.height(200); 919 | 848- 920 | 849: // #@todo should use promise instead callback 921 | 850- var request = function (callback) { 922 | 851- $.ajax({ 923 | 924 | ## DOM 925 | 926 | 活用 `cloneNode`、`innerHTML` 和 `DocumentFragment`(jsPerf: [1](http://jsperf.com/appendchild-documentfragment-innerhtml/4), [2](http://jsperf.com/clonenode-in-nodes-iteration/2)): 927 | 928 | var nav = document.querySelect('nav'), 929 | clonedNode = nav.cloneNode(true), 930 | newItems = clonedNode.querySelectorAll('.item'), 931 | length = newItems.length, 932 | parent = nav.parentElement 933 | ; 934 | 935 | while (length--) { 936 | newItems[length]; // do something... 937 | } 938 | 939 | parent.replaceChild(clonedNode, nav); 940 | 941 | 使用 `[].forEach.call` 等方式遍历元素数组: 942 | 943 | $('nav a').each(callback); 944 | 945 | [].forEach.call(document.querySelectorAll('nav a'), callback); 946 | 947 | 事件命名建议使用可读性更高的句子: 948 | 949 | $profile 950 | .on('profileDidUpdate', callback) 951 | .on('profileDidUpdateFailed', callback); 952 | 953 | 利用 DOM 内置的行为,而不是强制抹去内置行为: 954 | 955 | // bad 956 |
      957 | 958 |
      959 | 960 | $('form').on('submit', function(){ 961 | return false; 962 | }); 963 | 964 | $('input').on('click', function(){ 965 | $('form').submit(); 966 | }); 967 | 968 | // good 969 |
      970 | 971 |
      972 | 973 | $('form').on('submit', function(){ 974 | var $form = $(this); 975 | 976 | // .. 977 | 978 | return false; 979 | }); 980 | 981 | // bad 982 |
      983 | 984 |
      985 | 986 | // good 987 |
      988 | 989 |
      990 | 991 | $('form').each(function(){ 992 | this.setAttribute('novalidate', 'true'); 993 | }); 994 | 995 | $('input').on('input', function(){ 996 | if (new RegExp(this.getAttribute('pattern')).test(this.value)) {} 997 | }); 998 | 999 | 对输入进行 `trim` 处理: 1000 | 1001 | // bad 1002 | var name = $('name').val(); 1003 | 1004 | // good 1005 | var name = $.trim($('name').val()); 1006 | 1007 | ## jQuery 1008 | 1009 | 在变量名前添加 `$`: 1010 | 1011 | var $form = $(this), 1012 | _$select = $form.find('select'); 1013 | 1014 | 缓存元素或使用 `chains`: 1015 | 1016 | // bad 1017 | $('button').on('click', callback); 1018 | $('button').prop('disabled', false); 1019 | 1020 | // good 1021 | $('button') 1022 | .on('click', callback) 1023 | .prop('disabled', false); 1024 | 1025 | $form = $('form'); 1026 | 1027 | $.ajax().then(function(){ 1028 | $form; 1029 | }); 1030 | 1031 | 使用高级 [CSS selector](http://api.jquery.com/category/selectors/): 1032 | 1033 | // bad 1034 | $('div').first(); 1035 | 1036 | $('input').each(function(){ 1037 | if ($(this).css('display') !== 'none') {} 1038 | }); 1039 | 1040 | // good 1041 | $('div:first'); 1042 | 1043 | $('input:not(:hidden)').each(function(){}); 1044 | 1045 | 合理选择 context 和 `find`: 1046 | 1047 | // bad 1048 | $('nav').find('a'); 1049 | $('a', 'nav'); 1050 | $('a', $nav); 1051 | 1052 | // good 1053 | $('nav a'); 1054 | $('nav > a'); 1055 | $('a', document.getElementById('nav')); 1056 | $('a', $nav[0]); 1057 | $nav.find('a'); 1058 | 1059 | 使用 `on`: 1060 | 1061 | // bad 1062 | $('button').live('click', callback); 1063 | $('button').click(callback); 1064 | 1065 | // good 1066 | $(document).on('click', 'button', callback); 1067 | $('button').on('click', callback); 1068 | 1069 | 使用 `each` 来减少判断语句: 1070 | 1071 | // bad 1072 | var $body = $('body') 1073 | if ($body.length) { 1074 | // .. 1075 | } 1076 | 1077 | // good 1078 | $('body').each(function(){ 1079 | $body = $(this); 1080 | }); 1081 | 1082 | 使用可读的变量名,避免使用 `$this`: 1083 | 1084 | // bad 1085 | $('body').each(function(){ 1086 | $this = $(this); 1087 | }); 1088 | 1089 | // good 1090 | $('body').each(function(){ 1091 | $body = $(this); 1092 | }); 1093 | 1094 | 使用 `length` 而不是 `size()`([jsPerf](http://jsperf.com/size-vs-length/5)): 1095 | 1096 | // bad 1097 | $elem.size(); 1098 | 1099 | // good 1100 | $elem.length; 1101 | 1102 | ## ECMAScript Compatibility 1103 | 1104 | - 兼容性列表:[ES5](http://kangax.github.io/es5-compat-table/), [ES6](http://kangax.github.io/es5-compat-table/es6/) 1105 | - [Polyfill.io](http://polyfill.io/) - 可针对各种浏览器添加垫片 1106 | - [ES5 Polyfill](https://github.com/es-shims/es5-shim) 1107 | - [ES6 Promise](https://github.com/jakearchibald/ES6-Promises) 1108 | 1109 | ## Meta 1110 | 1111 | - 在每个文件的末尾添加一行空行 1112 | - 请删除行尾多余的空格 -------------------------------------------------------------------------------- /Typescript.md: -------------------------------------------------------------------------------- 1 | # Typescript 语言规范 2 | 3 | ## 命名规范 4 | - 命名需选择有意义的名字,除了默认成俗的名字外,尽量不要使用简写形式 5 | - 虽然 JavaScript 支持 Unicode 字符,但请使用 [a-zA-Z0-9_\$] 6 | - 使用驼峰命名法(camel case),变量和函数使用小驼峰,如 getElementById,类、构造函数和枚举类型使用大驼峰,如 `DocumentFragment` 7 | - 常量所有字母均大写并使用 _ 分割单词,如 Number.MAX_VALUE 8 | - 除了常量和枚举值以外,不要使用下划线’_’ 9 | - 尊重惯例,避免使用常用库、框架名,如 $ = {}、jQuery = {}、_ = {} 10 | - Boolean 变量、函数请加上 is、has 前缀 11 | - 当保存 this 对象时,尽量使用可对应上下文的命名,如report,或使用 that 12 | - 不要使用过长的变量名(例如50个字符)。过长的变量名会导致代码丑陋(ugly)和难以阅读(hard-to-read),还可能因为字符限制在某些编译器上存在兼容性问题。 13 | - 方法使用首字母小写的驼峰命名法:getStudentSchoolType 14 | - 方法参数使用首字母小写的驼峰命名法:setSchoolName(schoolName) 15 | - 使用有意义的方法参数命名,这样做可以在没有文档的情况下尽量做到“自解释(documentate itself)” 16 | 17 | 示例: 18 | 19 | // bad 20 | valid = true; 21 | k = 'word' 22 | hasAttr = function (e, a) { 23 | var _a = e.getAttribute(a); 24 | return _a === null; 25 | }; 26 | 27 | function user (opts) { 28 | this.name = opts.name; 29 | this.ID = opts.ID; 30 | } 31 | 32 | $form.on('submit', function(){ 33 | var that = this; 34 | }); 35 | 36 | // good 37 | isValid = true; 38 | keyword = 'word'; 39 | hasAttr = function (elem, attr) { 40 | var _attr = elem.getAttribute(attr); 41 | return _attr === null; 42 | } 43 | 44 | function User (options) { 45 | this.name = options.name; 46 | this.id = options.id; 47 | } 48 | 49 | $form.on('submit', function(){ 50 | var rootForm = this; 51 | // or 52 | var _this = this; 53 | }); 54 | 55 | ## 书写约定 56 | > 配置TS-Lint,通过IDE自动化约定配置规范 57 | 58 | ### Typescript 59 | 60 | - member-access: 显示的声明Class的成员修饰。即public/private需要指定 61 | - member-ordering: 声明Class成员需要按照以下规则: 62 | - public 在 private 之前 63 | - static 在 instance 之前 64 | - variables 在 functions 之前 65 | - no-var-requires: 禁止使用 `var module = require("module")` 66 | - typedef: 类型必须声明 67 | - 函数必须有返回类型 68 | - 非箭头函数必须有类型 69 | - 箭头函数必须有类型 70 | - 接口的返回类型属性 71 | - 变量声明 72 | - 成员变量声明 73 | - typedef-whitespace: 类型之间必须留有空白 74 | 75 | ### 其它更多约定 76 | 77 | > 以下是完整的tslint规范,具体对应的规则,直接浏览[TsLint Rule](http://palantir.github.io/tslint/rules/) 78 | 79 | ``` 80 | { 81 | "rules": { 82 | "align": [ 83 | true, 84 | "parameters", 85 | "statements" 86 | ], 87 | "ban": false, 88 | "class-name": false, 89 | "comment-format": [ 90 | true, 91 | "check-space", 92 | "check-uppercase" 93 | ], 94 | "curly": true, 95 | "eofline": true, 96 | "forin": true, 97 | "indent": [ 98 | true, 99 | "tab" 100 | ], 101 | "interface-name": false, 102 | "jsdoc-format": true, 103 | "label-position": true, 104 | "label-undefined": true, 105 | "max-line-length": [ 106 | true, 107 | 140 108 | ], 109 | "member-access": false, 110 | "member-ordering": [ 111 | true, 112 | "public-before-private", 113 | "static-before-instance", 114 | "variables-before-functions" 115 | ], 116 | "no-any": false, 117 | "no-arg": true, 118 | "no-bitwise": true, 119 | "no-conditional-assignment": false, 120 | "no-consecutive-blank-lines": false, 121 | "no-console": [ 122 | true, 123 | "debug", 124 | "info", 125 | "time", 126 | "timeEnd", 127 | "trace" 128 | ], 129 | "no-construct": true, 130 | "no-constructor-vars": true, 131 | "no-debugger": true, 132 | "no-duplicate-key": true, 133 | "no-duplicate-variable": true, 134 | "no-empty": true, 135 | "no-eval": true, 136 | "no-inferrable-types": false, 137 | "no-internal-module": false, 138 | "no-null-keyword": true, 139 | "no-require-imports": false, 140 | "no-shadowed-variable": true, 141 | "no-string-literal": true, 142 | "no-switch-case-fall-through": true, 143 | "no-trailing-whitespace": true, 144 | "no-unreachable": true, 145 | "no-unused-expression": true, 146 | "no-unused-variable": true, 147 | "no-use-before-declare": true, 148 | "no-var-keyword": true, 149 | "no-var-requires": true, 150 | "object-literal-sort-keys": false, 151 | "one-line": [ 152 | true, 153 | "check-open-brace", 154 | "check-catch", 155 | "check-else", 156 | "check-whitespace" 157 | ], 158 | "quotemark": [ 159 | false, 160 | "single", 161 | "avoid-escape" 162 | ], 163 | "radix": true, 164 | "semicolon": false, 165 | "switch-default": true, 166 | "trailing-comma": [ 167 | false, 168 | { 169 | "singleline": "never", 170 | "multiline": "always" 171 | } 172 | ], 173 | "triple-equals": [ 174 | true, 175 | "allow-null-check" 176 | ], 177 | "typedef": [ 178 | true, 179 | "call-signature", 180 | "parameter", 181 | "property-declaration", 182 | "variable-declaration", 183 | "member-variable-declaration" 184 | ], 185 | "typedef-whitespace": [ 186 | true, 187 | { 188 | "call-signature": "nospace", 189 | "index-signature": "nospace", 190 | "parameter": "nospace", 191 | "property-declaration": "nospace", 192 | "variable-declaration": "nospace" 193 | } 194 | ], 195 | "use-strict": [ 196 | true, 197 | "check-module" 198 | ], 199 | "variable-name": [ 200 | true, 201 | "check-format", 202 | "allow-leading-underscore", 203 | "ban-keywords" 204 | ], 205 | "whitespace": [ 206 | true, 207 | "check-branch", 208 | "check-decl", 209 | "check-operator", 210 | "check-separator", 211 | "check-type" 212 | ] 213 | } 214 | } 215 | ``` 216 | 217 | 218 | ## 项目结构 219 | 220 | - utils: 通用模块 221 | - chunks: 按需加载模块 222 | - components: 项目组件,包括基础组件(base)、业务模块(module) 223 | - decorators: 修饰器 224 | - redux: 单向数据流 225 | - routes: 路由 226 | - views: 容器/视图 227 | - external: 第三方或外部插件 228 | - entry/hot: 模块入口文件 229 | - typing: TS模块接口定义文件 230 | - template: 模板文件 231 | 232 | 233 | ## 常见问题 234 | 235 | ### 如何引入JS/JSX模块 236 | > 解决方案有以下方法: 237 | 238 | - 模块通过npm安装,需要编写接口定义文件(d.ts),vscode/webstorm会根据接口文件(d.ts)定义自动映射到模块 239 | - 配置参数:allowJs: true。在tsconfig.json/jsconfig.json中配置 240 | - 模块不通过npm 安装,而是直接从某个文件夹引入JSX,需要由babel先编译再由ts编译(临时解决方案),在ts导入时,需要按照以下方法:(不推荐此类方式,假如引入的模块不存在无法处理的需求,请不要使用该方案) 241 | 242 | ``` 243 | // 导入模块 244 | import calendar = require('../../external/calendar'); 245 | // 权宜之计,因为无法识别JSX元素,JSX仅能通过Import from 引入 246 | const Calendar: any = calendar as any; 247 | ``` 248 | 249 | ### Import / Require 区别 250 | `import mod from 'modules'` 251 | 252 | > 当模块是ES6时,并且提供default,则使用这个方法(假如该代码被babel编成ES5,无法使用该方法) 253 | 254 | ``` 255 | module.export['default'] = mod; 256 | module.export.default = mod 257 | export default mod 258 | ``` 259 | 260 | `import mod = require('modules')` 261 | 262 | > 当模块非ES6,并且未提供default,则使用该方法 263 | 264 | ``` 265 | module.exports = mod; 266 | ``` 267 | 268 | ### JSX出现红色下划线,不能够识别为JSX元素 269 | > 满足JSX条件,有以下情形: 270 | 271 | - JSX组件首字母必须是大写字母,如 ``。错误的写法:`` 272 | - 通过 Import mod from 'module' 导入, 否则只能强制转换成 any 273 | - 当前文件扩展,必须是tsx 274 | - 必须是Stateless Component / extend React.Component / createElement / createClass 中的一种 275 | 276 | 277 | ## 小技巧 278 | 279 | - tsd.d.ts中引用node_modules/typescript/lib/lib.es6.d.ts,能够确保项目支持ES6语法 280 | 281 | - namespace(全局绑定,直接就引用,不需要require) 282 | - module(基于amd/cmd模式的引用,会export出代码) 283 | 284 | 285 | - exports.__esModule = true; //babel使用 286 | 287 | - ES5导入外部模块(declare module)JS后缀的文件,外部模块提供export default导出当前模块时,使用Import something = ‘sorting’. 否则会带出object.default,同时写d.ts时需要export default导出模块 288 | - ES5本身写的ts是可以直接import something from ‘something’,不会带出 object.default 289 | 290 | 291 | ## TS语法 292 | 293 | ### 基本类型 294 | 295 | Boolean 296 | 297 | ``` 298 | let isDone: boolean = false; 299 | ``` 300 | Number 301 | 302 | ``` 303 | let binary: number = 0b1010; 304 | let octal: number = 0o744; 305 | ``` 306 | 307 | String 308 | 309 | ``` 310 | let color: string = 'blue'; 311 | ``` 312 | 313 | Array 314 | 315 | ``` 316 | let list: number[] = [1, 2, 3]; 317 | let list: Array = [1, 2, 3]; 318 | ``` 319 | 320 | Tuple(不相同的元素) 321 | 322 | ``` 323 | // Declare a tuple type 324 | let x: [string, number]; 325 | // Initialize it 326 | x = ['hello', 10]; // OK 327 | // Initialize it incorrectly 328 | x = [10, 'hello']; // Error 329 | ``` 330 | 331 | Enum 332 | 333 | ``` 334 | enum Color {Red, Green, Blue}; 335 | enum Color {Red = 1, Green = 2, Blue = 4}; 336 | enum Color {Red = 1, Green, Blue}; 337 | let c: Color = Color.Green; 338 | ``` 339 | 340 | Any 341 | 342 | ``` 343 | let notSure: any = 4; 344 | notSure.ifItExists(); // okay, ifItExists might exist at runtime 345 | notSure.toFixed(); // okay, toFixed exists (but the compiler doesn't check) 346 | let prettySure: Object = 4; 347 | prettySure.toFixed(); // error 348 | let list: any[] = [1, true, "free"]; 349 | ``` 350 | Void 351 | 352 | ``` 353 | function warnUser(): void { 354 | alert("This is my warning message"); 355 | } 356 | // 只允许设置 undefined / null 357 | let unusable: void = undefined; 358 | ``` 359 | 360 | 类型断点的两种方法 361 | 362 | ``` 363 | // 一般 364 | let someValue: any = "this is a string"; 365 | let strLength: number = (someValue).length; 366 | 367 | // as 语法 368 | let someValue: any = "this is a string"; 369 | let strLength: number = (someValue as string).length; 370 | ``` 371 | 372 | ### 变量 373 | - `var`: 定义变量 374 | - `let`: 定义块变量 375 | - `const`: 定义常量 376 | - `Destructuring`: 解构函数 377 | 378 | ``` 379 | let input = [1, 2]; 380 | let [first, second] = input; 381 | function f([first, second]: [number, number]){ } 382 | let [first, ...rest] = [1, 2, 3, 4]; 383 | let [, second, , fourth] = [1, 2, 3, 4]; 384 | let o = { 385 | a: "foo", 386 | b: 12, 387 | c: "bar" 388 | } 389 | let {a, b} = o; 390 | let {a, b}: {a: string, b: number} = o; 391 | // 函数声明 392 | type C = {a: string, b?: number} 393 | function f({a, b}: C): void { 394 | // ... 395 | } 396 | function f({a, b} = {a: "", b: 0}): void { ... } 397 | function f({a, b = 0} = {a: ""}): void { 398 | // ... 399 | } 400 | ``` 401 | 402 | ### Interfaces(接口) 403 | 404 | - 对象 405 | 406 | ``` 407 | interface SquareConfig { 408 | color?: string; 409 | width?: number; 410 | [propName: string]: any; 411 | } 412 | ``` 413 | 414 | - 接口类型转换 415 | 416 | ``` 417 | let mySquare = createSquare({ width: 100, opacity: 0.5 } as SquareConfig); 418 | ``` 419 | 420 | - Function 421 | 422 | ``` 423 | interface SearchFunc { 424 | (source: string, subString: string): boolean; 425 | } 426 | ``` 427 | 428 | - Indexable Types 429 | 430 | ``` 431 | interface NumberDictionary { 432 | [index: string]: number; 433 | length: number; // ok, length is a number 434 | name: string; // error 435 | } 436 | ``` 437 | 438 | - Class Types 439 | 440 | ``` 441 | interface ClockConstructor { 442 | new (hour: number, minute: number): ClockInterface; 443 | } 444 | interface ClockInterface { 445 | tick(); 446 | } 447 | class Clock implements ClockConstructor { 448 | currentTime: Date; 449 | constructor(h: number, m: number) { } 450 | } 451 | ``` 452 | - Extending Interfaces(继承接口) 453 | 454 | ``` 455 | interface Shape { 456 | color: string; 457 | } 458 | 459 | interface PenStroke { 460 | penWidth: number; 461 | } 462 | 463 | interface Square extends Shape, PenStroke { 464 | sideLength: number; 465 | } 466 | 467 | let square = {}; 468 | ``` 469 | 470 | - Hybrid Types(混合类型) 471 | 472 | ``` 473 | interface Counter { 474 | (start: number): string; 475 | interval: number; 476 | reset(): void; 477 | } 478 | ``` 479 | - Interfaces Extending Classes(接口继承类) 480 | 481 | ``` 482 | class Control { 483 | private state: any; 484 | } 485 | 486 | interface SelectableControl extends Control { 487 | select(): void; 488 | } 489 | ``` 490 | 491 | ### Classes(类) 492 | 493 | ``` 494 | // 普通类 495 | class Animal { 496 | name: string; 497 | constructor(theName: string) { this.name = theName; } 498 | move(distanceInMeters: number = 0) { 499 | console.log(`${this.name} moved ${distanceInMeters}m.`); 500 | } 501 | } 502 | // 继承 503 | class Snake extends Animal { 504 | constructor(name: string) { super(name); } 505 | move(distanceInMeters = 5) { 506 | console.log("Slithering..."); 507 | super.move(distanceInMeters); 508 | } 509 | } 510 | // public 全局 / protected 不能被外部使用 / private 私有 511 | class Animal { 512 | protected name: string; 513 | public constructor(theName: string) { this.name = theName; } 514 | private move(distanceInMeters: number) { 515 | console.log(`${this.name} moved ${distanceInMeters}m.`); 516 | } 517 | } 518 | // Accessors 519 | class Employee { 520 | get fullName(): string { 521 | return this._fullName; 522 | } 523 | 524 | set fullName(newName: string) { 525 | if (passcode && passcode == "secret passcode") { 526 | this._fullName = newName; 527 | } 528 | else { 529 | console.log("Error: Unauthorized update of employee!"); 530 | } 531 | } 532 | } 533 | 534 | // static 静态变量,在实例间共享,对于所有实例都是通用的 535 | 536 | class Grid { 537 | static origin = {x: 0, y: 0}; 538 | calculateDistanceFromOrigin(point: {x: number; y: number;}) { 539 | let xDist = (point.x - Grid.origin.x); 540 | let yDist = (point.y - Grid.origin.y); 541 | return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale; 542 | } 543 | constructor (public scale: number) { } 544 | } 545 | var grid1 = new Grid(1.0); // 1x scale 546 | var grid2 = new Grid(5.0); // 5x scale 547 | 548 | // Abstract Classes 抽象类 与接口类似,同样不能够被实例化 549 | 550 | abstract class Animal { 551 | abstract makeSound(): void; 552 | move(): void { 553 | console.log('roaming the earth...'); 554 | } 555 | } 556 | ``` 557 | 558 | ### Functions (函数) 559 | 560 | ``` 561 | function add(x: number, y: number): number { 562 | return x + y; 563 | } 564 | 565 | let myAdd = function(x: number, y: number): number { return x+y; }; 566 | 567 | let myAdd: (baseValue:number, increment:number) => number = 568 | function(x: number, y: number): number { return x + y; }; 569 | 570 | function buildName(firstName = "Will", lastName: string) { 571 | return firstName + " " + lastName; 572 | } 573 | 574 | // Rest Parameters 575 | 576 | function buildName(firstName: string, ...restOfName: string[]) { 577 | return firstName + " " + restOfName.join(" "); 578 | } 579 | 580 | function buildName(firstName: string, ...restOfName: string[]) { 581 | return firstName + " " + restOfName.join(" "); 582 | } 583 | 584 | let buildNameFun: (fname: string, ...rest: string[]) => string = buildName; 585 | 586 | 587 | // 重载 588 | 589 | function pickCard(x: {suit: string; card: number; }[]): number; 590 | function pickCard(x: number): {suit: string; card: number; }; 591 | function pickCard(x): any { 592 | // Check to see if we're working with an object/array 593 | // if so, they gave us the deck and we'll pick the card 594 | if (typeof x == "object") { 595 | let pickedCard = Math.floor(Math.random() * x.length); 596 | return pickedCard; 597 | } 598 | // Otherwise just let them pick the card 599 | else if (typeof x == "number") { 600 | let pickedSuit = Math.floor(x / 13); 601 | return { suit: suits[pickedSuit], card: x % 13 }; 602 | } 603 | } 604 | 605 | let myDeck = [{ suit: "diamonds", card: 2 }, { suit: "spades", card: 10 }, { suit: "hearts", card: 4 }]; 606 | let pickedCard1 = myDeck[pickCard(myDeck)]; 607 | alert("card: " + pickedCard1.card + " of " + pickedCard1.suit); 608 | 609 | let pickedCard2 = pickCard(15); 610 | alert("card: " + pickedCard2.card + " of " + pickedCard2.suit); 611 | 612 | ``` 613 | 614 | ### Generics(泛型) 615 | 616 | ``` 617 | function loggingIdentity(arg: T): T { 618 | console.log(arg.length); // Error: T doesn't have .length 619 | return arg; 620 | } 621 | 622 | function loggingIdentity(arg: T[]): T[] { 623 | console.log(arg.length); // Array has a .length, so no more error 624 | return arg; 625 | } 626 | 627 | function identity(arg: T): T { 628 | return arg; 629 | } 630 | 631 | let myIdentity: (arg: T) => T = identity; 632 | 633 | // 也可使用不同泛型表示,只要序列一致 634 | function identity(arg: T): T { 635 | return arg; 636 | } 637 | 638 | let myIdentity: (arg: U) => U = identity; 639 | 640 | // 也可以这么使用 641 | let myIdentity: {(arg: T): T} = identity; 642 | 643 | // 泛型接口 644 | interface GenericIdentityFn { 645 | (arg: T): T; 646 | } 647 | 648 | function identity(arg: T): T { 649 | return arg; 650 | } 651 | 652 | let myIdentity: GenericIdentityFn = identity; 653 | 654 | // 泛型类 655 | class GenericNumber { 656 | zeroValue: T; 657 | add: (x: T, y: T) => T; 658 | } 659 | // 泛型约束 660 | interface Lengthwise { 661 | length: number; 662 | } 663 | 664 | function loggingIdentity(arg: T): T { 665 | console.log(arg.length); // Now we know it has a .length property, so no more error 666 | return arg; 667 | } 668 | 669 | loggingIdentity({length: 10, value: 3}); 670 | 671 | function find(n: T, s: Findable) { 672 | // ... 673 | } 674 | 675 | // 泛型类约束 676 | function create(c: {new(): T; }): T { 677 | return new c(); 678 | } 679 | 680 | class BeeKeeper { 681 | hasMask: boolean; 682 | } 683 | 684 | class ZooKeeper { 685 | nametag: string; 686 | } 687 | 688 | class Animal { 689 | numLegs: number; 690 | } 691 | 692 | class Bee extends Animal { 693 | keeper: BeeKeeper; 694 | } 695 | 696 | class Lion extends Animal { 697 | keeper: ZooKeeper; 698 | } 699 | 700 | function findKeeper (a: {new(): A; 701 | prototype: {keeper: K}}): K { 702 | 703 | return a.prototype.keeper; 704 | } 705 | 706 | ``` 707 | ### Enum 枚举 708 | 709 | ``` 710 | // 普通枚举 711 | enum Direction { 712 | Up = 1, 713 | Down, 714 | Left, 715 | Right 716 | } 717 | // 更多枚举类型 718 | enum FileAccess { 719 | // constant members 720 | None, 721 | Read = 1 << 1, 722 | Write = 1 << 2, 723 | ReadWrite = Read | Write, 724 | // computed member 725 | G = "123".length 726 | } 727 | // 返回自身 728 | enum Enum { 729 | A 730 | } 731 | let a = Enum.A; 732 | let nameOfA = Enum[Enum.A]; // "A" 733 | // 计算 734 | const enum Enum { 735 | A = 1, 736 | B = A * 2 737 | } 738 | ``` 739 | 740 | ### 类型 741 | 742 | ``` 743 | // 类型合并 744 | function padLeft(value: string, padding: string | number) { 745 | // ... 746 | } 747 | 748 | // 类型对象合并,只能合并相同的函数或值,不相同的则报错 749 | interface Bird { 750 | fly(); 751 | layEggs(); 752 | } 753 | 754 | interface Fish { 755 | swim(); 756 | layEggs(); 757 | } 758 | 759 | function getSmallPet(): Fish | Bird { 760 | // ... 761 | } 762 | 763 | let pet = getSmallPet(); 764 | pet.layEggs(); // okay 765 | pet.swim(); // errors 766 | 767 | // 正确用法 判断类型 768 | let pet = getSmallPet(); 769 | 770 | // Each of these property accesses will cause an error 771 | if (pet.swim) { 772 | pet.swim(); 773 | } 774 | else if (pet.fly) { 775 | pet.fly(); 776 | } 777 | // 或者强制转换 778 | let pet = getSmallPet(); 779 | 780 | if ((pet).swim) { 781 | (pet).swim(); 782 | } 783 | else { 784 | (pet).fly(); 785 | 786 | // 类型约束或保护 787 | function isFish(pet: Fish | Bird): pet is Fish { 788 | return (pet).swim !== undefined; 789 | } 790 | 791 | // typeof 792 | 793 | function isNumber(x: any): x is number { 794 | return typeof x === "number"; 795 | } 796 | 797 | // instanceof 798 | if (padder instanceof SpaceRepeatingPadder) { 799 | padder; // type narrowed to 'SpaceRepeatingPadder' 800 | } 801 | // Type Aliases 802 | type Name = string; 803 | type NameResolver = () => string; 804 | type NameOrResolver = Name | NameResolver; 805 | type Container = { value: T }; 806 | type Tree = { 807 | value: T; 808 | left: Tree; 809 | right: Tree; 810 | } 811 | // 交叉类型 812 | type LinkedList = T & { next: LinkedList }; 813 | interface Person { 814 | name: string; 815 | } 816 | 817 | var people: LinkedList; 818 | var s = people.name; 819 | var s = people.next.name; 820 | var s = people.next.next.name; 821 | var s = people.next.next.next.name; 822 | // 字符类型 823 | type Easing = "ease-in" | "ease-out" | "ease-in-out"; 824 | // 重载 825 | function createElement(tagName: "img"): HTMLImageElement; 826 | function createElement(tagName: "input"): HTMLInputElement; 827 | // ... more overloads ... 828 | function createElement(tagName: string): Element { 829 | // ... code goes here ... 830 | } 831 | // 多态 832 | let v = new BasicCalculator(2) 833 | .multiply(5) 834 | .add(1) 835 | .currentValue(); 836 | ``` 837 | ### Iterators(迭代器) and Generators 838 | 839 | ``` 840 | // for of 841 | let someArray = [1, "string", false]; 842 | 843 | for (let entry of someArray) { 844 | console.log(entry); // 1, "string", false 845 | } 846 | ``` 847 | 848 | ### Modules 849 | 850 | ``` 851 | // export 各种类型 852 | export interface StringValidator { 853 | isAcceptable(s: string): boolean; 854 | } 855 | export const numberRegexp = /^[0-9]+$/; 856 | export class ZipCodeValidator implements StringValidator { 857 | isAcceptable(s: string) { 858 | return s.length === 5 && numberRegexp.test(s); 859 | } 860 | } 861 | export { ZipCodeValidator }; 862 | export { ZipCodeValidator as mainValidator }; 863 | // rename 864 | export {ZipCodeValidator as RegExpBasedZipCodeValidator} from "./ZipCodeValidator"; 865 | // export 所有模块 866 | export * from "./ZipCodeValidator"; // exports class ZipCodeValid 867 | // import 868 | import { ZipCodeValidator } from "./ZipCodeValidator"; 869 | import { ZipCodeValidator as ZCV } from "./ZipCodeValidator"; 870 | import * as validator from "./ZipCodeValidator"; 871 | import "./my-module.js"; 872 | // default 873 | export default class ZipCodeValidator 874 | import validator from "./ZipCodeValidator"; 875 | 876 | // export = and import = require() 877 | // export = 878 | export = ZipCodeValidator; 879 | import zip = require("./ZipCodeValidator"); 880 | 881 | // 动态模块 882 | // for nodeJS 883 | declare function require(moduleName: string): any; 884 | import { ZipCodeValidator as Zip } from "./ZipCodeValidator"; 885 | if (needZipValidation) { 886 | let ZipCodeValidator: typeof Zip = require("./ZipCodeValidator"); 887 | let validator = new ZipCodeValidator(); 888 | if (validator.isAcceptable("...")) { /* ... */ } 889 | } 890 | // for requireJS 891 | declare function require(moduleNames: string[], onLoad: (...args: any[]) => void): void; 892 | 893 | import { ZipCodeValidator as Zip } from "./ZipCodeValidator"; 894 | 895 | if (needZipValidation) { 896 | require(["./ZipCodeValidator"], (ZipCodeValidator: typeof Zip) => { 897 | let validator = new ZipCodeValidator(); 898 | if (validator.isAcceptable("...")) { /* ... */ } 899 | }); 900 | } 901 | 902 | // 与其他JavaScript库工作 903 | declare module "url" { 904 | export interface Url { 905 | protocol?: string; 906 | hostname?: string; 907 | pathname?: string; 908 | } 909 | 910 | export function parse(urlStr: string, parseQueryString?, slashesDenoteHost?): Url; 911 | } 912 | 913 | declare module "path" { 914 | export function normalize(p: string): string; 915 | export function join(...paths: any[]): string; 916 | export var sep: string; 917 | } 918 | /// node.d.ts and then load the modules using import url = require("url"); 919 | /// 920 | import * as URL from "url"; 921 | let myUrl = URL.parse("http://www.typescriptlang.org"); 922 | ``` 923 | ### Namespaces 924 | 925 | ``` 926 | // merge namespace 927 | namespace Validation { 928 | export interface StringValidator { 929 | isAcceptable(s: string): boolean; 930 | } 931 | } 932 | /// 933 | namespace Validation { 934 | const numberRegexp = /^[0-9]+$/; 935 | export class ZipCodeValidator implements StringValidator { 936 | isAcceptable(s: string) { 937 | return s.length === 5 && numberRegexp.test(s); 938 | } 939 | } 940 | } 941 | // 别名 942 | namespace Shapes { 943 | export namespace Polygons { 944 | export class Triangle { } 945 | export class Square { } 946 | } 947 | } 948 | import polygons = Shapes.Polygons 949 | let sq = new polygons.Square(); // Same as "new Shapes.Polygons.Square()" 950 | // 环境名称空间 (d.ts) 951 | declare namespace D3 { 952 | export interface Selectors { 953 | select: { 954 | (selector: string): Selection; 955 | (element: EventTarget): Selection; 956 | }; 957 | } 958 | 959 | export interface Event { 960 | x: number; 961 | y: number; 962 | } 963 | 964 | export interface Base extends Selectors { 965 | event: Event; 966 | } 967 | } 968 | 969 | declare var d3: D3.Base; 970 | ``` 971 | ### Module Resolution 972 | 973 | ``` 974 | // /root/src/folder/A.ts 975 | import { b } from "./moduleB" 976 | // 会查找 977 | /root/src/folder/moduleB.ts 978 | /root/src/folder/moduleB.d.ts 979 | 980 | // /root/src/folder/A.ts 981 | import { b } from "moduleB" 982 | // 查找以下文件 983 | /root/src/folder/moduleB.ts 984 | /root/src/folder/moduleB.d.ts 985 | /root/src/moduleB.ts 986 | /root/src/moduleB.d.ts 987 | /root/moduleB.ts 988 | /root/moduleB.d.ts 989 | /moduleB.ts 990 | /moduleB.d.ts 991 | 992 | ``` 993 | 994 | ### Declaration Merging 995 | 996 | ``` 997 | // Merging Interfaces 998 | interface Document { 999 | createElement(tagName: any): Element; 1000 | } 1001 | interface Document { 1002 | createElement(tagName: "div"): HTMLDivElement; 1003 | createElement(tagName: "span"): HTMLSpanElement; 1004 | } 1005 | interface Document { 1006 | createElement(tagName: string): HTMLElement; 1007 | createElement(tagName: "canvas"): HTMLCanvasElement; 1008 | } 1009 | // Merging Namespaces 1010 | namespace Animal { 1011 | let haveMuscles = true; 1012 | 1013 | export function animalsHaveMuscles() { 1014 | return haveMuscles; 1015 | } 1016 | } 1017 | 1018 | namespace Animal { 1019 | export function doAnimalsHaveMuscles() { 1020 | return haveMuscles; // <-- error, haveMuscles is not visible here 1021 | } 1022 | } 1023 | // Merging Namespaces with Classes, Functions, and Enums 1024 | enum Color { 1025 | red = 1, 1026 | green = 2, 1027 | blue = 4 1028 | } 1029 | 1030 | namespace Color { 1031 | export function mixColor(colorName: string) { 1032 | if (colorName == "yellow") { 1033 | return Color.red + Color.green; 1034 | } 1035 | else if (colorName == "white") { 1036 | return Color.red + Color.green + Color.blue; 1037 | } 1038 | else if (colorName == "magenta") { 1039 | return Color.red + Color.blue; 1040 | } 1041 | else if (colorName == "cyan") { 1042 | return Color.green + Color.blue; 1043 | } 1044 | } 1045 | } 1046 | // 全局扩展 1047 | // observable.ts 1048 | export class Observable { 1049 | // ... still no implementation ... 1050 | } 1051 | 1052 | declare global { 1053 | interface Array { 1054 | toObservable(): Observable; 1055 | } 1056 | } 1057 | 1058 | Array.prototype.toObservable = function () { 1059 | // ... 1060 | } 1061 | ``` 1062 | ### Mixins 1063 | 1064 | ``` 1065 | class SmartObject implements Disposable, Activatable 1066 | 1067 | applyMixins(SmartObject, [Disposable, Activatable]); 1068 | 1069 | function applyMixins(derivedCtor: any, baseCtors: any[]) { 1070 | baseCtors.forEach(baseCtor => { 1071 | Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => { 1072 | derivedCtor.prototype[name] = baseCtor.prototype[name]; 1073 | }); 1074 | }); 1075 | } 1076 | ``` 1077 | ## 参考链接 1078 | 1079 | - [Typescript官网](http://www.typescriptlang.org/) 1080 | - [Wiki](https://github.com/Microsoft/TypeScript/wiki) 1081 | - [编译选项](https://github.com/vilic/typescript-guide/blob/master/wiki/compiler-options.md) 1082 | - [TypeScript 新增特性一览](https://github.com/vilic/typescript-guide/blob/master/wiki/whats-new-in-typescript.md) --------------------------------------------------------------------------------