
[{"content":"","date":"May 12, 2026","externalUrl":null,"permalink":"/blog/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"},{"content":" 一、公司定位 # Marvell Technology（MRVL）对普通投资者而言相对陌生。它不像 Apple、NVIDIA 那样直接面向消费者，也不像 Intel 那样长期占据 PC CPU 心智。但在 AI 基础设施时代，它是产业链中极为关键的**\u0026ldquo;隐形卖铲人\u0026rdquo;**。\n如果将整个 AI 世界比作一座超级城市，Marvell 扮演的角色是：\nAI 数据中心里的\u0026quot;高速公路\u0026quot; 数据交换枢纽 网络交通系统 服务器之间的连接器 NVIDIA 负责\u0026quot;算\u0026quot;，Marvell 更偏向负责\u0026quot;传\u0026quot;。在 AI 时代，\u0026ldquo;传输\u0026quot;正在变得和\u0026quot;计算\u0026quot;一样重要——AI 训练的核心瓶颈，已不再是单颗芯片算得够不够快，而是成千上万颗 GPU 能否高效协同。\n二、核心业务与收入来源 # Marvell 本质上销售的是高性能数据中心芯片，主要产品方向如下：\n产品方向 本质作用 网络芯片 让服务器互联 光模块 DSP 让数据高速传输 定制 ASIC 帮大客户定制 AI 芯片 存储控制器 管理数据读写 其业务特点是：不做终端产品，专注于数据中心里的底层零部件，类似高铁里的轨道系统、港口里的调度系统、城市里的电网——普通人看不见，但没有它，整个系统效率会崩塌。\n三、核心产品分析 # 1. 数据中心网络芯片 # 这是 Marvell 目前最重要的业务板块。AI 数据中心并非由单块 GPU 工作，而是 1,000、10,000 甚至更多 GPU 协同训练。这些 GPU 之间需要疯狂交换数据，要求超高速连接、超低延迟、巨大带宽。\nMarvell 的芯片本质上就是\u0026quot;GPU 之间的交通系统\u0026rdquo;：\n道路（物理连接） 红绿灯（流量控制） 高速公路（高带宽通道） 物流系统（数据调度） 如果交通不行，再强的 GPU 都会堵车。\n2. 定制 AI 芯片（ASIC） # 这是市场当前非常关注的增长点。Amazon、Google、Microsoft 等超大云厂商越来越不想完全依赖 NVIDIA，原因包括：\nNVIDIA GPU 太贵，毛利被大量侵蚀 供货持续紧张，被卡生态 希望拥有差异化竞争优势 这些云厂商开始自己设计 AI 芯片，但大部分互联网公司并不具备真正造芯片的能力。于是 Marvell 出现了——它帮这些公司：\n定制芯片架构 完成高速 I/O 设计 实现网络互联方案 推进芯片落地 相当于**\u0026ldquo;AI 芯片代工设计伙伴\u0026rdquo;**。这块业务的特点是：客户一旦采用，通常会绑定多年，客户粘性极强。\n四、产业链位置 # Marvell 在 AI 产业链中的位置非常有意思：\n环节 代表公司 AI 模型 OpenAI GPU NVIDIA 云平台 AWS / Azure 服务器 Dell / Supermicro 网络互联 Marvell / Broadcom 晶圆制造 TSMC Marvell 属于AI 基础设施里的\u0026quot;连接层\u0026quot;。这个位置的特点：\n不像应用层那么\u0026quot;性感\u0026quot;，但非常刚需 技术门槛极高 一旦进入客户体系，很难被替换（类似高速铁路里的信号系统，普通人不会注意，但没人敢随便换供应商） 五、核心客户 # Marvell 的核心客户基本都是全球最有钱、最需要 AI 算力的公司：\n超大云厂商（AWS、Azure、Google Cloud） 数据中心运营商 电信公司 企业级服务器厂商 只有这些公司才真正需要\u0026quot;海量数据传输\u0026quot;，普通企业根本用不到这一层级的基础设施。\n六、客户付费的核心逻辑 # AI 时代最大的瓶颈，正在从\u0026quot;算力\u0026quot;变成**\u0026ldquo;数据流动效率\u0026rdquo;**。\n当 10,000 张 GPU 同时训练时，通信、带宽、延迟、功耗开始成为核心问题。Marvell 的价值就在于帮客户：\n降低延迟：提升数据交换速度 提高吞吐：让更多数据同时流通 节省电力：更高效的传输方案 提高 GPU 利用率：减少 GPU 因网络堵塞而闲置 GPU 非常昂贵，如果因为网络堵塞而闲置，损失极大。因此客户愿意花很多钱解决\u0026quot;连接效率\u0026quot;问题。\n七、与同行的差异化竞争 # Marvell 最核心的竞争力是：定制化能力 + 高速互联能力。\n公司 定位特点 Broadcom 平台型巨头，产品线更广，规模更大 NVIDIA AI 完整生态霸主，GPU 中心化 Marvell AI 基础设施工程公司，专门解决\u0026quot;复杂连接问题\u0026quot;，特别擅长为超级客户做深度定制 AI 数据中心越来越不像\u0026quot;标准电脑\u0026quot;，而像\u0026quot;超级定制工业系统\u0026quot;。Marvell 正是为这种复杂系统提供连接解决方案的专家。\n八、商业模式类比 # 用一个机场系统来理解：\nNVIDIA：造飞机发动机，决定飞机飞得多快 Marvell：负责塔台调度、跑道系统、行李传输、空中交通管理、通信系统 如果这些都做得不好，飞机再强也没用——整个机场会瘫痪。\nAI 数据中心现在越来越像这种系统。真正的瓶颈不是单个芯片，而是整个系统如何协同。Marvell 卖的就是\u0026quot;协同效率\u0026quot;。\n九、长期存在价值 # Marvell 的存在价值，不是\u0026quot;发明 AI\u0026quot;，而是让超大规模 AI 能真正运转起来。\nAI 越发展：\nGPU 越多 数据规模越大 集群越复杂 能耗要求越高 整个世界就越需要：\n更高效的数据传输 更低功耗的连接方案 更复杂的网络架构 Marvell 本质上是在解决：\u0026ldquo;超级算力时代的交通问题\u0026rdquo;。\n市场近两年开始重新给它高估值，正是因为越来越多的人意识到：当 AI 集群不断扩大后，\u0026ldquo;连接效率\u0026quot;会越来越接近\u0026quot;算力本身\u0026quot;的重要性。\n本文为个人研究笔记，不构成投资建议。投资有风险，决策需谨慎。\n","date":"May 12, 2026","externalUrl":null,"permalink":"/blog/posts/2026-05-12-marvell-ai-infrastructure/","section":"Posts","summary":"一、公司定位 # Marvell Technology（MRVL）对普通投资者而言相对陌生。它不像 Apple、NVIDIA 那样直接面向消费者，也不像 Intel 那样长期占据 PC CPU 心智。但在 AI 基础设施时代，它是产业链中极为关键的**“隐形卖铲人”**。\n如果将整个 AI 世界比作一座超级城市，Marvell 扮演的角色是：\nAI 数据中心里的\"高速公路\" 数据交换枢纽 网络交通系统 服务器之间的连接器 NVIDIA 负责\"算\"，Marvell 更偏向负责\"传\"。在 AI 时代，“传输\"正在变得和\"计算\"一样重要——AI 训练的核心瓶颈，已不再是单颗芯片算得够不够快，而是成千上万颗 GPU 能否高效协同。\n二、核心业务与收入来源 # Marvell 本质上销售的是高性能数据中心芯片，主要产品方向如下：\n产品方向 本质作用 网络芯片 让服务器互联 光模块 DSP 让数据高速传输 定制 ASIC 帮大客户定制 AI 芯片 存储控制器 管理数据读写 其业务特点是：不做终端产品，专注于数据中心里的底层零部件，类似高铁里的轨道系统、港口里的调度系统、城市里的电网——普通人看不见，但没有它，整个系统效率会崩塌。\n","title":"Marvell：AI 基础设施时代的隐形卖铲人","type":"posts"},{"content":"","date":"May 12, 2026","externalUrl":null,"permalink":"/blog/posts/","section":"Posts","summary":"","title":"Posts","type":"posts"},{"content":" 第一层：公司本质 # Rocket Lab（RKLB）—— 美国小型火箭发射龙头。核心业务：电子号（Electron）小火箭发射 + 光子号（Photon）卫星平台 + 中子号（Neutron）可回收火箭（研发中）。收入结构：发射服务 ~40% + 航天系统 ~60%。产业链位置：太空基础设施端到端服务商，对标 SpaceX 但聚焦小卫星市场。客户：NASA、美国太空军、商业卫星公司。付费逻辑：高频、快速、低成本的发射窗口，填补 SpaceX 重型火箭空白。与同行差异：专注小卫星高频发射 + 卫星平台定制，毛利率高于传统发射公司但规模仍小。\n第二层：行业格局 # 美国小型发射市场 \u0026lt; 50 亿美元，但增速 20%+ CAGR（低轨星座爆发）。行业处于\u0026quot;商业化放量\u0026quot;阶段，Starlink、SDA 星座驱动需求。关键竞争要素：发射频率、成功率、成本控制。主要对手：SpaceX（猎鹰9号，主力重型）、Virgin Orbit（已破产）、Astra（挣扎中）。风向指标：NASA 合同授予、SDA 订单、年发射次数、太空军合同。\n第三层：护城河 # 最难复制：① 发射频率常态化（年 15+ 次，全球仅次于 SpaceX）；② 端到端能力（火箭 + 卫星平台 + 任务设计）；③ 中子号可回收火箭研发（2026 年首飞）。最大颠覆风险：SpaceX 星舰成功后进一步压低发射成本；中国商业发射崛起（Galactic Energy 等）。\n第四层：财务分析 # 报告期 营收 净利 毛利率 净利率 2025Q1 $122.6M -$60.6M 25.98% -49.4% 2025Q2 $144.5M -$66.4M 29.71% -46.0% 2025Q3 $155.7M -$18.3M 34.03% -11.7% 2025Q4 $179.7M -$52.9M 35.05% -29.4% 全年营收 +46%，加速明显。毛利率从 25.98% 升至 35.05%，规模效应体现。净利仍亏损，经营现金流负，依赖股权融资。危险信号：中子号研发烧钱，现金消耗率需关注。\n第五层：管理层 # CEO Peter Beck（创始人），物理学出身，18 年坚持小火箭路线。历史战略：2006 年创立 → 2017 年首发 → 2020 年上市 → 2023 年光子号放量。资本配置：专注研发（R\u0026amp;D \u0026gt; 营收 30%），无乱收购。市场信任度高（发射成功率 92%+，NASA/太空军合同持续）。最大优点：战略定力 + 执行力。最大风险：过度依赖单一 CEO，中子号首飞技术风险。\n第六层：估值分析 # 现价 $117.35，MA5=$92.96，严重偏离均线，6 个月 +50%。市值 ~$52 亿，PS-TTM ≈ 9.5x，高于高增长航天股中位数（6-8x），估值偏高。\nBull：中子号首飞成功 + NASA/太空军大单 → PS 15x → $180+ Base：按当前增速 → PS 9-10x 维持 → $100-120 震荡 Bear：中子号首飞失败 → PS 5x → $60-70 关键观察：季度发射次数（\u0026gt; 15 次/年）、NASA/太空军新合同、中子号首飞窗口（2026 年 Q3-Q4）。\n第七层：投资结论 \u0026amp; 投资逻辑树 # 【Rocket Lab RKLB】投资逻辑树 ■ 机会 ① 中子号可回收火箭 2026 年首飞，对标 SpaceX 猎鹰9号 ② 低轨星座爆发（Starlink 竞品、SDA），年均发射 20+ 次 ③ 端到端能力（火箭 + 卫星平台 + 任务设计），毛利持续提升 ■ 风险 ① 中子号首飞风险（技术 + 时间线） ② SpaceX 星舰成功后进一步压低发射成本 ③ 现金消耗快，需持续融资稀释股权 ■ 适合：长期持有（3-5 年航天基础设施周期） ■ 不适合：短期波段（估值已反映 2 年预期，技术风险高） ■ 关键指标 - 季度发射次数（目标 \u0026gt; 15 次/年） - NASA/太空军新合同公告 - 中子号首飞时间线（2026 年 Q3-Q4） ■ 目标观察价位 - 建仓区：$80-90（回调至 MA60 附近） - 减仓区：PS \u0026gt; 15x 或 中子号首飞连续推迟 本文为个人研究笔记，不构成投资建议。投资有风险，决策需谨慎。\n","date":"May 12, 2026","externalUrl":null,"permalink":"/blog/posts/2026-05-12-rocket-lab-rklb-seven-layer-analysis/","section":"Posts","summary":"第一层：公司本质 # Rocket Lab（RKLB）—— 美国小型火箭发射龙头。核心业务：电子号（Electron）小火箭发射 + 光子号（Photon）卫星平台 + 中子号（Neutron）可回收火箭（研发中）。收入结构：发射服务 ~40% + 航天系统 ~60%。产业链位置：太空基础设施端到端服务商，对标 SpaceX 但聚焦小卫星市场。客户：NASA、美国太空军、商业卫星公司。付费逻辑：高频、快速、低成本的发射窗口，填补 SpaceX 重型火箭空白。与同行差异：专注小卫星高频发射 + 卫星平台定制，毛利率高于传统发射公司但规模仍小。\n第二层：行业格局 # 美国小型发射市场 \u003c 50 亿美元，但增速 20%+ CAGR（低轨星座爆发）。行业处于\"商业化放量\"阶段，Starlink、SDA 星座驱动需求。关键竞争要素：发射频率、成功率、成本控制。主要对手：SpaceX（猎鹰9号，主力重型）、Virgin Orbit（已破产）、Astra（挣扎中）。风向指标：NASA 合同授予、SDA 订单、年发射次数、太空军合同。\n第三层：护城河 # 最难复制：① 发射频率常态化（年 15+ 次，全球仅次于 SpaceX）；② 端到端能力（火箭 + 卫星平台 + 任务设计）；③ 中子号可回收火箭研发（2026 年首飞）。最大颠覆风险：SpaceX 星舰成功后进一步压低发射成本；中国商业发射崛起（Galactic Energy 等）。\n","title":"Rocket Lab（RKLB）七层投研分析","type":"posts"},{"content":"","date":"May 12, 2026","externalUrl":null,"permalink":"/blog/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"},{"content":"","date":"May 12, 2026","externalUrl":null,"permalink":"/blog/","section":"阿伟的部落格","summary":"","title":"阿伟的部落格","type":"page"},{"content":"","date":"May 12, 2026","externalUrl":null,"permalink":"/blog/categories/%E5%88%86%E6%9E%90/","section":"Categories","summary":"","title":"分析","type":"categories"},{"content":"","date":"May 12, 2026","externalUrl":null,"permalink":"/blog/tags/%E8%82%A1%E7%A5%A8%E7%A0%94%E7%A9%B6/","section":"Tags","summary":"","title":"股票研究","type":"tags"},{"content":"Hello World\n","date":"May 12, 2026","externalUrl":null,"permalink":"/blog/posts/2026-05-12-hello-world/","section":"Posts","summary":"Hello World\n","title":"Hello World","type":"posts"},{"content":"时隔 7 年，重新捡起了自己当年的 blog，好感叹。还记得之前搞 blog 的目的是记录一些技术日常，以及作为面试进入腾讯的其中一个亮点。果然，一进入腾讯后，就没再更新过了，自己也一直不是一个很纯粹的技术人。\n这次重新捡起，也是因为最近开始尝试了之前自己毫无兴趣的摄影，以前觉得摄影给到的感觉不够真实，因为很多摄影作品确实存在大量的图片美化，以至于现实似乎是不存在的，所以自己一直觉得很嗤之以鼻。疫情三年悄然过去，好像三年突然就没了，才想起来自己从来没有去认真记录过什么，于是心血来潮，开始接触摄影，也就有了这个网站重新诞生。\n写了一些文字，但又不知道写了啥，也算是一个开篇吧。之后会悄悄的在这里更新自己的博文、摄影等，也算是对自己生涯的一个记录。\n","date":"August 9, 2023","externalUrl":null,"permalink":"/blog/posts/2023-08-09-the-new-blog/","section":"Posts","summary":"时隔 7 年，重新捡起了自己当年的 blog，好感叹。还记得之前搞 blog 的目的是记录一些技术日常，以及作为面试进入腾讯的其中一个亮点。果然，一进入腾讯后，就没再更新过了，自己也一直不是一个很纯粹的技术人。\n这次重新捡起，也是因为最近开始尝试了之前自己毫无兴趣的摄影，以前觉得摄影给到的感觉不够真实，因为很多摄影作品确实存在大量的图片美化，以至于现实似乎是不存在的，所以自己一直觉得很嗤之以鼻。疫情三年悄然过去，好像三年突然就没了，才想起来自己从来没有去认真记录过什么，于是心血来潮，开始接触摄影，也就有了这个网站重新诞生。\n写了一些文字，但又不知道写了啥，也算是一个开篇吧。之后会悄悄的在这里更新自己的博文、摄影等，也算是对自己生涯的一个记录。\n","title":"这个 blog 又回来了","type":"posts"},{"content":"","date":"October 5, 2016","externalUrl":null,"permalink":"/blog/archives/","section":"Archives","summary":"","title":"Archives","type":"archives"},{"content":" 工作之后，心血来潮，在京东下单了一台灿坤的意式家用半自动咖啡机。自己本身是一个很喜欢喝咖啡的人（一天离开咖啡就浑身不自在），但是经常喝速溶咖啡肯定对健康是不好的，喝星巴克又费钱，所以就入了一台咖啡机。现在已经爱上做意式咖啡这一过程了，同时对自己做的咖啡真心是喝不腻的。\n苦于没有人教授，买了咖啡机两个月来，从来没有成功的到达拉花这个步骤。因为每一次奶泡总是打不好，奶泡总是过厚，奶泡和奶不能很好的融合，导致奶泡不够绵密，不够绵密的奶泡是无法进行拉花的，倒出来就是一坨坨的泡沫。趁着国庆这个假日，终于有时间能够好好研究下奶泡的打法，皇天不负有心人啊，下午终于成功了一次，打出来的奶泡是绵密的，很适合拉花（然而拉花失败了，没有把握好注入点）。\n现在呢，总结下，打奶泡的过程吧，不要轻视这一过程，原本我也只是以为有个机器，只要打出奶泡就行了，什么事都是一键生成。这两个月随着一盒盒的牛奶钱付之东流，我才明白世界原来不是那么简单的（哭）。打个奶泡也是需要学习的，这真是充分说明了学习能力的重要性~看了那么多视频教程和原理讲解，总算是明白了，心疼前期的二逼打泡技术（还有我的牛奶和咖啡豆钱 T T）。\n首先，硬件是必须元素：\n意式半自动咖啡机。 咖啡机预热充足，放出蒸汽管中残留的水分，同时蒸汽管的水蒸气稳定 一个 350ml 的拉花缸（取决使用牛奶的量） 其次，原料也是很重要的：\n必须使用脂肪含量大于 3.0%的纯牛奶（也叫全脂牛奶） 牛奶在 5 摄氏度下为益（当然不要低于 0 摄氏度） 牛奶多于拉花缸的 3 分之一 然后，打泡的手法：\n蒸汽管起先保持在液面上（相切），并且倾斜于液面，蒸汽管的注入点为中心稍微靠人这一侧（不可贴缸壁），斜右上方 45 度靠杯缘处 打开蒸汽，蒸汽管发出嘶嘶的尖叫声（不要吓到，这是没问题的~），这个过程叫做发泡，此时要保证牛奶是在奶缸中小幅度旋转（不是的话，就是注入点错了），这个时候牛奶中的奶泡会越来越多，但是很多都浮在奶泡表面，得到自己想要的奶泡厚度后进入下一步骤（此时牛奶的温度大约 40 摄氏度）。需要注意的是，如果蒸汽管离奶平面太高，奶泡会变巨大；蒸汽管插入太深，就不会有奶泡，只是用花哨的技术加热了牛奶…… 等到奶泡到达你需要的量时，或者温度接近 40 摄氏度，将蒸汽管深入牛奶中，大概 1cm 的深度，此时进入打绵过程。这一过程，主要是让浮在表面的奶泡和底下的牛奶充分融合。注入点稍微往中间靠，依旧 45 度角倾斜，使牛奶旋转起来！此时正确的场景是这样的，牛奶不停的旋转（不跳跃），表面的奶泡在旋转下进入漩涡中间，逐渐消失，奶泡融入了牛奶中。这个过程是没有嘶嘶声的，等到牛奶温度到达 65 摄氏度左右，即可停止了。 最后，还没有结束的，打完的牛奶，呈现的是表面光滑，基本看不见泡泡。通过轻微晃动奶缸，让牛奶和奶泡更均匀，通过奶缸轻震桌面，让大泡泡消失。到这个时候，奶泡就真的完成了。\n注意点是：\n奶泡和奶是千万不能分离的，要融合 注入点一定要使得牛奶旋转！旋转！旋转！不许跳跃！发泡和打绵都要旋转！！全程旋转，奶泡基本成功~ 可以使用水，滴入洗洁精，观察注入点的流动方向（穷人福利） ","date":"October 5, 2016","externalUrl":null,"permalink":"/blog/archives/2016-10-05-close-to-coffee/","section":"Archives","summary":" 工作之后，心血来潮，在京东下单了一台灿坤的意式家用半自动咖啡机。自己本身是一个很喜欢喝咖啡的人（一天离开咖啡就浑身不自在），但是经常喝速溶咖啡肯定对健康是不好的，喝星巴克又费钱，所以就入了一台咖啡机。现在已经爱上做意式咖啡这一过程了，同时对自己做的咖啡真心是喝不腻的。\n苦于没有人教授，买了咖啡机两个月来，从来没有成功的到达拉花这个步骤。因为每一次奶泡总是打不好，奶泡总是过厚，奶泡和奶不能很好的融合，导致奶泡不够绵密，不够绵密的奶泡是无法进行拉花的，倒出来就是一坨坨的泡沫。趁着国庆这个假日，终于有时间能够好好研究下奶泡的打法，皇天不负有心人啊，下午终于成功了一次，打出来的奶泡是绵密的，很适合拉花（然而拉花失败了，没有把握好注入点）。\n现在呢，总结下，打奶泡的过程吧，不要轻视这一过程，原本我也只是以为有个机器，只要打出奶泡就行了，什么事都是一键生成。这两个月随着一盒盒的牛奶钱付之东流，我才明白世界原来不是那么简单的（哭）。打个奶泡也是需要学习的，这真是充分说明了学习能力的重要性~看了那么多视频教程和原理讲解，总算是明白了，心疼前期的二逼打泡技术（还有我的牛奶和咖啡豆钱 T T）。\n首先，硬件是必须元素：\n意式半自动咖啡机。 咖啡机预热充足，放出蒸汽管中残留的水分，同时蒸汽管的水蒸气稳定 一个 350ml 的拉花缸（取决使用牛奶的量） 其次，原料也是很重要的：\n必须使用脂肪含量大于 3.0%的纯牛奶（也叫全脂牛奶） 牛奶在 5 摄氏度下为益（当然不要低于 0 摄氏度） 牛奶多于拉花缸的 3 分之一 然后，打泡的手法：\n蒸汽管起先保持在液面上（相切），并且倾斜于液面，蒸汽管的注入点为中心稍微靠人这一侧（不可贴缸壁），斜右上方 45 度靠杯缘处 打开蒸汽，蒸汽管发出嘶嘶的尖叫声（不要吓到，这是没问题的~），这个过程叫做发泡，此时要保证牛奶是在奶缸中小幅度旋转（不是的话，就是注入点错了），这个时候牛奶中的奶泡会越来越多，但是很多都浮在奶泡表面，得到自己想要的奶泡厚度后进入下一步骤（此时牛奶的温度大约 40 摄氏度）。需要注意的是，如果蒸汽管离奶平面太高，奶泡会变巨大；蒸汽管插入太深，就不会有奶泡，只是用花哨的技术加热了牛奶…… 等到奶泡到达你需要的量时，或者温度接近 40 摄氏度，将蒸汽管深入牛奶中，大概 1cm 的深度，此时进入打绵过程。这一过程，主要是让浮在表面的奶泡和底下的牛奶充分融合。注入点稍微往中间靠，依旧 45 度角倾斜，使牛奶旋转起来！此时正确的场景是这样的，牛奶不停的旋转（不跳跃），表面的奶泡在旋转下进入漩涡中间，逐渐消失，奶泡融入了牛奶中。这个过程是没有嘶嘶声的，等到牛奶温度到达 65 摄氏度左右，即可停止了。 最后，还没有结束的，打完的牛奶，呈现的是表面光滑，基本看不见泡泡。通过轻微晃动奶缸，让牛奶和奶泡更均匀，通过奶缸轻震桌面，让大泡泡消失。到这个时候，奶泡就真的完成了。\n注意点是：\n奶泡和奶是千万不能分离的，要融合 注入点一定要使得牛奶旋转！旋转！旋转！不许跳跃！发泡和打绵都要旋转！！全程旋转，奶泡基本成功~ 可以使用水，滴入洗洁精，观察注入点的流动方向（穷人福利） ","title":"第一次离拉花那么近","type":"archives"},{"content":"工作职责：\n作为 KM 平台中心开发组的员工，这段时间主要负责 KM 外网产品“乐享”的前端开发工作。其中涉及的工作为 PC 端的页面开发、移动端的 H5 开发、共有组件的开发和抽取等需求。主要表现为利用 jQuery、Seajs、LESS 等技术进行相关页面的数据处理，交互逻辑开发，共有组件的开发；利用 Laravel 等技术进行后台的业务逻辑的开发和维护。\n工作成果（主要以开发模块做总结）：\n个人空间模块的前端和后台开发 公司首页、K 吧首页、论坛首页的页面改版 共有组件的开发和抽取 不足之处：\n与设计师在设计稿的出稿时间上沟通不足，导致开发时间分配紊乱 对需求的一些边界条件考虑不够齐全，出现有时开发中出现重构的问题 改进计划：\n分到需求后与设计师提前沟通好出稿时间，对设计工作量大的需求进行开发时间的隔离，避免设计工作量大的需求排在一起，优先级高的需求要优先处理 多与产品进行需求上的沟通，多从产品的角度去思考需求的潜在问题 每天适当空出时间进行新技术的学习 ","date":"September 24, 2016","externalUrl":null,"permalink":"/blog/archives/2016-09-24-probation-summary-in-tencent/","section":"Archives","summary":"工作职责：\n作为 KM 平台中心开发组的员工，这段时间主要负责 KM 外网产品“乐享”的前端开发工作。其中涉及的工作为 PC 端的页面开发、移动端的 H5 开发、共有组件的开发和抽取等需求。主要表现为利用 jQuery、Seajs、LESS 等技术进行相关页面的数据处理，交互逻辑开发，共有组件的开发；利用 Laravel 等技术进行后台的业务逻辑的开发和维护。\n工作成果（主要以开发模块做总结）：\n个人空间模块的前端和后台开发 公司首页、K 吧首页、论坛首页的页面改版 共有组件的开发和抽取 不足之处：\n与设计师在设计稿的出稿时间上沟通不足，导致开发时间分配紊乱 对需求的一些边界条件考虑不够齐全，出现有时开发中出现重构的问题 改进计划：\n分到需求后与设计师提前沟通好出稿时间，对设计工作量大的需求进行开发时间的隔离，避免设计工作量大的需求排在一起，优先级高的需求要优先处理 多与产品进行需求上的沟通，多从产品的角度去思考需求的潜在问题 每天适当空出时间进行新技术的学习 ","title":"腾讯试用期总结（2016年07月10日~2016年09月24日）","type":"archives"},{"content":" 函数的扩展 # ES6 对函数进行了扩展，新的函数支持定义默认值、扩展运算符、写法更加简洁的箭头函数等。\n函数参数的默认值（3 种形式） # 1.函数默认值\nlet fn = function (x = 1, y = 2) { return x + y; }; fn(); // 3 // 代替函数体内判断的写法 let fn = function (x, y) { x = x || 1; y = y || 2; return x + y; }; fn(); // 3 2.函数默认值——解构参数\n可以通过解构变量来定义函数参数的默认值，减少参数的个数\nlet fn = function ({ x = 1, y = 2 }) { return x + y; }; fn(); // 3 3.函数默认值与解构赋值结合\n注意：前面这两种方法都存在一个问题，如果只使用第二个参数，就必须给第一个参数传入undefined来指示第一个参数的默认值，通过解构赋值和函数默认值结合的形式可以避免这样的问题。\n// 既可以不传入参数，也可以指定参数调用 let fn = function ({ x = 1, y = 2 } = {}) { return x + y; }; fn({ y: 2 }); // 3 实例：默认参数与解构赋值混合使用\nlet fn = function (x = 1, { y = 2, z = 3 } = {}) { return x + y + z; }; fn(5); // 10 rest 参数与拓展符 # ES6 也支持了函数 rest 形式的参数（使用者不需要考虑参数的个数），rest 参数必须跟在函数最后面，实例如下：\nlet fn = function (...values) { return values.join(\u0026#34; \u0026#34;); }; fn(\u0026#34;hello\u0026#34;, \u0026#34;i\u0026#39;m\u0026#34;, \u0026#34;waynezheng\u0026#34;); // hello i\u0026#39;m waynezheng 扩展运算符的灵活运用 # // 数组合并 let arr = [...[1, 2, 3], ...[4, 5, 6]]; // [1, 2, 3, 4, 5, 6] // 拆分字符串 let str = [...\u0026#34;hello\u0026#34;]; // [\u0026#39;h\u0026#39;, \u0026#39;e\u0026#39;, \u0026#39;l\u0026#39;, \u0026#39;l\u0026#39;, \u0026#39;o\u0026#39;] 注意：实现了iterator接口的对象，都可以实用扩展运算符进行解构。\n箭头函数 # ES6 引入的箭头函数，使函数的定义方式更加简洁，同时也解决了this绑定的问题。\n基本用法 # let fn = (x) =\u0026gt; x; // 等价于 let fn = function (x) { return x; }; 解决this指向的问题 # let fn = function () { this.name = \u0026#34;fn\u0026#34;; setTimeout(() =\u0026gt; { console.log(this.name); // fn }, 1000); }; fn(); // 代替用that赋值this let fn = function () { let that = this; setTimeout(function () { this.name = \u0026#34;fn\u0026#34;; console.log(this.name); // undefined console.log(that.name); // fn }, 1000); }; fn(); 对象的扩展 # ES6 让对象的定义更加简洁，同时引入了一些常见的第三方框架的功能，实例如下：\n简洁的写法 # 1.返回对象简写\nlet fn = function () { let x, y; return { x, y }; }; // 等价于 let fn = function () { let x, y; return { x: x, y: y, }; }; 2.方法简写\nlet obj = { sayName() { return \u0026#34;Yip Man\u0026#34;; }, }; 3.配合 import，export 的模块化输出简写\nlet foo = { sayName() {}, }; let bar = { sayName() {}, }; exports = { foo, bar }; // 等价于 exports = { foo: foo, bar: bar, }; Object 上的扩展函数 # 1.严谨的值比较\nlet a = NaN; let b = NaN; // 旧的比较 a === b; // false 0 === -0; // true Object.is(a, b); // true Object.is(0, -0); // false 2.对象的浅复制\n// 克隆一个对象 let obj = { a: 1 }; let clone = Object.assign({}, obj); 3.扩展默认属性（代替 jQuery 的 extend 功能）\nconst DEFAULT_PARAM = { url: \u0026#34;localhost\u0026#34;, method: \u0026#34;get\u0026#34;, }; let getBooks = function (options) { let param = Object.assign({}, DEFAULT_PARAM, options); }; getBooks({ data: { a: 1, b: 2 } }); // { url: \u0026#39;localhost\u0026#39;, method: \u0026#39;get\u0026#39;, data: {a: 1, b: 2} } 4.拓展已有对象\n// 假设原有代码存在这样一个对象 let oldObj = function() { ... // 几乎不可维护的代码 } oldObj.prototype = { method() { ... } } Object.assign(oldObj.prototype, { newMethod() { ... } }) Promise 对象 # Promise 是异步编程的解决方案，很简单的说，使用场景就是解决多层嵌套回调函数的问题（简称回调地狱），下面是一些实例：\n解决回调地狱问题 # 有时候可能会遇到这么些需求，第三个请求的参数依赖于第二个请求的结果，第二个请求的参数依赖于第一个请求的结果，而这一过程只能在前端发起，那么可能会写出这样的代码：\n$.get(url1, param, function (res) { let param = res.value; $.get(url2, param, function (res) { let param = res.value; $.get(url3, param, function (res) { // 到了第三层才是在编写业务逻辑 }); }); }); 试想，如果其中一个函数错误，程序就会中断，因此还需要给每个请求加上 error 的回调函数，写出来的代码嵌套了一层又一层。看看用 Promise 来书写这段代码是怎么样的：\n// 假设get函数内部实现了promise $.get(url1, param) .then(function (res) { let param = res.value; return $.get(url2, param); // 返回promise对象 }) .then(function (res) { let param = res.value; return $.get(url3, param); // 返回promise对象 }) .then(function (res) { // 业务逻辑编写 }) .catch(function (error) { // 统一处理回调发生的错误 }); 封装函数支持 promise # 编写一个 promise 的方法也很简单，下面上实例：\n// 以$.ajax为例 function ajaxGet(url, param) { return new Promise((resolve, reject) =\u0026gt; { $.ajax( url, param, function (res) { if (res.state == 200) { resolve(res); } else { reject(\u0026#34;请求不成功！\u0026#34;); } }, function (error) { reject(error); } ); }); } // 使用 ajaxGet(url, param) .then((res) =\u0026gt; { // 业务逻辑，处理resolve }) .catch((err) =\u0026gt; { // 处理reject console.log(err); }); // 这样也可以 ajaxGet(url, param).then( (res) =\u0026gt; { // 业务逻辑，处理resolve }, (err) =\u0026gt; { // 处理reject } ); // 只要返回的是promise对象，则可以做链式调用 ajaxGet(url, param) .then((res) =\u0026gt; { // 业务逻辑，处理resolve return ajaxGet(url2, res); }) .then((res) =\u0026gt; { // 上面的ajaxGet函数的处理 }) .catch((err) =\u0026gt; { // 处理reject console.log(err); }); 包装多个 promise # Promise.all()支持将多个Promise实例，包装成一个新的Promise，使用场景也很广泛。比如需要多个异步操作返回数据后进行处理时：\nlet promises = [1, 2, 4, 5].map((post_id) =\u0026gt; { return ajaxGet(\u0026#34;post/\u0026#34;, { id: post_id }); }); Promise.all(promises) .then((resultArray) =\u0026gt; { // 对所有数据做处理 }) .catch((error) =\u0026gt; { // 处理第一个出现reject的情况 }); 数据降级 # 合理的利用catch方法，可以使数据有更好的处理方式。比如说，某一个模块数据的加载，由于第一次请求服务器抖动了，请求失败，这时可以降级从缓存中取得数据；要是缓存不小心也挂了，可以从localStorage中获取缓存的数据，例子如下：\nfunction getDataFromlocalStorage() { if (window.localStorage \u0026amp;\u0026amp; window.localStorage[\u0026#34;DATA\u0026#34;]) { return new Promise.resolve(window.localStorage[\u0026#34;DATA\u0026#34;]); } else { return new Promise.reject(); } } function getDataFromCache() { return getDataCache(url); } function parseData(data) { return JSON.parse(data); } function getData() { let promise = getDataFromServer(url) .then(parseData) // 正常业务逻辑 .catch(function () { // 请求失败，从服务器缓存取数据 return getDataFromCache().then(parseData); }) .catch(function () { // 服务器缓存取数据失败，从localstorage取数据 return getDataFromlocalStorage().then(parseData); }) .catch(function () { // 没办法了，什么数据都没有，只能处理异常了 }); } Class # ES6 的Class可以视为创建类的语法糖（但本质上实现方式是不一样的），Class的使用会比以往的prototype直观得多：\n1.类的基本使用方法\nclass Person { static is_live = true; // 静态属性 constructor(name, age) { this.name = name; this.age = age; } get name() { return this.name; } set name(name) { this.name = name; } sayName() { alert(this.name); } static eat() { alert(\u0026#34;eat done\u0026#34;); } } let person = new Person(\u0026#34;Yip Man\u0026#34;, 23); person.sayName(); // Yip Man Person.eat(); // eat done 2.类的继承\nclass Man extends Person { constructor(name, age, sex) { super(name, age); this.sex = sex; } get name() { return this.name; } set name(name) { this.name = name; } sayName() { alert(this.name); } } 模块化 # 在 ES6 还没有标准化之前，模块化有 AMD 和 CMD 两种方式，AMD 使用于浏览器端，CMD 使用于服务端，相对应的类库有 requirejs 和 common.js，但是有了 ES6 的模块，当然是使用 ES6 的。\nexport 命令 # export可以输出常量，变量，函数，对象，使用方式很简单：\nexport var firstName = \u0026#39;Michael\u0026#39;; export const A = 1; export function multiply(x, y) { return x * y; }; export { name: \u0026#39;Yip Man\u0026#39; }; export class q { constructor() { this.es6 = \u0026#39;hello\u0026#39;; } } import 命令 # import可以将上面的export输出的导入到指定文件中使用，方式也很简单：\nimport { es6 } from \u0026#34;./someModule\u0026#34;; import { lastName as surname } from \u0026#34;./profile\u0026#34;; // 取别名 该系列完结啦!（如有变动会更新） 如有错误请帮我指出下，感激不尽~\n","date":"August 15, 2016","externalUrl":null,"permalink":"/blog/archives/2016-08-15-es6-2-practice/","section":"Archives","summary":"函数的扩展 # ES6 对函数进行了扩展，新的函数支持定义默认值、扩展运算符、写法更加简洁的箭头函数等。\n函数参数的默认值（3 种形式） # 1.函数默认值\nlet fn = function (x = 1, y = 2) { return x + y; }; fn(); // 3 // 代替函数体内判断的写法 let fn = function (x, y) { x = x || 1; y = y || 2; return x + y; }; fn(); // 3 2.函数默认值——解构参数\n可以通过解构变量来定义函数参数的默认值，减少参数的个数\nlet fn = function ({ x = 1, y = 2 }) { return x + y; }; fn(); // 3 3.函数默认值与解构赋值结合\n注意：前面这两种方法都存在一个问题，如果只使用第二个参数，就必须给第一个参数传入undefined来指示第一个参数的默认值，通过解构赋值和函数默认值结合的形式可以避免这样的问题。\n// 既可以不传入参数，也可以指定参数调用 let fn = function ({ x = 1, y = 2 } = {}) { return x + y; }; fn({ y: 2 }); // 3 实例：默认参数与解构赋值混合使用\nlet fn = function (x = 1, { y = 2, z = 3 } = {}) { return x + y + z; }; fn(5); // 10 rest 参数与拓展符 # ES6 也支持了函数 rest 形式的参数（使用者不需要考虑参数的个数），rest 参数必须跟在函数最后面，实例如下：\n","title":"ES6实践（二）","type":"archives"},{"content":"具体的 ES6 语法，可以参考阮一峰老师写的《ECMAScript 6 入门》。 该文章只整理一些实战中很有用的例子。\nlet\u0026amp;const # let 块级作用域变量声明 # let基本修复了以往var声明的弊端——变量全局污染，变量声明只在块级作用域中有效，不可重复声明，ES6 同时新增了新的块级作用域{}，一些实用的例子如下：\n1.代替原有的闭包创建的块级作用域\n{ // code } // 代替IIFE写法 (function () { // code })(); 2.解决 for 循环索引变化问题\n// 循环结束i被垃圾回收 for (let i = 0; i \u0026lt; 6; i++) {} // 循环结束i的值为5 for (var i = 0; i \u0026lt; 6; i++) {} const 常量声明 # const解决了没有常量定义的问题，定义后的常量不能在修改，不可重复声明，同样的，常量声明只在块级作用域中有效，实用例子很简单：\nconst URL = \u0026#34;http://www.google.com\u0026#34;; // 代替一直写js文件最开头的变量模仿常量 window.URL = \u0026#34;http://www.google.com\u0026#34;; 需要注意的问题 # let声明的变量不属于window的属性\nlet foo = 123; console.log(window.foo); // undefined 变量解构赋值 # 解构可以理解为从指定的数组或者对象中提取数组元素或者对象属性的值，然后赋值到指定变量中，实用的例子：\n变量快速交换 # // 交换x和y的值 let x = 1, y = 2, z = 3; [x, y, z] = [z, x, y]; // 3 1 2 函数的默认赋值 # let fn = function ({ foo = 1, bar = \u0026#34;foo\u0026#34;, hello = false }) { console.log(foo); console.log(bar); console.log(hello); }; fn({}); // 1 // foo // false 遍历 Map 结构 # for (let [key, value] of map) { console.log(key + \u0026#39;=\u0026gt;\u0026#39; value); } 模块快速解析 # // ES6的模块语法 import { selector, spinner } from \u0026#34;Bootstrap.js\u0026#34;; // require.js const { SourceMapConsumer, SourceNode } = require(\u0026#34;source-map\u0026#34;); 字符串的扩展 # ES6 添加很多对String的扩展（本质上是修补语法）\nincludes(), startsWith(), endsWith() # 都支持搜索起始位置的设置，startsWith()查询参数字符是否在源字符串的头部，endsWith()查询参数字符是否在源字符串的尾部\n\u0026#34;hello world\u0026#34;.includes(\u0026#34;wor\u0026#34;); // true // 代替语意不明确的indexOf \u0026#34;hello world\u0026#34;.indexOf(\u0026#34;wor\u0026#34;) !== -1; 模板字符串 # 新的模板字符串功能可以使 html 片段的引入更加美观与规范，嵌入的变量名的形式为${var}，大括号中支持 js 的语法，实用的例子：\nlet basket = { count: 1, onSale: \u0026#34;iPhone\u0026#34; }; // 模板字符串，空格和缩进都会被保留 $(\u0026#34;#result\u0026#34;).append(` There are \u0026lt;b\u0026gt;${basket.count}\u0026lt;/b\u0026gt; items in your basket, \u0026lt;em\u0026gt;${basket.onSale}\u0026lt;/em\u0026gt; are on sale! \u0026lt;p\u0026gt;${basket.count + basket.add}\u0026lt;/p\u0026gt; `); // 代替传统的html字符串 $(\u0026#34;#result\u0026#34;).append( \u0026#34;There are \u0026lt;b\u0026gt;\u0026#34; + basket.count + \u0026#34;\u0026lt;/b\u0026gt; \u0026#34; + \u0026#34;items in your basket, \u0026#34; + \u0026#34;\u0026lt;em\u0026gt;\u0026#34; + basket.onSale + \u0026#34;\u0026lt;/em\u0026gt; are on sale!\u0026#34; ); 数值的扩展 # ES6 添加很多对Number的扩展（本质上还是修补语法），对Math对象也添加了很多实用的函数\nNumber.isFinite(), Number.isNaN(), Number.isInteger() # ES6 之前对Infinity, NaN, 整型的定义不明确，没有对应的检测方法，ES6 添加了相应的检测方法，比较简单，不做示例\n常用的 Math 方法 # // 去除小数部分，返回整数部分 Math.trunc(5.333); // 5 // 判断是否为正数，负数，0 // 参数为正数，返回+1； // 参数为负数，返回-1； // 参数为0，返回0； // 参数为-0，返回-0; // 其他值，返回NaN。 Math.sign(-5); // -1 数组的扩展 # ES6 对数组做了很多实用的扩展，实用的例子如下：\n将类似数组的对象转换为数组 # 1.Array.from()可以将具有隐性的数组属性的对象转换为真正的数组，例如 jQuery 返回的Dom对象，原生 js 的NodeList对象\n// 转换arguments let fn = function () { var args = Array.from(arguments); }; // 转换NodeList let lis = document.querySelectorAll(\u0026#34;li\u0026#34;); let arrLis = Array.from(lis); arrLis.forEach(function () {}); // 数组才能实用forEach 2.使用拓展符也可以达到这种效果\n// 转换arguments let fn = function () { var args = [...arguments]; }; // 转换NodeList let arrLis = [...document.querySelectorAll(\u0026#34;li\u0026#34;)]; arrLis.forEach(function () {}); // 数组才能实用forEach 3.快速组合数组\nArray.of()可以快速的将变量或者常量进行组合，返回新的数组\nlet x = \u0026#34;Man\u0026#34;, y = \u0026#34;Yip\u0026#34;; console.log(Array.of(x, y)); // [\u0026#39;Man\u0026#39;, \u0026#39;Yip\u0026#39;] 更丰富的查找功能 find()\u0026amp;findIndex()，includes() # 1.find()可以通过回调函数进行数组过滤，返回该成员，findIndex()返回索引\nlet arr = [1, 2, 3, 4, 5]; // ES6（需要注意该方法IE的支持性不怎么好） let item = arr.find(function (value, index, arr) { if (value \u0026gt; 3) { return true; } }); // 传统查找 let item; arr.forEach(function () { if (value \u0026gt; 3) { item = value; return; } }); 2.includes()代替语意差的indexOf\nlet arr = [1, 2, 3, 4, 5]; arr.includes(3); // true arr.indexOf(3) !== -1; // true 未完待续\n","date":"August 7, 2016","externalUrl":null,"permalink":"/blog/archives/2016-08-07-es6-practice/","section":"Archives","summary":"具体的 ES6 语法，可以参考阮一峰老师写的《ECMAScript 6 入门》。 该文章只整理一些实战中很有用的例子。\nlet\u0026const # let 块级作用域变量声明 # let基本修复了以往var声明的弊端——变量全局污染，变量声明只在块级作用域中有效，不可重复声明，ES6 同时新增了新的块级作用域{}，一些实用的例子如下：\n1.代替原有的闭包创建的块级作用域\n{ // code } // 代替IIFE写法 (function () { // code })(); 2.解决 for 循环索引变化问题\n// 循环结束i被垃圾回收 for (let i = 0; i \u003c 6; i++) {} // 循环结束i的值为5 for (var i = 0; i \u003c 6; i++) {} const 常量声明 # const解决了没有常量定义的问题，定义后的常量不能在修改，不可重复声明，同样的，常量声明只在块级作用域中有效，实用例子很简单：\n","title":"ES6实践（一）","type":"archives"},{"content":" 订阅-发布模式 # Backbone.js最吸引人的基本上就是事件对订阅-发布模式（pub/sub）的使用了，一个典型的Backbone例子如下：\n// 新建一个model var Person = Backbone.Model.extend({ ... }); var person = new Person({}); // 绑定人名变更事件（订阅） person.on(\u0026#39;callme\u0026#39;, function () { alert(\u0026#39;wayne\u0026#39;); }); // 修改人名，触发事件（发布） person.trigger(\u0026#39;callme\u0026#39;); // 此时会弹出\u0026#39;wayne\u0026#39;的信息 // 解除事件绑定（取消订阅） person.off(\u0026#39;callme\u0026#39;); person.trigger(\u0026#39;callme\u0026#39;); // 什么都不执行 有了这个模式，我们就可以做到一些比较节省工作的事情，一个典型的应用场景——在线笔记：\n用户在笔记的其中一个分类 backbone学习 点击添加一个新的笔记并保存 保存成功，笔记总数+1 使用jQuery的大致实现过程如下：\n$(\u0026#39;#save-btn\u0026#39;).click(function() { // 保存笔记 $.ajax(function() { success: function() { changeCount(); } }); };); 看着似乎没什么问题，但是如果需求改变了，比如变成这样：\n用户在笔记的其中一个分类 backbone学习 点击添加一个新的笔记并保存 保存成功后，笔记总数+1、笔记编写界面刷新、本地笔记缓存同步、服务器缓存同步、自动分享到团队笔记中。。。 这个时候代码会变成这样：\n$(\u0026#39;#save-btn\u0026#39;).click(function() { // 保存笔记 $.ajax(function() { success: function() { dataView.changeCount(); noteView.refresh(); local.syncNote(); server.syncNote(); share.toMyTeam(); ... } }); };); 此时，各模块的代码严重耦合在了一起，写这段代码的人同时需要了解其他模块有什么函数，其他模块的编写者也不能随意更改自己的模块接口。\n而使用Backbone.js编写可以避免这个问题，大致实现过程如下：\n各模块订阅noteModel的save-note事件，并写好callback\ndataView.listenTo(noteModel, \u0026#39;save-note\u0026#39;, dataView.changeCount); noteView.listenTo(noteModel, \u0026#39;save-note\u0026#39;, noteView.refresh); local.listenTo(noteModel, \u0026#39;save-note\u0026#39;, local.syncNote); server.listenTo(noteModel, \u0026#39;save-note\u0026#39;, server.syncNote); share.listenTo(noteModel, \u0026#39;save-note\u0026#39;, share.toMyTeam); 在Note View中点击保存时，noteModel发布save-note事件\n$(\u0026#39;#save-btn\u0026#39;).click(function() // 保存笔记 $.ajax(function() { success: function() { noteModel.trigger(\u0026#39;save-note\u0026#39;); } }) }) 如上所示，编写保存模块的人不需要再去了解其他模块的内容，只需要在保存成功后发布通知，其他模块的人也不需要担心模块变更影响到别人的问题，只需要专注于自己的模块编写。这样减少了代码的耦合性。\nBackbone.js中发布-订阅模式的实现 # 一开始Backbone.js 0.1.0中的发布-订阅模式是很简单的\nbind将所有事件绑定到this._callbacks对象中 unbind通过for循环将指定事件删除 trigger通过for循环触发匹配的事件 extend将Event拓展到其他模块上 代码如下：\nBackbone.Events = { // 绑定事件到ev上，通过callback进行回调 // ev等于\u0026#39;all\u0026#39;时，所有事件触发都会激活callback回调 bind: function(ev, callback) { // 总回调对象，存储所有回调的事件，格式如下： // this._callbacks = { // \u0026#39;ev\u0026#39; : [ // function callback1() {}, // function callback2() {} // ... // ] // } var calls = this._callbacks || (this._callbacks = {}); // 将回调函数加入this._callbacks对应的ev中，此处ev是数组， // 因为存在着一个事件绑定多个回调函数的情况 var list = this._callbacks[ev] || (this._callbacks[ev] = []); list.push(callback); // 链式调用 return this; }, // 移除一个或者多个回调函数，如果callback为空，移除该事件的所有callback， // 如果事件为空，移除所有事件的绑定 unbind: function(ev, callback) { var calls; if (!ev) { // 移除所有绑定 this._callbacks = {}; } else if (calls = this._callbacks) { if (!callback) { calls[ev] = []; } else { var list = calls[ev]; // 该事件下回调为空，返回 if (!list) return this; // 移除事件 for (var i = 0, l = list.length; i \u0026lt; l; i++) { if ( callback=== list[i]) { list.splice(i, 1); break; } } } } // 链式调用 return this; }, // 触发一个事件，执行所有绑定的回调 // 除了事件名称，所有回调都要传递同样的参数\u0026#39;trigger\u0026#39; trigger: function(ev) { var list, calls, i, l; var calls = this._callbacks; // callback队列为空，走你 if (!(calls = this._callbacks)) return this; if (list = calls[ev]) { for (i = 0, l = list.length; i \u0026lt; l; i++) { // 传递参数到回调函数并触发事件 list[i].apply(this, _.rest(arguments)); } } if (list = calls[\u0026#39;all\u0026#39;]) { for (i = 0, l = list.length; i \u0026lt; l; i++) { list[i].apply(this, arguments); } } return this; } }; _.extend(Backbone.Model.prototype, Backbone.Events, {}); 到Backbone.js 0.9.0之前做的都是一些小的语意重构或者bug修正，并没有什么太大的变化，Backbone.js 0.9.0对Event做了优化\nbind改为业界通用的on，即语意重构，但是向下兼容 通过队列结构来重构for循环事件绑定（个人觉得意义不大，js中数组的处理速度是比直接读对象快的） 代码如下：\nBackbone.Events = { // 语意重构，将bind改为on，unbind改为off on: function(events, callback, context) { var ev; events = events.split(/\\s+/); // 注意，此处calls指向this._callbacks，即calls改变，this._callbacks也会改变 var calls = this._callbacks || (this._callbacks = {}); // callback队列 while (ev = events.shift()) { // 每次取出一个事件 // tail是一个空的对象，用于充当下一个节点 // 此处的结构是这样的： // this._callbacks = { // event1: { // next: { // callback: function(){}, // context: el, // next: { // callback: function(){}, // context: el, // next: { // ... // tail: （等于next） // } // } // }, // tail: {} // }, // // event2: { // ... // } // } var list = calls[ev] || (calls[ev] = {}); var tail = list.tail || (list.tail = list.next = {}); tail.callback = callback; // 回调 tail.context = context; // 执行环境上下文，相当于this变量 list.tail = tail.next = {}; } return this; }, off: function(events, callback, context) { var ev, calls, node; if (!events) { // 清空所有事件的所有绑定 delete this._callbacks; } else if (calls = this._callbacks) { events = events.split(/\\s+/); while (ev = events.shift()) { // 先清空该事件下的所有绑定： node = calls[ev]; // 这里的node等于上面on中的list delete calls[ev]; // 清空xx事件下的所有绑定的方法 // Object.off(\u0026#39;add\u0026#39;)的情况 if (!callback || !node) continue; // Object.off(\u0026#39;add\u0026#39;, \u0026#39;addOne\u0026#39;)的情况下， // 将context不匹配的重新绑定 // Create a new list, omitting the indicated event/context pairs. while ((node = node.next) \u0026amp;\u0026amp; node.next) { // Object.off(\u0026#39;add\u0026#39;, \u0026#39;addOne\u0026#39;, this.$input)的情况， // context一致则过滤掉 if (node.callback === callback \u0026amp;\u0026amp; (!context || node.context === context)) continue; // 将context不匹配的重新绑定 this.on(ev, node.callback, node.context); } } } return this; }, trigger: function(events) { var event, node, calls, tail, args, all, rest; // 没有回调则返回当前对象，便于链式调用 if (!(calls = this._callbacks)) return this; all = calls[\u0026#39;all\u0026#39;]; (events = events.split(/\\s+/)).push(null); // null是为了制造循环的结束标志 while (event = events.shift()) { // all事件的入栈处理，多加一个event用于区分 if (all) events.push({ next: all.next, tail: all.tail, event: event }); // 其他事件的入栈处理 if (!(node = calls[event])) continue; events.push({ next: node.next, tail: node.tail }); } rest = slice.call(arguments, 1); // 取出用户自定义参数，即events后面接的参数 while (node = events.pop()) { tail = node.tail; // 如果是all事件，则将event并入rest中 args = node.event ? [node.event].concat(rest) : rest; while ((node = node.next) !== tail) { // tail等于next，则说明该事件的所有回调已经结束 // 调用事件 node.callback.apply(node.context || this, args); } } return this; } }; ","date":"June 18, 2016","externalUrl":null,"permalink":"/blog/archives/2016-06-18-read-backbonejs-source-code-3/","section":"Archives","summary":"订阅-发布模式 # Backbone.js最吸引人的基本上就是事件对订阅-发布模式（pub/sub）的使用了，一个典型的Backbone例子如下：\n// 新建一个model var Person = Backbone.Model.extend({ ... }); var person = new Person({}); // 绑定人名变更事件（订阅） person.on('callme', function () { alert('wayne'); }); // 修改人名，触发事件（发布） person.trigger('callme'); // 此时会弹出'wayne'的信息 // 解除事件绑定（取消订阅） person.off('callme'); person.trigger('callme'); // 什么都不执行 有了这个模式，我们就可以做到一些比较节省工作的事情，一个典型的应用场景——在线笔记：\n用户在笔记的其中一个分类 backbone学习 点击添加一个新的笔记并保存 保存成功，笔记总数+1 使用jQuery的大致实现过程如下：\n$('#save-btn').click(function() { // 保存笔记 $.ajax(function() { success: function() { changeCount(); } }); };); 看着似乎没什么问题，但是如果需求改变了，比如变成这样：\n用户在笔记的其中一个分类 backbone学习 点击添加一个新的笔记并保存 保存成功后，笔记总数+1、笔记编写界面刷新、本地笔记缓存同步、服务器缓存同步、自动分享到团队笔记中。。。 这个时候代码会变成这样：\n$('#save-btn').click(function() { // 保存笔记 $.ajax(function() { success: function() { dataView.changeCount(); noteView.refresh(); local.syncNote(); server.syncNote(); share.toMyTeam(); ... } }); };); 此时，各模块的代码严重耦合在了一起，写这段代码的人同时需要了解其他模块有什么函数，其他模块的编写者也不能随意更改自己的模块接口。\n","title":"阅读backbone源码——订阅-发布模式（pub/sub）","type":"archives"},{"content":"","date":"May 28, 2016","externalUrl":null,"permalink":"/blog/categories/life/","section":"Categories","summary":"","title":"Life","type":"categories"},{"content":"","date":"May 28, 2016","externalUrl":null,"permalink":"/blog/tags/%E9%9A%8F%E7%AC%94/","section":"Tags","summary":"","title":"随笔","type":"tags"},{"content":"学校附近一家店有一个外卖小哥，年龄跟我差不多，二十出头，留着军队小兵的平头，皮肤是典型的东方黄，不偏不倚，大眼睛，浓眉毛，身高跟我也差不多，一米七几，说高不算，说矮也勉强，中等身材，约莫 120 斤，130 斤。\n小哥平日里负责的是送外卖，主要服务对象也是学校里的学生，每天骑着一辆小电驴，电驴上驾着一泡沫箱装着的外卖，呼呼地穿梭于这几个校区之间。\n可能是我名字比较特别，第二次点这家的外卖时，在宿舍楼下见到小哥，像往常一样报了宿舍名。小哥拿起写着我名字的那份，皱了下浓浓的眉毛。\n\u0026ldquo;郑伟柏是吧？你的外卖。\u0026quot;。\n\u0026ldquo;额……对\u0026rdquo;。\n习惯于别人总是叫错我的名字了，我只是愣了下，并没有去更正小哥的话，我想叫错就错吧，也就拿下外卖而已。\n接下来的日子里，说来也奇怪。小哥就这样记住了我错误的名字，隔了好长一段时间点他家的外卖，小哥每次送过来，电话接起，对面那头是直接喊起我的名字了。\u0026ldquo;郑伟柏，你的外卖，麻烦来楼下拿一下！\u0026quot;，\u0026ldquo;郑伟柏，你外卖到了\u0026rdquo;，\u0026ldquo;郑伟柏，外卖在楼下！\u0026quot;……\n小哥不知咋地，叫我名字特勤快，每次隔了大老远，骑着小电驴，就叫我的名字，看了那么多次订单上的我的名字，却没有一次改正叫法，这让我觉得很是有趣。小哥每次都笑的很开心，远远的就看见他咧着大嘴，露出一口白牙，骑着小电驴跑上斜坡来，丝毫\n没有因为这份日复一日，年复一年的枯燥工作心烦过。\n临近毕业了。这几天下雨，又叫了他家的外卖，想想这应该是最后一次叫他家的外卖了吧！八点钟点的外卖，小哥 8 点 30 分来了电话，依旧是那熟悉的开头。下了楼，不见小哥的身影。过了两分钟，小哥穿着件旧蓝色的雨衣从斜坡上慢慢跑了上来，左手提着我的外卖，大老远的依旧是标志性的表情——咧嘴笑。\n\u0026ldquo;哈哈，我电动车在斜坡下骑到一半没电了！郑伟柏，你的外卖啊！\u0026quot;。\n\u0026ldquo;那你咋回去啊现在，这下大雨的，那电动车可以直接脚踏吧？\u0026quot;。\n\u0026ldquo;哈哈，可以啊，我慢慢骑回去，就你一份而已，无所谓无所谓，送完就下班了都。得了得了哈，我下去牵车走了，再见再见\u0026rdquo;。\n\u0026ldquo;嘿小哥。\u0026quot;。\n\u0026ldquo;咋啦咋啦？\u0026quot;。\n\u0026ldquo;我名字叫 ABC（这里代指三字姓名），不叫 ACB\u0026rdquo;。\n\u0026ldquo;嘿，好好好，原来我老叫错了啊，ABC，不管不管，ACB 叫熟了都\u0026rdquo;。\n小哥晃晃悠悠的跑下了斜坡，过会儿渐渐消失在雨中。\n再见啦，有趣的外卖小哥。\n","date":"May 28, 2016","externalUrl":null,"permalink":"/blog/archives/2016-05-28-jotting/","section":"Archives","summary":"学校附近一家店有一个外卖小哥，年龄跟我差不多，二十出头，留着军队小兵的平头，皮肤是典型的东方黄，不偏不倚，大眼睛，浓眉毛，身高跟我也差不多，一米七几，说高不算，说矮也勉强，中等身材，约莫 120 斤，130 斤。\n小哥平日里负责的是送外卖，主要服务对象也是学校里的学生，每天骑着一辆小电驴，电驴上驾着一泡沫箱装着的外卖，呼呼地穿梭于这几个校区之间。\n可能是我名字比较特别，第二次点这家的外卖时，在宿舍楼下见到小哥，像往常一样报了宿舍名。小哥拿起写着我名字的那份，皱了下浓浓的眉毛。\n“郑伟柏是吧？你的外卖。\"。\n“额……对”。\n习惯于别人总是叫错我的名字了，我只是愣了下，并没有去更正小哥的话，我想叫错就错吧，也就拿下外卖而已。\n接下来的日子里，说来也奇怪。小哥就这样记住了我错误的名字，隔了好长一段时间点他家的外卖，小哥每次送过来，电话接起，对面那头是直接喊起我的名字了。“郑伟柏，你的外卖，麻烦来楼下拿一下！\"，“郑伟柏，你外卖到了”，“郑伟柏，外卖在楼下！\"……\n小哥不知咋地，叫我名字特勤快，每次隔了大老远，骑着小电驴，就叫我的名字，看了那么多次订单上的我的名字，却没有一次改正叫法，这让我觉得很是有趣。小哥每次都笑的很开心，远远的就看见他咧着大嘴，露出一口白牙，骑着小电驴跑上斜坡来，丝毫\n没有因为这份日复一日，年复一年的枯燥工作心烦过。\n临近毕业了。这几天下雨，又叫了他家的外卖，想想这应该是最后一次叫他家的外卖了吧！八点钟点的外卖，小哥 8 点 30 分来了电话，依旧是那熟悉的开头。下了楼，不见小哥的身影。过了两分钟，小哥穿着件旧蓝色的雨衣从斜坡上慢慢跑了上来，左手提着我的外卖，大老远的依旧是标志性的表情——咧嘴笑。\n“哈哈，我电动车在斜坡下骑到一半没电了！郑伟柏，你的外卖啊！\"。\n“那你咋回去啊现在，这下大雨的，那电动车可以直接脚踏吧？\"。\n“哈哈，可以啊，我慢慢骑回去，就你一份而已，无所谓无所谓，送完就下班了都。得了得了哈，我下去牵车走了，再见再见”。\n“嘿小哥。\"。\n“咋啦咋啦？\"。\n“我名字叫 ABC（这里代指三字姓名），不叫 ACB”。\n“嘿，好好好，原来我老叫错了啊，ABC，不管不管，ACB 叫熟了都”。\n小哥晃晃悠悠的跑下了斜坡，过会儿渐渐消失在雨中。\n再见啦，有趣的外卖小哥。\n","title":"外卖小哥","type":"archives"},{"content":"PDO 提供了一个PDOStatement::debugDumpParams — 打印一条 SQL 预处理命令的调试方法，但是打印出来的结果有点鸡肋，比如说：\n\u0026lt;?php try { $pdo = new PDO(); $sql = \u0026#34;SELECT * FROM `users` WHERE `username` = ? AND `email` = ?\u0026#34;; $stmt = $pdo-\u0026gt;prepare($sql); $stmt-\u0026gt;bindParam(1, $username, PDO::PARAM_STR); $stmt-\u0026gt;bindParam(2, $email, PDO::PARAM_STR); $username = \u0026#39;Saul Goodman\u0026#39;; $email = \u0026#39;********@qq.com\u0026#39;; $stmt-\u0026gt;execute(); $stmt-\u0026gt;debugDumpParams(); } catch (PDOException $e) { echo $e-\u0026gt;getMessage(); } // 结果很感人： // SQL: [58] SELECT * FROM `users` WHERE `username` = ? AND `email` = ? // Params: 2 // Key: Position #0: // paramno=0 // name=[0] \u0026#34;\u0026#34; // is_param=1 // param_type=2 // Key: Position #1: // paramno=1 // name=[0] \u0026#34;\u0026#34; // is_param=1 // param_type=2 ?\u0026gt; 要是 SQL 语句一复杂这根本就阅读不了嘛！\nPDO 的执行方式的不同，导致 SQL 语句并不是在参数绑定上去后再传入数据库服务器的，上面的这段代码它在内部的执行方式是这样的：\n\u0026lt;?php // $stmt = $pdo-\u0026gt;prepare($sql); prepare sql from \u0026#39;SELECT * FROM `users` WHERE `username` = ? AND `email` = ?\u0026#39; // $username = \u0026#39;Saul Goodman\u0026#39;; // $email = \u0026#39;********@qq.com\u0026#39;; // $stmt-\u0026gt;bindParam(1, $username, PDO::PARAM_STR); // $stmt-\u0026gt;bindParam(2, $email, PDO::PARAM_STR); set @username = \u0026#39;Saul Goodman\u0026#39; set @email = \u0026#39;********@qq.com\u0026#39; // $stmt-\u0026gt;execute(); execute sql using @username, @email ?\u0026gt; 可以看到，PDO 的执行在 mysql 中其实是分步进行的，因此 PDO 并没有办法去获取到绑定参数后完整的 SQL 语句\n当然还是有解决的调试方法的，就是通过 mysql 日志来查看所有的数据库查询，以下是在 windows 平台，通过 mysql 控制台来设置（也可以通过 mysql my.ini 配置文件来设置）：\n# 查看日志情况 # show variables like \u0026#39;%general%\u0026#39;; # 开启日志 # SET GLOBAL general_log = \u0026#39;On\u0026#39;; # 指定日志文件 # SET GLOBAL general_log_file = \u0026#39;E:/my.log\u0026#39;; 再执行一遍刚刚额查询，就可以在 E:/my.log 中看到以下的内容了：\n160411 13:26:27 4 Connect root@localhost on mall 4 Query SELECT * FROM `users` WHERE `username` = \u0026#39;Saul Goodman\u0026#39; AND `email` = \u0026#39;********@qq.com\u0026#39; 4 Quit ","date":"April 11, 2016","externalUrl":null,"permalink":"/blog/archives/2016-04-11-how-to-debug-pdo-database-queries/","section":"Archives","summary":"PDO 提供了一个PDOStatement::debugDumpParams — 打印一条 SQL 预处理命令的调试方法，但是打印出来的结果有点鸡肋，比如说：\n\u003c?php try { $pdo = new PDO(); $sql = \"SELECT * FROM `users` WHERE `username` = ? AND `email` = ?\"; $stmt = $pdo-\u003eprepare($sql); $stmt-\u003ebindParam(1, $username, PDO::PARAM_STR); $stmt-\u003ebindParam(2, $email, PDO::PARAM_STR); $username = 'Saul Goodman'; $email = '********@qq.com'; $stmt-\u003eexecute(); $stmt-\u003edebugDumpParams(); } catch (PDOException $e) { echo $e-\u003egetMessage(); } // 结果很感人： // SQL: [58] SELECT * FROM `users` WHERE `username` = ? AND `email` = ? // Params: 2 // Key: Position #0: // paramno=0 // name=[0] \"\" // is_param=1 // param_type=2 // Key: Position #1: // paramno=1 // name=[0] \"\" // is_param=1 // param_type=2 ?\u003e 要是 SQL 语句一复杂这根本就阅读不了嘛！\n","title":"PHP PDO参数绑定查询后，查看实际执行SQL的方法","type":"archives"},{"content":"原生 js 轮播组件的代码在这：https://github.com/wynn719/study-baidu-ife/tree/master/task0002/task0002_3\n之前做的时候，会发现轮播在浏览器可见的情况下（没有缩小，没有切换标签），轮播是正常运行的；而当浏览器不可见的情况下（缩小, 切换标签），重新打开页面，轮播会出现几秒钟抽搐（真的很像）的现象，然后又恢复正常运行。\n排除了动画逻辑，定时器泄露的问题，偶然情况下，发现 IE 下居然没问题！\n看来崇拜的 chrome 也有抽筋的时候，**即 chrome 在浏览器不可见的情况下（缩小, 切换标签），会把定时器挂起（不会在后台运行），因此重新打开时，挂起的 n 个定时器突然运行，**所以 chrome 就抽风了！\n说了这么多，解决方法很简单，只要监听这个事件 visibilitychange ：\n// fix chrome下的bug，定时器切换选项卡时被挂起 document.addEventListener(\u0026#34;visibilitychange\u0026#34;, function () { if (document.visibilityState == \u0026#34;visible\u0026#34;) { slide(); // 未挂起时执行轮播 } else if (document.visibilityState == \u0026#34;hidden\u0026#34;) { clearInterval(timer); // 挂起时清除定时器 } }); 问题就解决了~\ndemo 展示在这：http://wynn719.github.io/study-baidu-ife/task0002/task0002_3/task0002_3.html\n（可恶，这个问题居然困惑了我好久，气煞我也，所以要记录下来……）\n","date":"February 27, 2016","externalUrl":null,"permalink":"/blog/archives/2016-02-27-notice-chrome-inactive-tab/","section":"Archives","summary":"原生 js 轮播组件的代码在这：https://github.com/wynn719/study-baidu-ife/tree/master/task0002/task0002_3\n之前做的时候，会发现轮播在浏览器可见的情况下（没有缩小，没有切换标签），轮播是正常运行的；而当浏览器不可见的情况下（缩小, 切换标签），重新打开页面，轮播会出现几秒钟抽搐（真的很像）的现象，然后又恢复正常运行。\n排除了动画逻辑，定时器泄露的问题，偶然情况下，发现 IE 下居然没问题！\n看来崇拜的 chrome 也有抽筋的时候，**即 chrome 在浏览器不可见的情况下（缩小, 切换标签），会把定时器挂起（不会在后台运行），因此重新打开时，挂起的 n 个定时器突然运行，**所以 chrome 就抽风了！\n说了这么多，解决方法很简单，只要监听这个事件 visibilitychange ：\n// fix chrome下的bug，定时器切换选项卡时被挂起 document.addEventListener(\"visibilitychange\", function () { if (document.visibilityState == \"visible\") { slide(); // 未挂起时执行轮播 } else if (document.visibilityState == \"hidden\") { clearInterval(timer); // 挂起时清除定时器 } }); 问题就解决了~\ndemo 展示在这：http://wynn719.github.io/study-baidu-ife/task0002/task0002_3/task0002_3.html\n（可恶，这个问题居然困惑了我好久，气煞我也，所以要记录下来……）\n","title":"chrome网页切换时，js定时器的问题","type":"archives"},{"content":" 从14年下半个学期开始，养成了读书的这个习惯，从一开始的厌倦到现在的欲罢不能，非常感谢昔日的自己能够坚持这个好习惯，并且让这个习惯成功的转型成了爱好。\n以前从来不懂得读书的好处，“读了10本书后，你会以为身边不读书的人都很愚蠢；读了50本书后，你慢慢地发现自己才是最愚昧无知的人；你读的书越多，就越认识到自己的不足~”，这是在某个地方看到的，现在觉得真的很有道理。\n既然是读书小结，那就不要太多废话了，直接贴图吧，感谢大神们做出了一个如此好用的豆瓣统计！\n小结 # 今年读的书有点多啊，70本，读下来觉得似乎有点过了，接下来每年应该保持50本就差不多，不然吸收不了啊！再接再厉！\n","date":"December 31, 2015","externalUrl":null,"permalink":"/blog/archives/2015-12-31-read-in-2015/","section":"Archives","summary":" 从14年下半个学期开始，养成了读书的这个习惯，从一开始的厌倦到现在的欲罢不能，非常感谢昔日的自己能够坚持这个好习惯，并且让这个习惯成功的转型成了爱好。\n以前从来不懂得读书的好处，“读了10本书后，你会以为身边不读书的人都很愚蠢；读了50本书后，你慢慢地发现自己才是最愚昧无知的人；你读的书越多，就越认识到自己的不足~”，这是在某个地方看到的，现在觉得真的很有道理。\n既然是读书小结，那就不要太多废话了，直接贴图吧，感谢大神们做出了一个如此好用的豆瓣统计！\n小结 # 今年读的书有点多啊，70本，读下来觉得似乎有点过了，接下来每年应该保持50本就差不多，不然吸收不了啊！再接再厉！\n","title":"2015的读书小结","type":"archives"},{"content":"最近读了一本书，松浦弥太郎的《今天也要用心过生活》，书里的道理总是很简单，印证那句老掉牙了的话：道理我都懂，但依然过不好这一生。突然想到，道理明明很简单，我缺乏的是主观能动性吧。生活本可以常驻新鲜，在看不到的地方保持自己的天真，curiosity，小孩子的天性，人长大后就没了，人也衰老的越来越快，心也就变硬了。\n2015年快过去了，不管过的好不好，2016年终将来临。我想借由这本书的内容审视下2015年的自己，2015年所做得不好的地方要一一对自己指出，然后给2016年的自己好好指点一下迷津，让2016年的我活得更加有意义。\n以下是书的内容和对自己的审视：\nChapter 1 健康的早餐 # 每天的自我改造\n2015：会尝试着学习一些小知识，小技能来丰富自己的生活，但持续性不好，一年下来不到100天\n2016：一天中花时间学习小的爱好，争取做到持续时间大于150天\n“早安”的功效\n2015：经常会回避那些自己不喜欢接近的人（除去一些因为三观不合而不像认识的）\n2016：多尝试主动与他们沟通，减少尴尬\n让自己从容的一小时\n2015：经常给自己独处的空间，这个做得挺好\n2016：可以把这一小时从早起中抽出，继续保持\n发现快乐，用心下功夫\n2015：缺乏在小事中找快乐的能力\n2016：如果感到做这件小事能够开心，那就尽管去做吧\n发现全新自己的捷径\n不认同，略过。\n清洁的自信\n2015：做得挺好的\n2016：继续保持\n起而行的行动家\n2015：经常犹犹豫豫，但事后发现事情明明可以很快就做好决定，而且收效更好\n2016：忌讳犹豫不决，从简单的事情开始做，小事在10分钟之内决定好，大事在1个小时到半天内决定好\n自在的节奏\n2015：对自己的生活节奏和工作节奏把握的不是很好，多半是因为情绪的波动导致自己节奏被打乱，没有注意合作伙伴的节奏，比如有时候容易打扰到别人，没有事先沟通好交流时间\n2016：需要注意调整自己的节奏，同时也要注意合作伙伴的节奏，切勿心急\n用心吃饭\n虽然没有自己亲手去做饭，但一直很用心的吃饭，略过\n优雅的握筷方式\n不认同，略过\n干净的姿态\n2015：作为一个coder，虽然仪表是弄得比较整洁，但是姿态总是不够干净利落\n2016：保持仪表整洁干净的同时，要多健身多锻炼，保持姿态的自然，落落大方，多注意不要弯腰驼背，坐下时不要老是头前倾\n遇见“老师”\n2015：遇到一些行为举止不雅的人，太容易意气用事了\n2016：把那些你所厌恶的人和行为当作你自己的“老师”，有则改之，无则加勉，看不惯的东西，笑笑而过就好了\n让笑容为生活保鲜\n做得挺好的，继续保持\n今天也要用心生活\n2015：每个人都从内心里抗拒新事物的发生，抗拒面对新事物，有时候我会很喜欢去尝试新事物，但有的时候还是太畏畏缩缩，容易因为情绪失控而厌倦面对新事物\n2016：年纪越大，越要有勇气挑战新事物，从小事调整开始，尝试着去学习身边每一个细微的事\nChapter 2 上乘的午餐 # 分享喜悦\n2015：这可以说是我2015甚至是活到现在一直做不好的事情，天生不喜欢和别人分享感情，缺乏与他人的互动，也可以说是社交上的一大败笔\n2016：尝试着给他人提供自己的快乐，开心的时候可以适当的分享给他人，可以是愉快一笑，也可是侃侃而谈，但要记住分享要把握尺度，既不要全部敞开，也不要过于封闭自己\n“给予”的规模\n做的挺好的，继续保持\n在破裂之时，才真正开始\n坏的东西修了继续用，我不喜欢这样，坏了就让它过去，除非是珍贵的东西，不然的话，让新的到来算是给自己带来一点喜悦感；即时是人也好，不是特别珍贵的，不希望花太多时间去修复，拆了东墙补西墙\n人机关系的养成\n2015：人之忌，好为人师也。人们在向他人传授独有的知识时，总是特别的兴奋，以至于得意忘形，不知分寸。传道授业，也要人之所愿，有时候自己也会犯这样的错误，遇到自己知道而别人不知道，倾向于得意忘形，给人难堪，别人下不了台阶\n2016：在别人不懂的情况下，不要当面拆穿别人，可以私底下指出，给人留有余地，切勿在公共场合宣扬别人的不知，得一时之快而丢长久之和。\n马卡龙沟通法\n小礼物是好，但不一定所有人都喜欢，偶尔为之甚好，多则不宜\n成全对方——开口的艺术\n2015：在平常的偶遇中，有时候不喜欢直面他人，失去了给他人开口的机会\n2016：眼神坚定点吧，抬起头来微笑着面对对方，兴许对方真的有什么话想对你说\n干脆地道歉\n大多数情况下，错误我都会认错，脸皮厚了好多哈哈，继续保持\n凛然的诚实\n现在慢慢的没有对别人有轻易下结论的习惯，挺好的，继续保持\n留有余地的约定\n2015：没有很好的区分生活的约定与工作的约定，容易过度，也容易节制\n2016：生活的约定，要找到双方的妥协点，给对方留有余地，而不要咄咄逼人；工作的约定，严肃对待，有余地，但是一定要执行\n谎言的尾巴\n平时不喜欢戳破别人的谎言，会去考虑谎言的缘由，做得挺好的，继续保持\n让百步，又何妨\n2015：口水战经常发生，有时能够把控住自己，有时也容易与人争执，而争执的结果其实并没有多大的意义\n2016：遇到口水战的时候，让对方先说完，而不是两方争吵，明白对方错了就让步让口水战消停，因为此时口水战已经无意义了；明白自己错了，主动认输，道歉也不是什么大不了的事\n对闲言碎语深呼吸\n2015：声浪，舆论，谣言，是人都怕，而这些可能产生于陌生人，朋友，甚至亲人，他人怎么想其实并不重要，自己确实挺在意别人的看法的\n2016：做好自己就行了，莫让无心变有心，其实别人并没有那么在意你什么样的人，只不过都是茶余饭后的谈资，10个人中伤你的同时也会10个人在赞赏你，遇到了就深呼吸，冷静下来吧\n一个人的奢侈\n给自己独处的时间，同时也给他人独处的时间，做的不错，继续保持\nCharter 3 圆融人生的香料 # 香味的功效\n爱好一直都有，做的不错，继续保持\n“不知道”的箱子\n2015：不耻下问，有做到，但没有时时记录自己的“为什么”\n2016：不懂得，先记下来（手机，小本子），先自己google，不懂就去请教懂的人\n散发树木想起的地图\n画我家的地图？哦，等我有我的房子在说吧~\n历史上的小事情\n读史可以明志，2015年看了很多历史书籍，也慢慢开始对历史感兴趣了，很遗憾高中很讨厌历史这门课，继续保持\n轻松写信\n有去旅游的话，我会尝试着写明信片的\n阅读旅行\n2015年读了好多书，深感读书之益，会继续保持的\n亲身经历的笔记\n2015：太依赖于周边环境和网络了\n2016：要多出去走走，多尝试做想做的事情，just do it\n轻柔的举止\n2015：有段时间，因为太在意别人口中的不够男人的说法（这与上面的真好呼应啊），而做事大大咧咧，很用力的关门什么的，其实这是很不礼貌的做法\n2016：做事要文雅，而文雅的基础是要心清，人平静了自然做事就不会莽撞\n双臂交抱是大忌\n2015：不同文化不同看法，不过双臂交叉确实会让自己显得难以接近，跷二郎腿显得高傲，这两个动作自己经常做啊！\n2016：尝试着减少这些动作\n爱护你的手脚\n身体发肤，受之父母，不敢不爱护\n生活中的减法\n2015：爱好只增不减，没有在一个爱好上保持太多的精力和时间\n2016：对不怎么喜欢的爱好就放弃吧，先把喜欢的做好，比如学好乐理，练好吉他，再去考虑画画吧\n医治“不够的病”\n不赞同观点，略过\n金钱的种子\n2015：从攒钱到买买买，现在也放开了，多了kindle，显示屏，一堆书，和平板电脑也有了，一堆适合自己的衣服\n2016：花钱还是要适度啊，多把钱投资在自己的价值上\n自己的决算表\n2015：本来一直有记录每月消费的习惯，实习时太忙就忘记了\n2016：小的消费可以汇总，记录自己大的消费\nCharter 4 平和的晚餐 # 用触摸表达珍重\n……与其摸自己的物品，还不如找个女朋友实在点……略过\n孤独是基本条件\n2015：足够孤独，我比作者还有经验\n2016：好好利用独处的时间\n慢慢延烧的暖意\n2015：太渴求付出就要得到回报了，既然付出了，就不要想着回报吧\n2016：依旧要主动向他人付出，回报慢慢会有的，不能着急\n做选择的训练\n2015：选择困难症患者啊，调侃自己是因为是因为金钱问题哈哈，其实确实有选择困难症，不管是物还是事，要知道犹犹豫豫会给别人造成负担\n2016：强迫自己做选择特训吧，多多益善，选择多了，自然就有经验了\n舍弃“苟且”的态度\n2015：心情烦躁的时候，处理事情总是不到位，容易“苟且”了事\n2016：注意评估事情的重要性，如真的需要“苟且”了事的就由它去吧，重要的一定要谨慎对待，寻常小事也做的好\n不把麻烦挂在嘴边\n2015：好像什么事对自己来说都是麻烦，吃个饭麻烦，洗个衣服麻烦，上个街麻烦，其实都不算是麻烦，只不过是懒惰驱使\n2016：觉得事情麻烦就更要去尝试它，解决的麻烦多了，就会觉得小麻烦不再是麻烦，大麻烦也只是小麻烦，多去尝试吧，这也是生活的挑战\n想做的事与应该做的事\n一直有使用愿望清单这类app的习惯，把自己想做的事记到愿望清单，有什么不开心的时候就找愿望清单中的一个想做的事，把它完成了，收获了很大的快乐；应该做的事我会记到日历事件里去，设个提醒，或者写一堆的tip纸，然后逐个打勾完成，2016要继续保持这种好习惯\n自己的用途\n2015：自己能做的事，有时候还是会因为怕麻烦而拒绝，但其实完全可以去做的，结果都是利大于弊\n2016：思考下这件事是否能给自己带来不错的收获，如果是的话就力所能及的做吧，也只不过是举手之劳，同时也要保持努力，时时保养，不要懈怠，不然能做的事只会越来越少\n了解你的构造\n我所能同时结交的朋友有限，所能同时拥有的技能有限，所能同时把控的events有限，认清自己的构造很重要，绝不能当自己是不会损坏的机器，超出自己负荷的要求就要拒绝\n彻底的休息\n闷闷不乐时不会委屈自己，会找个独处的地方，做的不凑，继续保持\n不“顺便”为之\n小恩小惠我不会去要的，取之有节，用之有度，做的不错，继续保持\n愿望这种魔法\n觉得信仰比愿望有意义多了，白日空想家也不错，略过\n纯粹的爱恋\n提倡暧昧来给人带来快乐吗？不认同，略过\n变身空无的练习\n2015：没有冥想的习惯，我还年轻呢，倒是有弹弹吉他，跑跑步的习惯，也是一种放松的方法\n2016：继续保持\n无我的幸福\n略过\n给自己的小结 # 2015好习惯好多，坏习惯也好多，2016又是美好的一年，望自己能够戒骄戒躁，保持虚心。不敢说2016年能够遵守给自己列的这些话，但起码也要心有所想，时时不忘\n","date":"December 19, 2015","externalUrl":null,"permalink":"/blog/archives/2015-12-15-my-2015-and-2016/","section":"Archives","summary":"最近读了一本书，松浦弥太郎的《今天也要用心过生活》，书里的道理总是很简单，印证那句老掉牙了的话：道理我都懂，但依然过不好这一生。突然想到，道理明明很简单，我缺乏的是主观能动性吧。生活本可以常驻新鲜，在看不到的地方保持自己的天真，curiosity，小孩子的天性，人长大后就没了，人也衰老的越来越快，心也就变硬了。\n2015年快过去了，不管过的好不好，2016年终将来临。我想借由这本书的内容审视下2015年的自己，2015年所做得不好的地方要一一对自己指出，然后给2016年的自己好好指点一下迷津，让2016年的我活得更加有意义。\n以下是书的内容和对自己的审视：\nChapter 1 健康的早餐 # 每天的自我改造\n2015：会尝试着学习一些小知识，小技能来丰富自己的生活，但持续性不好，一年下来不到100天\n2016：一天中花时间学习小的爱好，争取做到持续时间大于150天\n“早安”的功效\n2015：经常会回避那些自己不喜欢接近的人（除去一些因为三观不合而不像认识的）\n2016：多尝试主动与他们沟通，减少尴尬\n让自己从容的一小时\n2015：经常给自己独处的空间，这个做得挺好\n2016：可以把这一小时从早起中抽出，继续保持\n发现快乐，用心下功夫\n2015：缺乏在小事中找快乐的能力\n2016：如果感到做这件小事能够开心，那就尽管去做吧\n发现全新自己的捷径\n不认同，略过。\n清洁的自信\n2015：做得挺好的\n2016：继续保持\n起而行的行动家\n2015：经常犹犹豫豫，但事后发现事情明明可以很快就做好决定，而且收效更好\n2016：忌讳犹豫不决，从简单的事情开始做，小事在10分钟之内决定好，大事在1个小时到半天内决定好\n自在的节奏\n2015：对自己的生活节奏和工作节奏把握的不是很好，多半是因为情绪的波动导致自己节奏被打乱，没有注意合作伙伴的节奏，比如有时候容易打扰到别人，没有事先沟通好交流时间\n2016：需要注意调整自己的节奏，同时也要注意合作伙伴的节奏，切勿心急\n用心吃饭\n虽然没有自己亲手去做饭，但一直很用心的吃饭，略过\n优雅的握筷方式\n不认同，略过\n干净的姿态\n2015：作为一个coder，虽然仪表是弄得比较整洁，但是姿态总是不够干净利落\n2016：保持仪表整洁干净的同时，要多健身多锻炼，保持姿态的自然，落落大方，多注意不要弯腰驼背，坐下时不要老是头前倾\n遇见“老师”\n2015：遇到一些行为举止不雅的人，太容易意气用事了\n2016：把那些你所厌恶的人和行为当作你自己的“老师”，有则改之，无则加勉，看不惯的东西，笑笑而过就好了\n让笑容为生活保鲜\n做得挺好的，继续保持\n今天也要用心生活\n2015：每个人都从内心里抗拒新事物的发生，抗拒面对新事物，有时候我会很喜欢去尝试新事物，但有的时候还是太畏畏缩缩，容易因为情绪失控而厌倦面对新事物\n2016：年纪越大，越要有勇气挑战新事物，从小事调整开始，尝试着去学习身边每一个细微的事\nChapter 2 上乘的午餐 # 分享喜悦\n","title":"今天也要用心过生活","type":"archives"},{"content":"废话不多说，直接讲一下常见的单行截断：\n\u0026lt;div class=\u0026#34;single-line\u0026#34;\u0026gt; For a while I think we have got away with it, but then a flare goes up and we are caught suddenly in broad daylight. \u0026lt;/div\u0026gt; .single-line { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; width: 200px;/* 给一个宽度限制就可以出现...了 */ } 这个方法在各大浏览器都适用，然而当遇到需要多行截断时问题就来啦，除了粗暴的用后台来根据指定字数truncate掉，或者粗暴的写个jstruncate掉，其实还可以用纯css的方法来实现：\n\u0026lt;div class=\u0026#34;multiple-lines\u0026#34;\u0026gt; For a while I think we have got away with it, but then a flare goes up and we are caught suddenly in broad daylight. \u0026lt;/div\u0026gt; .multiple-lines { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; width: 200px; /* 一样要给一个宽度 */ } 唯一不足的是，如代码所见，对webkit内核的支持比较好，因此在移动端的兼容性会比较好（移动端大部分浏览器都是webkit内核）\n这么重要的属性估计很快也会得到w3c的支持的！如果有兼容性问题的话，对于这种功能只能是用后台或者js粗暴的解决了。。。\n","date":"November 16, 2015","externalUrl":null,"permalink":"/blog/archives/2015-11-16-pure-css-multiple-lines-truncation/","section":"Archives","summary":"废话不多说，直接讲一下常见的单行截断：\n\u003cdiv class=\"single-line\"\u003e For a while I think we have got away with it, but then a flare goes up and we are caught suddenly in broad daylight. \u003c/div\u003e .single-line { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; width: 200px;/* 给一个宽度限制就可以出现...了 */ } 这个方法在各大浏览器都适用，然而当遇到需要多行截断时问题就来啦，除了粗暴的用后台来根据指定字数truncate掉，或者粗暴的写个jstruncate掉，其实还可以用纯css的方法来实现：\n\u003cdiv class=\"multiple-lines\"\u003e For a while I think we have got away with it, but then a flare goes up and we are caught suddenly in broad daylight. \u003c/div\u003e .multiple-lines { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; width: 200px; /* 一样要给一个宽度 */ } 唯一不足的是，如代码所见，对webkit内核的支持比较好，因此在移动端的兼容性会比较好（移动端大部分浏览器都是webkit内核）\n","title":"CSS多行文字截断（适用移动端）","type":"archives"},{"content":"从后台返回的数据经常是以数组的形式传递过来的，开发中遇到的一个小问题，js在处理数组删除时很灵活，故记笔记。\nfor循环删除数组的某一项，一般的会马上这么写代码：\nfor(var i = 0, len = array.length; i \u0026lt; len; i++){ if (array[i] === number) { array[i].splice(i, 1); } } 但是这样是不行的，因为数组的长度已经改变，这样会造成数组越界，这时可能会想到这样写：\nfor(var i = 0; i \u0026lt; array.length; i++){ if (array[i] === number) { array[i].splice(i, 1); } } 同样也行不通，因为数组的索引变化难以把握\n正确的做法：\nfor(var i = array.length - 1; i \u0026gt;= 0; i--) { if(array[i] === number) { array.splice(i, 1); } } 这样可以保证数组 splice 后，循环还按正常的逻辑运行。\n当然，如果想保证索引不改变的话，可以使用 ES5 的新操作符\nfor(var i = 0, len = array.length; i \u0026lt; len; i++){ if (array[i] === number) { delete array[i]; // 索引逻辑不受影响哦！ // 需要浏览器支持的话，可以尝试把`array[i]`置为null // array[i] = null; } } ","date":"October 7, 2015","externalUrl":null,"permalink":"/blog/archives/2015-10-07-js-for-array-delete-item/","section":"Archives","summary":"从后台返回的数据经常是以数组的形式传递过来的，开发中遇到的一个小问题，js在处理数组删除时很灵活，故记笔记。\nfor循环删除数组的某一项，一般的会马上这么写代码：\nfor(var i = 0, len = array.length; i \u003c len; i++){ if (array[i] === number) { array[i].splice(i, 1); } } 但是这样是不行的，因为数组的长度已经改变，这样会造成数组越界，这时可能会想到这样写：\nfor(var i = 0; i \u003c array.length; i++){ if (array[i] === number) { array[i].splice(i, 1); } } 同样也行不通，因为数组的索引变化难以把握\n正确的做法：\nfor(var i = array.length - 1; i \u003e= 0; i--) { if(array[i] === number) { array.splice(i, 1); } } 这样可以保证数组 splice 后，循环还按正常的逻辑运行。\n当然，如果想保证索引不改变的话，可以使用 ES5 的新操作符\nfor(var i = 0, len = array.length; i \u003c len; i++){ if (array[i] === number) { delete array[i]; // 索引逻辑不受影响哦！ // 需要浏览器支持的话，可以尝试把`array[i]`置为null // array[i] = null; } }","title":"js for循环中删除数组的某一项","type":"archives"},{"content":"开发中经常会碰到的一种情况是：由于页面的内容不够长，无法撑开页面，导致底部的模块下面留下一大块空白，实在不雅，sticky footer 技术解决了这个问题\n1.页面结构：\n\u0026lt;div class=\u0026#34;page-wrap\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;header\u0026#34;\u0026gt;header\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;content\u0026#34;\u0026gt; \u0026lt;button id=\u0026#34;add\u0026#34;\u0026gt;Add Content\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;site-footer\u0026#34;\u0026gt;footer is!\u0026lt;/div\u0026gt; 2.css：因为此处使用到了 after 伪元素，所以也就意味着 IE7 以下是无法使用该方法的\n* { margin: 0; padding: 0; } html, body { height: 100%; /* 注意，这是必须的 */ } .header { height: 100px; background-color: #ffff80; } .page-wrap { min-height: 100%; margin-bottom: -150px; /* 必须与footer高度相等 */ } .page-wrap:after { content: \u0026#34;\u0026#34;; display: block; height: 150px; } .site-footer { height: 150px; } .site-footer { background-color: #0080ff; } 3.原理：\nmargin-bottom 将 footer 向上拉动 :after用于当页面长度超过屏幕时占位把 footer 向下挤 footer 必须位于最外层，这也 DOM 结构有所不规范 ","date":"September 5, 2015","externalUrl":null,"permalink":"/blog/archives/2015-09-5-sticky-footer/","section":"Archives","summary":"开发中经常会碰到的一种情况是：由于页面的内容不够长，无法撑开页面，导致底部的模块下面留下一大块空白，实在不雅，sticky footer 技术解决了这个问题\n1.页面结构：\n\u003cdiv class=\"page-wrap\"\u003e \u003cdiv class=\"header\"\u003eheader\u003c/div\u003e \u003cdiv class=\"content\"\u003e \u003cbutton id=\"add\"\u003eAdd Content\u003c/button\u003e \u003c/div\u003e \u003c/div\u003e \u003cdiv class=\"site-footer\"\u003efooter is!\u003c/div\u003e 2.css：因为此处使用到了 after 伪元素，所以也就意味着 IE7 以下是无法使用该方法的\n* { margin: 0; padding: 0; } html, body { height: 100%; /* 注意，这是必须的 */ } .header { height: 100px; background-color: #ffff80; } .page-wrap { min-height: 100%; margin-bottom: -150px; /* 必须与footer高度相等 */ } .page-wrap:after { content: \"\"; display: block; height: 150px; } .site-footer { height: 150px; } .site-footer { background-color: #0080ff; } 3.原理：\nmargin-bottom 将 footer 向上拉动 :after用于当页面长度超过屏幕时占位把 footer 向下挤 footer 必须位于最外层，这也 DOM 结构有所不规范 ","title":"sticky footer","type":"archives"},{"content":"转眼间，我已经在腾讯实习了两个月了。\n回想起今年4月，BG叫我一起去报腾讯的校招，我当时很犹豫，总觉得自己的能力还不足以去腾讯实习。“去看一看笔试考什么也好啊，就当做是练练胆，为以后做准备嘛。”，当时BG是这样跟我说的，然后我就抱着看笔试题的心态跑去笔试了（汗……）\n4人的小分队跑去腾讯笔试，全部都通过了笔试，其实我笔试写得不怎么好，也不知道是为什么就过了……一面的面试官，也就是我现在的leader，面了有30分钟，全部都问的是前端的知识，可能跟我的简历有关吧（简历上除了前端相关的就没有别的了，还要说一句，简历真的是粗制滥造啊，跟旁边一群简历弄得漂漂亮亮的小伙伴们比起来真是相形见绌啊!=_=）,面完后的第一感觉就是\u0026ndash;结束了。当时还很庆幸有过来面试，leader给了我很多前端学习的建议。然而，过了好几天，居然被通知面试过了，当时没有很开心，反而是更担忧了……\n二面，4人小分队只剩下3个人，雪糕弟掉队了~二面之前，我翻了翻网上别人写的腾讯二面经历，妥妥的技术面，全方位知识各种考验。可是二面的面试官却出乎了我的意料，到了之后，没怎么问技术的问题，只是问了我对前端的认识，我就噼里啪啦的说了一堆。30分钟的面试，剩下的5分钟都是在问我能够实习多久的事，真的吓坏我了！我还是以为结束了，然而，过了好几天，又被通知三面。\n三面只剩下2个人了，就是我和BG。HR面，聊人生，聊思想，聊性格，聊未来……是的，HR面就是在聊天，HR人很好，见我有点紧张，一进来就帮我拿书包和推椅子，是的，我更紧张了~HR在我临走之前还是给了我一些鼓励，让我很惊讶，当时就觉得腾讯的人真的好好，情商都好高。\n剩下的日子，很多小伙伴都是在担忧中度过。对于我，其实挺无所谓的，毕竟面试给我带来了挺多的知识，或许是这种淡然的态度，给我迎来了这次实习的机会。\n很凑巧，最后一轮面试，只有我通过了，每一轮走一个小伙伴，真的让我很不舒服，很希望能更他们一起实习。\n转眼间在腾讯实习了两个月，又到了腾讯校招的时候了，小伙伴们也重新踏上了这条路，有时候会想，要是当时我没有去报名，那今天的我也不会有这么大改变，或许还是默默无为~真的是验了一句话——不逼自己一把，永远不知道自己有多厉害。\n每年母上总会去帮四个孩子求签，而我每年都有四个字——戒骄戒躁，望自己能践行。\n","date":"September 3, 2015","externalUrl":null,"permalink":"/blog/archives/2015-09-3-about-tencent-internship/","section":"Archives","summary":"转眼间，我已经在腾讯实习了两个月了。\n回想起今年4月，BG叫我一起去报腾讯的校招，我当时很犹豫，总觉得自己的能力还不足以去腾讯实习。“去看一看笔试考什么也好啊，就当做是练练胆，为以后做准备嘛。”，当时BG是这样跟我说的，然后我就抱着看笔试题的心态跑去笔试了（汗……）\n4人的小分队跑去腾讯笔试，全部都通过了笔试，其实我笔试写得不怎么好，也不知道是为什么就过了……一面的面试官，也就是我现在的leader，面了有30分钟，全部都问的是前端的知识，可能跟我的简历有关吧（简历上除了前端相关的就没有别的了，还要说一句，简历真的是粗制滥造啊，跟旁边一群简历弄得漂漂亮亮的小伙伴们比起来真是相形见绌啊!=_=）,面完后的第一感觉就是–结束了。当时还很庆幸有过来面试，leader给了我很多前端学习的建议。然而，过了好几天，居然被通知面试过了，当时没有很开心，反而是更担忧了……\n二面，4人小分队只剩下3个人，雪糕弟掉队了~二面之前，我翻了翻网上别人写的腾讯二面经历，妥妥的技术面，全方位知识各种考验。可是二面的面试官却出乎了我的意料，到了之后，没怎么问技术的问题，只是问了我对前端的认识，我就噼里啪啦的说了一堆。30分钟的面试，剩下的5分钟都是在问我能够实习多久的事，真的吓坏我了！我还是以为结束了，然而，过了好几天，又被通知三面。\n三面只剩下2个人了，就是我和BG。HR面，聊人生，聊思想，聊性格，聊未来……是的，HR面就是在聊天，HR人很好，见我有点紧张，一进来就帮我拿书包和推椅子，是的，我更紧张了~HR在我临走之前还是给了我一些鼓励，让我很惊讶，当时就觉得腾讯的人真的好好，情商都好高。\n剩下的日子，很多小伙伴都是在担忧中度过。对于我，其实挺无所谓的，毕竟面试给我带来了挺多的知识，或许是这种淡然的态度，给我迎来了这次实习的机会。\n很凑巧，最后一轮面试，只有我通过了，每一轮走一个小伙伴，真的让我很不舒服，很希望能更他们一起实习。\n转眼间在腾讯实习了两个月，又到了腾讯校招的时候了，小伙伴们也重新踏上了这条路，有时候会想，要是当时我没有去报名，那今天的我也不会有这么大改变，或许还是默默无为~真的是验了一句话——不逼自己一把，永远不知道自己有多厉害。\n每年母上总会去帮四个孩子求签，而我每年都有四个字——戒骄戒躁，望自己能践行。\n","title":"记腾讯面试","type":"archives"},{"content":"","date":"June 26, 2015","externalUrl":null,"permalink":"/blog/archives/2015-06-26-javascript-design-patterns-note/","section":"Archives","summary":"","title":"百度ife-javascript设计模式学习笔记","type":"archives"},{"content":" constructor 是什么 # 当我们创建一个构造函数时，就会创建一个 constructor 的属性\nfunction Foo() { this.name = \u0026#34;Jimmy\u0026#34;; } // 实例化对象 var foo = new Foo(); console.log(Foo.constructor); // Function() console.log(Foo.prototype.constructor === Foo); // true console.log(foo.constructor); // Foo() console.log(foo.constructor === Foo); // true 由于 Foo 本身是由 Function 创建的，所以 Foo 的 constructor 就自然而然的指向了 Function()，而 foo 是由 Foo()创建的，所以 foo 的 constructor 就指向了 Foo，即 constructor 默认指向创建自己的函数。\n要注意的是，Foo.prototype.constructor 也指向 Foo，其实就是一个循环引用,Foo 的 prototype 属性指向 Foo 的原型，然后 Foo 的原型的 constructor 属性指向 Foo\n即：Foo.prototype -\u0026gt; Foo 原型，Foo 原型的 constructor -\u0026gt; Foo\nconstructor 在使用中应该注意的 # 要知道 constructor 一直都是指向创建当前对象的构造函数的，但是，在以下代码中，constructor 被修改了，而可能编码的人根本不知道~\nfunction Person(name) { this.name = name; } console.log(Person.prototype.constructor); // Person var p1 = new Person(\u0026#34;Jimmy\u0026#34;); // Person console.log(p1.constructor); // Object // 像这样，其实重新定义了prototype Person.prototype = { sayName: function () { console.log(this.name); }, }; console.log(Person.prototype.constructor); // Object var p2 = new Person(\u0026#34;Jimmy\u0026#34;); console.log(p2.constructor); // Object console.log(p2.constructor === Object); // true console.log(Person.prototype.constructor === Object); // true console.log(p2.constructor.prototype.constructor === Object); // true 说好的 p2 是 Person 创建的，可是此时 p2 却指向了 Object，其实是因为这一行的问题：\nPerson.prototype = { ... }; 这等价于：\nPerson.prototype = new Object({ ... }); 此时，Person.prototype 变成了由 Object 构造的，而 Person.prototype.constructor 也指向了创建自己的对象，即 Object。这时候就肯定不对头了~\n修正的方法也很简单，就是让 Person.prototype.constructor 重新指向 Person:\nfunction Person(name) { this.name = name; } Person.prototype = { sayName: function () { console.log(this.name); }, }; Person.prototype.constructor = Person; var p = new Person(\u0026#34;Jimmy\u0026#34;); console.log(p.constructor === Person); // true console.log(Person.prototype.constructor === Person); // true console.log(p.constructor.prototype.constructor === Person); // true 这下也就没问题了吧~\n不仅在创建对象的时候要注意，在做继承(这里的继承方式不是最好的，只是为了示例)的时候也要注意：\nfunction Person(name) { this.name = name; } // 这种并不会重写constructor Person.prototype.sayName = function () { console.log(this.name); }; // 新建一个子类 function Student(job) { Person.call(this, name); this.job = job; } // 原型链继承，注意此时prototype也被修改了， // constructor也难幸免 Student.prototype = Person.prototype; console.log(Student.prototype.constructor === Student); // false console.log(Student.prototype.constructor === Person); // true // 赶快修正回来！ Student.prototype.constructor = Student; console.log(Student.prototype.constructor === Student); // true console.log(Student.prototype.constructor === Person); // false var s = new Student(\u0026#34;Jimmy\u0026#34;, \u0026#34;student\u0026#34;); s.sayName(); // Jimmy 总结：在进行面向对象编程时，要及时修正 constructor 的指向，防止混乱\n","date":"June 25, 2015","externalUrl":null,"permalink":"/blog/archives/2015-06-24-javascript-constructor-note/","section":"Archives","summary":"constructor 是什么 # 当我们创建一个构造函数时，就会创建一个 constructor 的属性\nfunction Foo() { this.name = \"Jimmy\"; } // 实例化对象 var foo = new Foo(); console.log(Foo.constructor); // Function() console.log(Foo.prototype.constructor === Foo); // true console.log(foo.constructor); // Foo() console.log(foo.constructor === Foo); // true 由于 Foo 本身是由 Function 创建的，所以 Foo 的 constructor 就自然而然的指向了 Function()，而 foo 是由 Foo()创建的，所以 foo 的 constructor 就指向了 Foo，即 constructor 默认指向创建自己的函数。\n要注意的是，Foo.prototype.constructor 也指向 Foo，其实就是一个循环引用,Foo 的 prototype 属性指向 Foo 的原型，然后 Foo 的原型的 constructor 属性指向 Foo\n即：Foo.prototype -\u003e Foo 原型，Foo 原型的 constructor -\u003e Foo\n","title":"百度ife-javascript构造函数学习笔记","type":"archives"},{"content":"javascript 不具有其他语言的类，继承等的特性，因此 javascript 的面向对象编程更多的是基于构造函数和原型的方式实现类的功能；基于原型链来实现类的继承；基于闭包的原理来实现私有化；\n当然也有非原型链的继承，如 jquery 中使用的对象拓展(extend)，通过对对象的 深度复制来实现。\njavascript 面向对象编程 # javascript 本身不具有类的特征，那如何模拟类的特征呢？\n一些好的尝试，但不够完美 # 在此之前，有工厂模式，有构造函数模式，但都存在着一些大的问题：\n工厂模式的问题：无法识别对象是由谁创建的 构造函数的问题：在创建每个实例时，构造函数给每个实例的新建了方法，即使该方法本身是一模一样的，而面向对象的思想并不希望方法是一样的但是却要重复的创建，这本身浪费内存空间 原型模式 # 为了解决工厂模式和构造函数模式的问题，于是有了原型模式\n把共享的变量和方法都放到原型上，然后让子类的原型与父类的原型建立关系，就实现了原型式继承\n代码如下：\nfunction Person(){} // 定义要共享的方法 Person.prototype.name = \u0026#39;Jimmy\u0026#39;; Person.prototype.sayName = function(){ console.log(this.name); } // 或者另一种写法 Person.prototype = { constructor: Person, // 不要忘记修正constructor指向 name : \u0026#39;Jimmy\u0026#39;, sayName = function() { console.log(this.name); } } 但是原型模式也存在着一些问题：\nfunction Person() {} // 定义要共享的方法 Person.prototype.name = \u0026#34;Jimmy\u0026#34;; Person.prototype.sayName = function () { console.log(this.name); }; // 或者另一种写法 Person.prototype = { constructor: Person, // 不要忘记修正constructor指向 name: \u0026#34;Jimmy\u0026#34;, sayName: function () { console.log(this.name); }, }; var p1 = new Person(); var p2 = new Person(); p1.sayName(); // \u0026#39;Jimmy\u0026#39; p2.sayName(); // \u0026#39;Jimmy\u0026#39; console.log(p1.sayName === p2.sayName); // true p1.job = \u0026#34;student\u0026#34;; console.log(p1.job); // student console.log(p2.job); // undefined 这段代码是没有什么问题的，p1 和 p2 已经分离开了，而且也共享着属性和方法，但是，如果 Person 的属性为对象或者数组呢？\nfunction Person() {} // 定义要共享的方法 Person.prototype.name = \u0026#34;Jimmy\u0026#34;; Person.prototype.hobby = [\u0026#34;basketball\u0026#34;, \u0026#34;running\u0026#34;, \u0026#34;code\u0026#34;]; Person.prototype.sayName = function () { console.log(this.name); }; var p1 = new Person(); var p2 = new Person(); console.log(p2.hobby); // [\u0026#39;basketball\u0026#39;, \u0026#39;running\u0026#39;, \u0026#39;code\u0026#39;] p1.hobby.push(\u0026#34;girl\u0026#34;); console.log(p1.hobby); // [\u0026#34;basketball\u0026#34;, \u0026#34;running\u0026#34;, \u0026#34;code\u0026#34;, \u0026#34;girl\u0026#34;] console.log(p2.hobby); // [\u0026#34;basketball\u0026#34;, \u0026#34;running\u0026#34;, \u0026#34;code\u0026#34;, \u0026#34;girl\u0026#34;] 咦，p2.hobby 并没有\u0026rsquo;girl\u0026rsquo;这个爱好啊，可是 p1 的爱好居然影响了它，这就是原型模式的问题了，原因很简单，p1 和 p2 的__proto__都指向了 Person 的原型，导致在实例上的修改都改变 Person 的原型的方法\nconsole.log(p1.__proto__ === Person.prototype); // true console.log(p2.__proto__ === Person.prototype); // true 还有另一个问题，类的构造函数不是可以传递参数吗，定义在 prototype 上还怎么传参数呢？\n组合使用构造函数和原型 （推荐） # 为了解决上面的问题，将构造函数模式和原型模式做个组合就解决了问题：\nfunction Person(name) { this.name = name; this.hobby = [\u0026#34;basketball\u0026#34;, \u0026#34;running\u0026#34;, \u0026#34;code\u0026#34;]; } Person.prototype = { constructor: Person, sayName: function () { console.log(this.name); }, }; var p1 = new Person(\u0026#34;Jimmy\u0026#34;); var p2 = new Person(\u0026#34;king\u0026#34;); console.log(p1.hobby === p2.hobby); // false 构造函数模式用来定义实例属性，原型模式用来定义定义共享的属性和方法，这下解决了这个问题！\n这是使用最为广泛的创建自定义类型的方法！\n动态原型模式 # 组合使用构造函数和原型，使得与其他语言存在不同的地方，构造函数和原型独立了。如果是 java 代码，它定义是这样的：\nclass Person{ private String name; private int age; public Person(String name, int age){ this.name = name; this.age = age; } public String getName(){ return this.name; } public void setAge(age){ this.age = age; } } 可以看到构造函数和方法都是在 Person 类中定义的，而 javascript 的构造函数和原型则分离开了，而动态原型模式就是解决这一问题的：\nfunction Person(name, age) { this.name = name; this.age = age; if (typeof this.sayName != \u0026#34;function\u0026#34;) { Person.prototype.sayName = function () { alert(this.name); }; } } 通过检测 sayName 函数是否存在，在去定义原型上的函数，使得函数得一统一\n寄生构造函数模式 # 在前面几种模式都不适合时，寄生构造函数是一个不错的选择\n基本思想：创建一个函数 Fn，Fn 的作用仅仅是封装创建对象的代码，然后再返回新创建的对象\nfunction Person(name, age, job) { var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function () { console.log(this.name); }; return o; } var p = new Person(\u0026#34;Jimmy\u0026#34;, 22, \u0026#34;student\u0026#34;); p.sayName(); // \u0026#39;Jimmy\u0026#39; return o其实重写了使用new操作符隐式返回的this，因此重写了调用构造函数时返回的值\n一个很好的例子，拓展一个 Array 的自定义方法，产生一个特殊 Array，而不会修改到原生 Array\nfunction SpecialArray() { var arr = new Array(); arr.push.apply(arr, arguments); arr.toInterestString = function () { return this.join(\u0026#34;^_^\u0026#34;); }; return arr; } var arr = new SpecialArray(\u0026#34;first\u0026#34;, \u0026#34;second\u0026#34;, \u0026#34;third\u0026#34;); console.log(arr.toInterestString()); // first^_^second^_^third 可是存在着构造函数的问题：无法识别对象类型\nconsole.log(arr instanceof Array); // true console.log(arr.constructor); // Array 试图手动修改 constructor 的值也是徒劳的\u0026hellip;\nSpecialArray.prototype.constructor = SpecialArray; var arr = new SpecialArray(\u0026#34;first\u0026#34;, \u0026#34;second\u0026#34;, \u0026#34;third\u0026#34;); console.log(arr instanceof Array); // true console.log(arr.constructor); // Array 继承 # 深度复制实现继承（extend） # 其实就是把父对象的所有属性，全部都拷贝给子对象，然后再在子对象上做拓展，实现继承（不能叫继承吧，我觉得叫对象的拓展比较适合）\n先明白什么是浅复制：\nfunction extend(p) { var o = {}; for (var i in p) { o[i] = p[i]; } return o; } 这样虽然实现了简单的拷贝，但是，这样的拷贝只对基本类型有用，如果p中存在一个属性是数组或者对象，它可能是这样的：\np.obj = { name: { firstName: \u0026#34;Zheng\u0026#34;, secondName: \u0026#34;Jimmy\u0026#34;, }, }; 此时o只是引用了p.obj的地址，而没有复制到里面的firstName的什么鬼……，所以此时要使用的就是深复制啦，而深复制也就是把数组与对象做一个递归复制而已~\n如下：\nfunction deepExtend(p, o) { var o = o || {}; for (var i in p) { if (type p[i] === \u0026#39;object\u0026#39;) { o[i] = (p[i].constructor === Array) ? [] : {}; deepExtend(p[i], o[i]); }else { o[i] = p[i]; } } return o; } 而 jquery 中用的就是这种方法来实现继承的。\n构造函数和原型链继承 # 通过构造函数和原型链可以实现继承，其实 javascript 本身就是使用这种方式来实现继承的，如 Array 继承于 Object\n原型链继承 # 通过让子类的原型指向父类的创建的实例，实现子类共享父类的属性和方法\nfunction SuperType() { this.property = true; } SuperType.prototype.getSuperValue = function () { return this.property; }; function SubType() { this.subProperty = false; } // 子类的原型指向父类原型的引用，实现原型方法的共享 SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function () { return this.subProperty; }; var instance = new SubType(); console.log(instance.getSuperValue()); // true 这样就实现了简单的继承了，但是这样的继承却存在着很大的问题：\n生成的子类实例的 constructor 属性指向了父类（因为子类的原型指向了父类的原型，所以子类的原型上的 constructor 属性就指向了父类的构造函数） 每次继承时，都调用了父类（别忘了父类本质也是函数） 创建子类的实例时，不能向超类型的构造函数传递参数 如果属性中包含一个引用类型，那么子类实例对数组的操作会影响到另一个子类实例，看下面例子： function SuperType() { this.colors = [\u0026#34;red\u0026#34;, \u0026#34;blue\u0026#34;, \u0026#34;green\u0026#34;]; } function SubType() {} // 子类的原型指向父类原型的引用，实现原型方法的共享 SubType.prototype = new SuperType(); var instance1 = new SubType(); instance1.colors.push(\u0026#34;black\u0026#34;); var instance2 = new SubType(); // 另一个子类被影响到了 console.log(instance2.colors); // [\u0026#39;red\u0026#39;, \u0026#39;blue\u0026#39;, \u0026#39;green\u0026#39;, \u0026#39;black\u0026#39;] 为什么会影响到父类呢？因为子类的原型指向了父类的原型的引用，因此子类原型实际上是父类的实例，引用类型实际上只是把地址给了实例，实例之间就会共享所有的引用类型\n借用构造函数 # function SuperType(name) { this.name = name; this.colors = [\u0026#34;red\u0026#34;, \u0026#34;blue\u0026#34;, \u0026#34;green\u0026#34;]; } function SubType() { // 在子类中调用父类的方法，当做是执行函数更好理解 SuperType.call(this, \u0026#34;name\u0026#34;); this.age = 29; } var sub1 = new SubType(); sub1.colors.push(\u0026#34;black\u0026#34;); console.log(sub1.colors); // [\u0026#39;red\u0026#39;, \u0026#39;blue\u0026#39;, \u0026#39;green\u0026#39;, \u0026#39;black\u0026#39;] var sub2 = new SubType(); console.log(sub2.colors); // [\u0026#39;red\u0026#39;, \u0026#39;blue\u0026#39;, \u0026#39;green\u0026#39;] 借用构造函数虽然解决了原型链继承的实例共享和参数传递的问题，但是却出现了新的问题：\n由于只能在构造函数上定义，函数复用失效了 在超类型原型中定义的方法，子类也不能拥有（没有继承原型链） 组合继承 # 那么把原型继承和借用构造函数组合一下各取所长，就有了新的继承方式啦：\nfunction SuperType(name) { this.name = name; this.colors = [\u0026#34;red\u0026#34;, \u0026#34;blue\u0026#34;, \u0026#34;green\u0026#34;]; } SuperType.prototype.sayName = function () { alert(this.name); }; function SubType(name, age) { SuperType.call(this, name); this.age = age; } SubType.prototype = new SuperType(); SubType.prototype.sayAge = function () { alert(this.age); }; var instance1 = new SubType(\u0026#34;Nicholas\u0026#34;, 29); instance1.colors.push(\u0026#34;black\u0026#34;); alert(instance1.colors); //\u0026#34;red,blue,green,black\u0026#34; instance1.sayName(); //\u0026#34;Nicholas\u0026#34;; instance1.sayAge(); //29 var instance2 = new SubType(\u0026#34;Greg\u0026#34;, 27); alert(instance2.colors); //\u0026#34;red,blue,green\u0026#34; instance2.sayName(); //\u0026#34;Greg\u0026#34;; instance2.sayAge(); //27 可是又特么有问题啦！\nconsole.log(instance1.constructor); // SuperType instance1的constructor属性应该是指向创建它的构造函数的，但这里却指向了SuperType\n原型式继承 # 道格拉斯.克罗克福德实现的继承方法，不适用构造函数，而是借助原型可以基于已有的对象创建对象，同时还不必因此创建自定义类型。\nfunction object(o) { function F() {} // 创建临时构造函数 F.prototype = o; // 将传入的对象作为临时函数的原型 return new F(); // 返回临时函数的实例 } var person = { name: \u0026#34;Nicholas\u0026#34;, friends: [\u0026#34;Shelby\u0026#34;, \u0026#34;Court\u0026#34;, \u0026#34;Van\u0026#34;], }; var anotherPerson = object(person); anotherPerson.name = \u0026#34;Greg\u0026#34;; anotherPerson.friends.push(\u0026#34;Rob\u0026#34;); var yetAnotherPerson = object(person); yetAnotherPerson.name = \u0026#34;Linda\u0026#34;; yetAnotherPerson.friends.push(\u0026#34;Barbie\u0026#34;); alert(person.friends); //\u0026#34;Shelby,Court,Van,Rob,Barbie\u0026#34; 本质上是执行了一次浅复制，因此anotherPerson和yetAnotherPerson都共享了person的方法（其实我不知道这种继承的意义是啥……）\nECMAScript 中实现了该方法 Object.create()，Object.create() ，这与原型式继承是一样的\n寄生式继承 # 与原型式继承类似，增强对象，返回新的对象：\nfunction createAnother(original) { var clone = object(original); clone.sayHi = function(){ console.log(\u0026#39;hi\u0026#39;); }; return clone; } var person = { name : \u0026#39;Jimmy\u0026#39;, friend : [\u0026#39;king\u0026#39;, \u0026#39;steve\u0026#39;]; }; var anotherPerson = createAnother(person); anotherPerson.sayHi(); // hi 与构造函数模式相似，使用寄生式继承不能做到函数复用\n寄生式组合继承（大 BOSS，前面的都是铺垫） # 组合继承存在构造函数被多次调用的问题 子类的 constructor 被修改 寄生式组合继承就解决了这两个问题，并结合其他继承的特性：\nfunction object(o) { function F() {} F.prototype = o; return new F(); } // 用中间对象来过渡，避免调用父类的构造函数，浪费资源 // 接收子类和父类的构造函数 function inheritPrototype(subType, superType) { var prototype = object(superType.prototype); // 得到父类对象，存入副本 prototype.constructor = subType; // 修正constructor指向 subType.prototype = prototype; // 子类继承父类 } function SuperType(name) { this.name = name; this.color = [\u0026#34;red\u0026#34;, \u0026#34;blue\u0026#34;, \u0026#34;green\u0026#34;]; } SuperType.prototype.sayName = function () { console.log(this.name); }; function SubType(name, age) { SuperType.call(this, name); this.age = age; } inheritPrototype(SubType, SuperType); SubType.prototype.sayName = function () { console.log(this.age); }; 一切问题都解决了，这就是最理想的继承了，当然，为了避免把变量暴露在全局环境下，最好对寄生式组合继承做一个封装！\n","date":"June 25, 2015","externalUrl":null,"permalink":"/blog/archives/2015-06-24-javascript-oo-note/","section":"Archives","summary":"javascript 不具有其他语言的类，继承等的特性，因此 javascript 的面向对象编程更多的是基于构造函数和原型的方式实现类的功能；基于原型链来实现类的继承；基于闭包的原理来实现私有化；\n当然也有非原型链的继承，如 jquery 中使用的对象拓展(extend)，通过对对象的 深度复制来实现。\njavascript 面向对象编程 # javascript 本身不具有类的特征，那如何模拟类的特征呢？\n一些好的尝试，但不够完美 # 在此之前，有工厂模式，有构造函数模式，但都存在着一些大的问题：\n工厂模式的问题：无法识别对象是由谁创建的 构造函数的问题：在创建每个实例时，构造函数给每个实例的新建了方法，即使该方法本身是一模一样的，而面向对象的思想并不希望方法是一样的但是却要重复的创建，这本身浪费内存空间 原型模式 # 为了解决工厂模式和构造函数模式的问题，于是有了原型模式\n把共享的变量和方法都放到原型上，然后让子类的原型与父类的原型建立关系，就实现了原型式继承\n代码如下：\nfunction Person(){} // 定义要共享的方法 Person.prototype.name = 'Jimmy'; Person.prototype.sayName = function(){ console.log(this.name); } // 或者另一种写法 Person.prototype = { constructor: Person, // 不要忘记修正constructor指向 name : 'Jimmy', sayName = function() { console.log(this.name); } } 但是原型模式也存在着一些问题：\n","title":"百度ife-javascript面向对象学习笔记","type":"archives"},{"content":"学习任务来自：百度ife前端技术学院\n学习资料参考自：\n闭包\n深入理解javascript原型和闭包\n在经过前面作用域和原型、原型链的学习，感觉遇到闭包也不怕啦\u0026hellip; 准备被虐杀！\n先补全一些作用域上的知识：\n自由变量 # 在A作用域中使用的变量x，却没有在A作用域中声明（即在其他作用域中声明的），对于A作用域来说，x就是一个自由变量。\n例如：\nvar x = 10; function fn(){ var b = 20; // x不在fn的作用域中声明，但却调用了 // 因此x叫做自由变量 console.log(b + x); } 一个经常容易犯错的例子：\nvar x = 10; function fn() { console.log(x); } function show(f) { var x = 20; fn(); } show(); // 10 这个例子中，输出的不是20，而是10，因为在函数定义的时候就已经决定了函数的上下文执行环境，故，fn的作用域链上，只能找到window执行环境下的变量x\n另一个例子：\nvar x = 10; function fn(){ console.log(x); } function show(f){ var x = 20; (function(){ f(); })() } show(fn); // 10 很抱歉，还是10，原理同上~\n闭包 # 闭包就是能够读取其他函数内部变量的函数。可以简单的理解成“定义在一个函数内部的函数”，在本质上，闭包就是将函数内部和函数外部连接起来的一座桥梁。\n闭包应用的两种情况 # 函数作为返回值 # function fn(){ var max = 10; return function bar(x) { if (x \u0026gt; max) { console.log(x); } }; } var f1 = fn(); f1(15); bar函数作为返回值，赋值给f1变量，执行f1(15)时，用到了fn作用域下的max变量的值（此时fn在执行后作用域应该是销毁了，但是由于闭包的作用，变量max依旧存在）\n注意：内部函数定义在引用的变量之前，内部函数依旧可以访问闭包中的变量\nfunction sayAlice() { var sayAlert = function() { alert(alice); } // Local variable that ends up within closure var alice = \u0026#39;Hello Alice\u0026#39;; return sayAlert; } var helloAlice=sayAlice(); helloAlice(); // Hello Alice 函数作为参数被传递 # var max = 10, fn = function(x) { if (x \u0026gt; max) { console.log(x); } }; // 模拟块级域 // f(15)成功访问到了max = 10 (function(f)) { var max = 100; f(15); })(fn); fn函数作为一个参数被传递到另一个函数，赋值给f参数，执行f(15)，f的作用域中，max = 10，而不是100；\n闭包原理 # 创建函数A本身也就创建了一个新的，独立的作用域A-context，而由于执行A函数时，由于正好要引用到A函数上下文环境中存在的一个变量（或者几个变量）(该变量恰好存在于包裹该A函数的B函数的作用域中)，因此在销毁B函数后，因为该独立的A函数被返回的同时需要B函数的变量，所以该变量不会销毁，而接收该返回的A函数就是闭包\n闭包的问题 # 因为存在于闭包中的变量不会销毁，一直保存在内存中，因此在运行程序时就 增加了内容开销，在IE中可能导致内存泄露。 解决方法是：在退出函数之前，将不使用的局部变量全部删除（尽管javascript中有垃圾回收机制）\n在用闭包模拟javascript中所不含有的私有变量和共有变量时，把闭包当做公有方法要切记不要用闭包修改了私有变量\n在循环中引用闭包也容易出现问题\n错误的代码：\nfunction showHelp(help) { document.getElementById(\u0026#39;help\u0026#39;).innerHTML = help; } function setupHelp() { var helpText = [ {\u0026#39;id\u0026#39;: \u0026#39;email\u0026#39;, \u0026#39;help\u0026#39;: \u0026#39;Your e-mail address\u0026#39;}, {\u0026#39;id\u0026#39;: \u0026#39;name\u0026#39;, \u0026#39;help\u0026#39;: \u0026#39;Your full name\u0026#39;}, {\u0026#39;id\u0026#39;: \u0026#39;age\u0026#39;, \u0026#39;help\u0026#39;: \u0026#39;Your age (you must be over 16)\u0026#39;} ]; for (var i = 0; i \u0026lt; helpText.length; i++) { var item = helpText[i]; document.getElementById(item.id).onfocus = function() { showHelp(item.help); } } } setupHelp(); 我们希望的理想结果是指定哪个显示哪个，可是事实不是这样的，该问题的原因在于赋给 onfocus 是闭包（showHelp）中的匿名函数而不是闭包对象；在闭包（showHelp）中一共创建了三个匿名函数，但是它们都共享同一个环境（item）。在 onfocus 的回调被执行时，循环早已经完成，且此时 item 变量（由所有三个闭包所共享）已经指向了 helpText 列表中的最后一项。\n正确的做法是使onfocus指向一个新的闭包对象：\nYour age (you must be over 16) function showHelp(help) { document.getElementById(\u0026#39;help\u0026#39;).innerHTML = help; } function makeHelpCallback(help) { return function() { showHelp(help); }; } function setupHelp() { var helpText = [ {\u0026#39;id\u0026#39;: \u0026#39;email\u0026#39;, \u0026#39;help\u0026#39;: \u0026#39;Your e-mail address\u0026#39;}, {\u0026#39;id\u0026#39;: \u0026#39;name\u0026#39;, \u0026#39;help\u0026#39;: \u0026#39;Your full name\u0026#39;}, {\u0026#39;id\u0026#39;: \u0026#39;age\u0026#39;, \u0026#39;help\u0026#39;: \u0026#39;Your age (you must be over 16)\u0026#39;} ]; for (var i = 0; i \u0026lt; helpText.length; i++) { var item = helpText[i]; document.getElementById(item.id).onfocus = makeHelpCallback(item.help); } } setupHelp(); 设计模式中的单例模式 # 单例模式singleton就是闭包的典型应用\nvar singleton = function () { var privateVariable; function privateFunction(x) { ...privateVariable... } return { firstMethod: function (a, b) { ...privateVariable... }, secondMethod: function (c) { ...privateFunction()... } }; }(); 此时， privateVariable和privateVariable都是私有变量，通过闭包完成了私有的成员和方法的封装。只返回了两个接口~\n","date":"June 23, 2015","externalUrl":null,"permalink":"/blog/archives/2015-06-22-javascript-closure-note/","section":"Archives","summary":"学习任务来自：百度ife前端技术学院\n学习资料参考自：\n闭包\n深入理解javascript原型和闭包\n在经过前面作用域和原型、原型链的学习，感觉遇到闭包也不怕啦… 准备被虐杀！\n先补全一些作用域上的知识：\n自由变量 # 在A作用域中使用的变量x，却没有在A作用域中声明（即在其他作用域中声明的），对于A作用域来说，x就是一个自由变量。\n例如：\nvar x = 10; function fn(){ var b = 20; // x不在fn的作用域中声明，但却调用了 // 因此x叫做自由变量 console.log(b + x); } 一个经常容易犯错的例子：\nvar x = 10; function fn() { console.log(x); } function show(f) { var x = 20; fn(); } show(); // 10 这个例子中，输出的不是20，而是10，因为在函数定义的时候就已经决定了函数的上下文执行环境，故，fn的作用域链上，只能找到window执行环境下的变量x\n","title":"百度ife-javascript闭包学习笔记","type":"archives"},{"content":"学习任务来自：百度 ife 前端技术学院\n学习资料参考自：\nJavaScript 探秘：强大的原型和原型链\n理解 JavaScript 原型\n深入理解 javascript 原型和闭包\n个人注：主要是区分Prototype,prototype,__proto__,[[Prototype]]，理解constructor\nPrototype：原型本尊 prototype：原型属性，指向Person，实例中不存在 __proto__：原型访问器，指向 创造该对象 的原型 [[Prototype]]：等同于__proto__ constructor：构造函数，指向函数本身（如 new Person)，constructor.prototype也指向Prototype javascript 原型和原型链 # 对象中的proto属性（隐式原型，javascript 并不希望我们访问到该属性） # 每当创建一个对象，该对象就会有一个__proto__属性，该属性指向创建该对象的函数的原型\n如下代码中：\nvar foo = { x: 10, y: 20, }; 当我们创建了一个 foo 对象，foo 对象的__proto__属性就会指向 foo 的原型Prototype；同时 foo 的Prototype也是一个对象，它也有__proto__，指向Object.prototype；在同时，Object.prototype的__proto__指向null。因此，可以说是个对象就有__proto__属性！\n而且，对象是沿着__proto__这条原型链来走的！！！\nconstructor 属性 # function Foo(y) { this.y = y; } Foo.prototype.x = 10; Foo.prototype.calculate = function (z) { return this.x + this.y + z; }; var b = new Foo(20); alert(b.calculate(30)); 此时，Foo.prototype 有一个constructor属性，指向 Foo 对象\n原型 # 原型是一个对象，其他对象可以通过它实现属性继承。 任何一个对象都可以成为原型 所有的对象在默认的情况下都有一个原型，因为原型本身也是对象，所以每个原型自身又有一个原型(只有一种例外，默认的对象原型在原型链的顶端。 一个对象的真正原型是被对象内部的[[Prototype]]属性(property)所持有。ECMA 引入了标准对象原型访问器 Object.getPrototype(object)，到目前为止只有 Firefox 和 chrome 实现了此访问器。除了 IE，其他的浏览器支持非标准的访问器__proto__，如果这两者都不起作用的，我们需要从对象的构造函数中找到的它原型属性。下面的代码展示了获取对象原型的方法：\nvar a = {}; //Firefox 3.6 and Chrome 5 Object.getPrototypeOf(a); //[object Object] //Firefox 3.6, Chrome 5 and Safari 4 a.__proto__; //[object Object] //all browsers a.constructor.prototype; //[object Object] 因此，不难理解，__proto__ 就是指向对象的原型，而不支持__proto__访问器的，constructor 的原型指向对象的原型\n当试图用基本类型访问原型时，内部发生了强制转换：\n//(works in IE too, but only by accident) string.__proto__ === String().__proto__; //true 因此，基本类型没有原型的说法是正确的\ninstanceof 的工作原理 # 如下代码中：\nfunction Foo() {} var f1 = new Foo(); console.log(f1 instanceof Foo); // true console.log(f1 instanceof Object); //true 在使用 instanceof 时，其内部的工作时这样的：\n沿着f1.__proto__这条链向上找 沿着Foo.prototype这条链向上找 当两条线找到同一个引用，返回 true，如果到终点还未重合，sorry，返回 false hasOwnProperty 的由来 # var Person = function () { this.name = \u0026#34;Jimmy\u0026#34;; }; Person.prototype.sayName = function () { alert(this.name); }; var person = new Person(); person.sayHello = function () { alert(\u0026#34;hello\u0026#34;); }; var item; for (item in person) { // 如果不加 hasOwnProperty，会遍历出prototype上的sayName if (person.hasOwnProperty(item)) { console.log(item); } } 如上所述的，person里本来是没有 hasOwnProperty 属性的，它其实是由 Object.prototype 继承而来的!\n对象沿着__proto__这条原型链来查找！！！\nperson的__proto__属性指向Person的原型，而Person的原型的__proto__属性又指向创建它的Object的原型，而Object的原型上就有这个方法~\n原型继承 # 原型继承的好处 # 如果仅仅只是因为一个实例而使用原型是没有多大意义的，这和直接添加属性到这个实例是一样的。\n原型真正魅力体现在多个实例共用一个通用原型的时候。原型对象(注:也就是某个对象的原型所引用的对象)的属性一旦定义，就可以被多个引用它的实例所继承(注:即这些实例对象的原型所指向的就是这个原型对象)，这种操作在性能和维护方面其意义是不言自明的。\n构造函数 # 构造函数提供了一种方便的跨浏览器机制，这种机制 允许在创建实例时为实例提供一个通用的原型\njavascript 中构造函数(constructor)也是一个函数，因此也是一个对象，自然就有了原型属性(与原型区分开来)。\n//function will never be a constructor but it has a prototype property anyway Math.max.prototype; //[object Object] //function intended to be a constructor has a prototype too var A = function (name) { this.name = name; }; A.prototype; //[object Object] //Math is not a function so no prototype property Math.prototype; //null 原型属性只存在于函数上，而不存在与实例上：\nvar A = function () { this.name = \u0026#34;Jimmy\u0026#34;; }; console.log(A.prototype); // A var a = new A(); console.log(a.prototype); // undefined 函数 A 的原型属性(prototype property)是一个对象，当这个函数被用作构造函数来创建实例时，该函数的 原型属性 将被作为 原型 赋值给 所有对象实例 (注:即所有实例的原型引用的是函数的原型属性)\n注意：一个函数的原型属性(function’s prototype property)其实和实际的原型(prototype)没有关系\n//(example fails in IE) var A = function (name) { this.name = name; }; A.prototype == A.__proto__; //false A.__proto__ == Function.prototype; //true - A\u0026#39;s prototype is set to its constructor\u0026#39;s prototype property 如果我现在替换 A 的原型属性为一个新的对象，实例对象的原型a.__proto__却仍然引用着原来它被创建时 A 的原型属性\nvar A = function (name) { this.name = name; }; var a = new A(\u0026#34;alpha\u0026#34;); a.name; //\u0026#39;alpha\u0026#39; A.prototype = { x: 23 }; a.x; //null 如果在实例被创建之后，改变了函数的原型属性所指向的对象，也就是改变了创建实例时实例原型所指向的对象\n拓展已有的对象 # 拓展已有对象的方式本身是不推荐的，如果真的需要对内置对象的原型进行拓展，检测该属性是否存在应该是代码第一件要做的事情！\n// 特性检测 if (String.prototype.timers) { String.prototype.times = function (count) { return count \u0026lt; 1 ? \u0026#34;\u0026#34; : new Array(count + 1).join(this); }; } \u0026#34;hello!\u0026#34;.times(3); // \u0026#34;hello!hello!hello!\u0026#34;; \u0026#34;please...\u0026#34;.times(6); // \u0026#34;please...please...please...please...please...please...\u0026#34; 原型通过原型链来继承 # 原型的继承机制是发生在内部且是隐式的.当想要获得一个对象 a 的属性 foo 的值，javascript 会在原型链中查找 foo 的存在，如果找到则返回 foo 的值，否则 undefined 被返回。\n","date":"June 23, 2015","externalUrl":null,"permalink":"/blog/archives/2015-06-20-javascript-prototype-note/","section":"Archives","summary":"学习任务来自：百度 ife 前端技术学院\n学习资料参考自：\nJavaScript 探秘：强大的原型和原型链\n理解 JavaScript 原型\n深入理解 javascript 原型和闭包\n个人注：主要是区分Prototype,prototype,__proto__,[[Prototype]]，理解constructor\nPrototype：原型本尊 prototype：原型属性，指向Person，实例中不存在 __proto__：原型访问器，指向 创造该对象 的原型 [[Prototype]]：等同于__proto__ constructor：构造函数，指向函数本身（如 new Person)，constructor.prototype也指向Prototype javascript 原型和原型链 # 对象中的proto属性（隐式原型，javascript 并不希望我们访问到该属性） # 每当创建一个对象，该对象就会有一个__proto__属性，该属性指向创建该对象的函数的原型\n","title":"百度ife-javascript原型学习笔记","type":"archives"},{"content":"学习任务来自：百度ife前端技术学院\n学习资料参考自：\n28 May 09 Javascript作用域原理\nJavaScript 开发进阶：理解 JavaScript 作用域和作用域链\njavascript的作用域链 # javascript中的函数运行在她们被定义的作用域里，而不是它们被执行的作用域里 \u0026ndash; 《javascript权威指南》\n注意：在JS中，作用域的概念和其他语言差不多， 在每次调用一个函数的时候 ，就会进入一个函数内的作用域，当从函数返回以后，就返回调用前的作用域.\njavascript中作用域的实现方式与C/C++不同，不是使用“堆栈”的方式，而是使用列表，实现过程如下（ECMA262）：\njavascript高级程序设计3中，指出函数的执行环境是保存在环境栈中的，执行完函数后，会把函数环境弹出栈\n……为何不一样的说法啊（高程中没有探讨到[[scope]]上）\n作用域链（scope chain）：保证对执行环境有权访问的所有变量和函数的有序访问。\n任何执行上下文时刻（context）的作用域，都是由作用域链（scope chain）来实现的。 在一个函数被定义的时候，会将它定义时刻的scope chain链接到这个函数对象的[[scope]]属性。 在一个函数对象被调用的时候，会创建一个活动对象（object），然后对每一个函数的形参（arguments），都命名为该活动对象的命名属性，然后将这个活动对象做为此时的作用域链（scope chain）最前端，并将这个函数对象的[[scope]]加入到scope chain中。 解析这个例子：\nvar func = function(lps, rps){ var name = \u0026#39;laruence\u0026#39;; ........ } func(); 它的解析过程是这样的：\n定义name 定义func时： func的[[scope]]创建，[[scope]]指向window对象 [[scope]]链接到scope chain上 执行func时： 创建活动对象funcObj（预编译时创建，假设是这个名字） 创建arguments对象，添加lps和rps属性 定义局部变量name=\u0026lsquo;laruence\u0026rsquo; 定义其他\u0026hellip; 运行函数，发生标识符解析时，逆向（从作用域链最前端）开始查找funcObj的每一个属性，找到返回，找不到，返回undefined 下一个例子：\nvar name = \u0026#39;laruence\u0026#39;; function echo() { alert(name); } function env() { var name = \u0026#39;eve\u0026#39;; echo(); } env(); 运行结果：\nlaruence 解析过程是这样的：\n定义name 定义echo时： echo的[[scope]]创建，[[scope]]指向window对象 [[scope]]链接到scope chain上 定义env时： env的[[scope]]创建，[[scope]]指向window对象 [[scope]]链接到scope chain上，在echo的[[scope]]后面 执行env时： 创建活动对象envObj（预编译时创建，假设是这个名字） 创建arguments对象 定义局部变量name=\u0026lsquo;eve\u0026rsquo; 执行echo函数，从echo的[[scope]]开始逆向查找作用域链，找到定义在window上的name，返回laruence，结束查找 结束！ 作用域链如该图：\n第三个例子：\nfunction factory() { var name = \u0026#39;laruence\u0026#39;; var intro = function(){ alert(\u0026#39;I am \u0026#39; + name); } return intro; } function app(para){ var name = para; var func = factory(); func(); } app(\u0026#39;eve\u0026#39;); 结果为：\nI am laruence 在刚进入app函数体时, app的活动对象有一个arguments属性, 俩个值为undefined的属性: name和func. 和一个值为\u0026rsquo;eve\u0026rsquo;的属性para;\n此时的scope chain如下:\n[[scope chain]] = [ { para : \u0026#39;eve\u0026#39;, name : undefined, func : undefined, arguments : [] }, { window call object } ] 当调用进入factory的函数体的时候, 此时的factory的scope chain为:\n[[scope chain]] = [ { name : undefined, intor : undefined }, { window call object } ] 注意到, 此时的作用域链中, 并不包含app的活动对象.\n在定义intro函数的时候, intro函数的[[scope]]为:\n[[scope chain]] = [ { name : \u0026#39;laruence\u0026#39;, intor : undefined }, { window call object } ] 从factory函数返回以后,在app体内调用intro的时候, 发生了标识符解析, 而此时的scope chain是:\n[[scope chain]] = [ { intro call object }, { name : \u0026#39;laruence\u0026#39;, intor : undefined }, { window call object } ] 因为scope chain中,并不包含factory活动对象. 所以, name标识符解析的结果应该是factory活动对象中的name属性, 也就是\u0026rsquo;laruence'.\nJavascript的预编译 # 例子：\nalert(typeof eve); //function function eve() { alert(\u0026#39;I am Laruence\u0026#39;); }; 由于在js中存在着预编译的过程（JS在执行每一段JS代码之前，都会首先处理var关键字和function关键字）。\n所以结果会是function\n在调用函数执行之前，会首先创建一个活动对象，然后搜寻这个函数中的局部变量定义,和函数定义，将变量名和函数名都做为这个活动对象的同名属性，对于局部变量定义，变量的值会在真正执行的时候才计算，此时只是简单的赋为undefined.\n而对于函数的定义,是一个要注意的地方:\n函数表达式（var声明的function在预编译中跟变量做一样的处理），如下例子，对于函数定义式，会将函数定义提前而函数表达式，会在执行过程中才计算.\nalert(typeof eve); //结果:function alert(typeof walle); //结果:undefined function eve() { //函数定义式 alert(\u0026#39;I am Laruence\u0026#39;); }; var walle = function() { //函数表达式 }; alert(typeof walle); //结果:function 而对于反编译模式创建的变量，如：\nvar name = \u0026#39;laruence\u0026#39;; age = 26; 变量age会被定义在顶级作用域中。\n当然，JS是对每一段的JS代码进行预编译的\n\u0026lt;script\u0026gt; alert(typeof eve); //结果:undefined \u0026lt;/script\u0026gt; \u0026lt;script\u0026gt; function eve() { alert(\u0026#39;I am Laruence\u0026#39;); } \u0026lt;/script\u0026gt; 作用域在实际运用中要注意的： # 在调用DOM节点时，把要使用的全局变量先存储在引用变量中，减少往scope chain最低层window全局变量查找，提高程序的效率（当然，在小型的项目中体现不出来，但是当项目越大时，过多的全局变量的查找无疑会加大程勋运行的开销）。\n减少查找的例子如下：\nfunction showTips(){ document.getElementById(\u0026#39;btn\u0026#39;).onclick = function(){ document.getElementById(\u0026#39;tip\u0026#39;).style.display = \u0026#39;block\u0026#39;; } } 将两次全局查找，优化成：\nfunction showTips(){ var doc = document; doc.getElementById(\u0026#39;btn\u0026#39;).onclick = function(){ doc.getElementById(\u0026#39;tip\u0026#39;).style.display = \u0026#39;block\u0026#39;; } } 在jquery的源码中，开头就把会频繁使用的变量存储起来了：\n(function( window, undefined ) { var // A central reference to the root jQuery(document) rootjQuery, // The deferred used on DOM ready readyList, // Support: IE9 // For `typeof xmlNode.method` instead of `xmlNode.method !== undefined` core_strundefined = typeof undefined, // Use the correct document accordingly with window argument (sandbox) // 看这里！我们都被存起来了~ location = window.location, document = window.document, docElem = document.documentElement, ...... 延长作用域链 # 有两种情况会发生作用域延长的情况\nwith语句 try-catch语句的catch块 注：虽然with语句确实可以延长作用域链，但是在很多书中都明确警告了使用with会带来一些难以预料的问题（至于是什么问题就不细究了，反正就是不要使用with\u0026hellip;）。\n所以，还是不学习with了\u0026hellip;\n关于try-catch语句的catch块：\n当try代码块中发生错误时，执行过程会跳转到catch语句，然后把异常对象推入一个可变对象并置于作用域的头部。在catch代码块内部，函数的所有局部变量将会被放在第二个作用域链对象中。\ntry{ doSomething(); }catch(ex){ alert(ex.message); //作用域链在此处改变 } 一旦catch语句执行完毕，作用域链机会返回到之前的状态。try-catch语句在代码调试和异常处理中非常有用，因此不建议完全避免。你可以通过优化代码来减少catch语句对性能的影响。**一个很好的模式是将错误委托给一个函数处理，**例如：\ntry{ doSomething(); }catch(ex){ handleError(ex); //委托给处理器方法 } 优化后的代码，handleError方法是catch子句中唯一执行的代码。该函数接收异常对象作为参数，这样你可以更加灵活和统一的处理错误。由于只执行一条语句，且没有局部变量的访问，作用域链的临时改变就不会影响代码性能了。\n","date":"June 18, 2015","externalUrl":null,"permalink":"/blog/archives/2015-06-18-javascript-scope-note/","section":"Archives","summary":"学习任务来自：百度ife前端技术学院\n学习资料参考自：\n28 May 09 Javascript作用域原理\nJavaScript 开发进阶：理解 JavaScript 作用域和作用域链\njavascript的作用域链 # javascript中的函数运行在她们被定义的作用域里，而不是它们被执行的作用域里 – 《javascript权威指南》\n注意：在JS中，作用域的概念和其他语言差不多， 在每次调用一个函数的时候 ，就会进入一个函数内的作用域，当从函数返回以后，就返回调用前的作用域.\njavascript中作用域的实现方式与C/C++不同，不是使用“堆栈”的方式，而是使用列表，实现过程如下（ECMA262）：\njavascript高级程序设计3中，指出函数的执行环境是保存在环境栈中的，执行完函数后，会把函数环境弹出栈\n……为何不一样的说法啊（高程中没有探讨到[[scope]]上）\n作用域链（scope chain）：保证对执行环境有权访问的所有变量和函数的有序访问。\n任何执行上下文时刻（context）的作用域，都是由作用域链（scope chain）来实现的。 在一个函数被定义的时候，会将它定义时刻的scope chain链接到这个函数对象的[[scope]]属性。 在一个函数对象被调用的时候，会创建一个活动对象（object），然后对每一个函数的形参（arguments），都命名为该活动对象的命名属性，然后将这个活动对象做为此时的作用域链（scope chain）最前端，并将这个函数对象的[[scope]]加入到scope chain中。 解析这个例子：\nvar func = function(lps, rps){ var name = 'laruence'; ........ } func(); 它的解析过程是这样的：\n","title":"百度ife-javascript作用域学习笔记","type":"archives"},{"content":"学习任务来自：百度ife前端技术学院\n任务二 # trim # 正确的实现：\n问题：应该用字符来代替 ，chrome下\\s可以匹配全角空格，但是考虑兼容的话，需要加上\\uFEFF\\xA0，去掉BOM头和全角空格。\nfunction trim(str) { // 此处 \\\\s 等是为了去除带转义字符的字符串，如 \u0026#39; \\s 19 \u0026#39; var re = new RegExp(\u0026#39;(^[\\\\s\\\\t\\\\xa0\\\\u3000]+)|([\\\\u3000\\\\xa0\\\\s\\\\t]+\\x24)\u0026#39;, \u0026#39;g\u0026#39;); return String.prototype.trim ? str.trim() : str.replace(re, \u0026#39;\u0026#39;); // MDN 的做法 // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/Trim // return String.prototype.trim ? str.trim() : str.replace(/^\\s+|\\s+$/g,\u0026#39;\u0026#39;); } 注意：\n\\xa0 表示不换行的空格 \\u3000 表示中文全角空格 \\x24 表示$ isEmail # 我的实现：\nfunction isEmail(emailStr) { if (typeof emailStr ===\u0026#39;string\u0026#39;) { var re = /^[a-z]([a-z0-9]*[-_]?[a-z0-9]+)*@([a-z0-9]*[-_]?[a-z0-9]+)+[\\.][a-z]{2,3}([\\.][a-z]{2})?$/i; return re.test(emailStr); } } 问题：无法识别lj.meng@s.baidu.com这种类型的邮箱\n更好的实现：\nfunction isEmail(emailStr) { if (typeof emailStr ===\u0026#39;string\u0026#39;) { var re = /^([\\w_\\.\\-\\+])+\\@([\\w\\-]+\\.)+([\\w]{2,10})+$/; return re.test(emailStr); } } 问题：邮箱因为格式太多，并不是单单一个正则表达式就可以搞定的\nisPlain 判断对象是否为字面量对象 # 字面量对象：简单来说，就是该对象由 {} 或者 new Objcet 形式创建\n实现方法：\n/** * 判断一个对象是不是字面量对象，即判断这个对象是不是由{}或者new Object类似方式创建 * * 事实上来说，在Javascript语言中，任何判断都一定会有漏洞，因此本方法只针对一些最常用的情况进行了判断 * * @return {Boolean} */ function isPlain(obj){ var hasOwnProperty = Object.prototype.hasOwnProperty, key; // 排除IE下的COM对象返回Object的问题 // 所有字面量对象都有 isPrototypeOf 这个属性 if (!obj || Object.prototype.toString.call(obj) !== \u0026#39;[object Object]\u0026#39; || !obj.nodeType || !(\u0026#39;isPrototypeOf\u0026#39; in obj) ) { return false; } // 判断new fun()自定义对象的情况 // constructor不是继承自原型链的 // 并且原型中有isPrototypeOf方法才是Object if (obj.constructor \u0026amp;\u0026amp; !hasOwnProperty.call(obj, \u0026#39;constructor\u0026#39;) \u0026amp;\u0026amp; !hasOwnProperty.call(obj.constructor.prototype, \u0026#39;isPrototypeOf\u0026#39;)) { return false; } //判断有继承的情况 //如果有一项是继承过来的，那么一定不是字面量Object //OwnProperty会首先被遍历，为了加速遍历过程，直接看最后一项 for(key in obj){} return key === undefined || hasOwnProperty.call(obj, key); } 任务三 # $.on # $.on = function (element, event, listener) { if (element.nodeType \u0026amp;\u0026amp; event) { if (element.addEventListener) { element.addEventListener(event, listener, false); } else if (element.attachEvent) { element.attachEvent(\u0026#39;on\u0026#39; + event, function() { listener.call(element); }); } // 几乎不存在不支持标准的浏览器了 // else { // element[\u0026#39;on\u0026#39; + event] = listener; // } } } ","date":"June 12, 2015","externalUrl":null,"permalink":"/blog/archives/2015-06-12-ife-task0002-util-001/","section":"Archives","summary":"学习任务来自：百度ife前端技术学院\n任务二 # trim # 正确的实现：\n问题：应该用字符来代替 ，chrome下\\s可以匹配全角空格，但是考虑兼容的话，需要加上\\uFEFF\\xA0，去掉BOM头和全角空格。\nfunction trim(str) { // 此处 \\\\s 等是为了去除带转义字符的字符串，如 ' \\s 19 ' var re = new RegExp('(^[\\\\s\\\\t\\\\xa0\\\\u3000]+)|([\\\\u3000\\\\xa0\\\\s\\\\t]+\\x24)', 'g'); return String.prototype.trim ? str.trim() : str.replace(re, ''); // MDN 的做法 // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/Trim // return String.prototype.trim ? str.trim() : str.replace(/^\\s+|\\s+$/g,''); } 注意：\n\\xa0 表示不换行的空格 \\u3000 表示中文全角空格 \\x24 表示$ isEmail # 我的实现：\n","title":"百度ife-util.js","type":"archives"},{"content":" 等高布局 # 容器高度随着左右两边的增高而变化 通过padding-bottom: 10000px;和margin-bottom: -10000px;互相挤压，外容器隐藏溢出，形成自适应高度 \u0026lt;div class=\u0026#34;wrap\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;left\u0026#34;\u0026gt; \u0026amp;nbsp;页面内容\u0026lt;br/\u0026gt; \u0026amp;nbsp;页面内容\u0026lt;br/\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;center\u0026#34;\u0026gt; \u0026amp;nbsp;页面内容\u0026lt;br/\u0026gt; \u0026amp;nbsp;页面内容\u0026lt;br/\u0026gt; \u0026amp;nbsp;页面内容\u0026lt;br/\u0026gt; \u0026amp;nbsp;页面内容\u0026lt;br/\u0026gt; \u0026amp;nbsp;页面内容\u0026lt;br/\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;right\u0026#34;\u0026gt; \u0026amp;nbsp;页面内容\u0026lt;br/\u0026gt; \u0026amp;nbsp;页面内容\u0026lt;br/\u0026gt; \u0026amp;nbsp;页面内容\u0026lt;br/\u0026gt; \u0026amp;nbsp;页面内容\u0026lt;br/\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; .wrap { width:100%; margin:0 auto; overflow:hidden; } .wrap:after { content:\u0026#34;\u0026#34;; display:block; clear:both; } .left { width:20%; background:Red; float:left; padding-bottom:10000px; margin-bottom:-10000px; } .center { float: left; padding-bottom: 10000px; margin-bottom: -10000px; background-color: yellow; width: 60%; } .right { width:20%; background:blue; float:right; padding-bottom:10000px; margin-bottom:-10000px; } 效果如下：\n注意：如果要支持ie6，请加上针对ie6的清除浮动hack_zoom: 1;\n双飞翼布局 # 布局原理 # 三栏布局 主要内容块在最前面（优先载入），但布局位于中间 两侧固定宽度，分别位于中间主要内容块的两侧，中间流式布局（自动调整宽度） 技术使用：float、margin(负边距)、position(relative) 优缺点 # 优点 # 实现了内容与布局的分离，即Eric提到的Any-Order Columns. main部分是自适应宽度的，很容易在定宽布局和流体布局中切换。 任何一栏都可以是最高栏，不会出问题。 需要的hack非常少（就一个针对ie6的清除浮动hack:_zoom: 1;） 在浏览器上的兼容性非常好，IE5.5以上都支持。 缺点 # main需要添加一个额外的包裹层。 代码实现 # html样式：\n\u0026lt;div class=\u0026#34;container\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;main\u0026#34;\u0026gt;main\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;left\u0026#34;\u0026gt;left\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;right\u0026#34;\u0026gt;right\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; 基本css：呈现三列布局\n.container { overflow: hidden; } .main { float: left; width: 100%; background-color: #c0c0c0; height: 200px; } .left { float: left; width: 200px; background-color: #80ffff; height: 200px; margin-left: -100%;/*注意此时为100%*/ } .right { float: left; width: 150px; background-color: #80ff80; margin-left: -150px; height: 200px; } 处理中间块：解决中间块被掩盖的问题\n\u0026lt;div class=\u0026#34;container\u0026#34;\u0026gt; \u0026lt;!-- 添加额外标签来解决 --\u0026gt; \u0026lt;div class=\u0026#34;main\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;main-content\u0026#34;\u0026gt;#main\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- 添加额外标签来解决 --\u0026gt; \u0026lt;div class=\u0026#34;left\u0026#34;\u0026gt;left\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;right\u0026#34;\u0026gt;right\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; /*加入一条新的属性*/ .main-content { margin: 0 230px 0 190px; } 效果如下：\n双飞翼布局完成啦！\n同时，还可以给该布局加上登高布局的特性，就形成一个流式的布局\n另一种实现方案 （absolute）(比较简单) # \u0026lt;div class=\u0026#34;container\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;main\u0026#34;\u0026gt;main\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;left\u0026#34;\u0026gt;left\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;right\u0026#34;\u0026gt;right\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; .container{ position: relative; } .main { height:600px; background:#f60; margin:0 200px; } .left { width:200px; background:#fc0; height:600px; position:absolute; left:0; top:0; } .right { width:200px; background:#fcc; height:600px; position:absolute; right:0; top:0; } 也可以加入等高布局的特性！\n注意：如果要支持ie6，请加上针对ie6的清除浮动hack_zoom: 1;\n（流式）栅格布局 # 原理 # 根据双飞翼布局实现的多套布局方案 可以根据网站的需求自定义一套自己的布局方案，即支持了流式布局，也有很好的兼容性，ie6只有轻微的hack处理 （hasLayout） 因为原理上大致相同就不做demo了（懒……）\n资料参考 # （转）栅格化系统demo bootstrap的栅格系统 ","date":"April 26, 2015","externalUrl":null,"permalink":"/blog/archives/2015-04-26-about-css-layout/","section":"Archives","summary":"等高布局 # 容器高度随着左右两边的增高而变化 通过padding-bottom: 10000px;和margin-bottom: -10000px;互相挤压，外容器隐藏溢出，形成自适应高度 \u003cdiv class=\"wrap\"\u003e \u003cdiv class=\"left\"\u003e \u0026nbsp;页面内容\u003cbr/\u003e \u0026nbsp;页面内容\u003cbr/\u003e \u003c/div\u003e \u003cdiv class=\"center\"\u003e \u0026nbsp;页面内容\u003cbr/\u003e \u0026nbsp;页面内容\u003cbr/\u003e \u0026nbsp;页面内容\u003cbr/\u003e \u0026nbsp;页面内容\u003cbr/\u003e \u0026nbsp;页面内容\u003cbr/\u003e \u003c/div\u003e \u003cdiv class=\"right\"\u003e \u0026nbsp;页面内容\u003cbr/\u003e \u0026nbsp;页面内容\u003cbr/\u003e \u0026nbsp;页面内容\u003cbr/\u003e \u0026nbsp;页面内容\u003cbr/\u003e \u003c/div\u003e \u003c/div\u003e .wrap { width:100%; margin:0 auto; overflow:hidden; } .wrap:after { content:\"\"; display:block; clear:both; } .left { width:20%; background:Red; float:left; padding-bottom:10000px; margin-bottom:-10000px; } .center { float: left; padding-bottom: 10000px; margin-bottom: -10000px; background-color: yellow; width: 60%; } .right { width:20%; background:blue; float:right; padding-bottom:10000px; margin-bottom:-10000px; } 效果如下：\n注意：如果要支持ie6，请加上针对ie6的清除浮动hack_zoom: 1;\n","title":"css常用布局总结","type":"archives"},{"content":"看完之后很多还是似懂非懂，估计等到有相应的项目做的时候，遇到某些点就会恍然大悟吧！\n规则1——减少HTTP请求（Minimize HTTP Requests） # 只有10%～20%的最终用户响应时间花在接收请求的HTML文档上，剩下的80%～90%时间都花在HTML文档所引用的所有组件（图片、脚本、样式表、Flash等）进行的HTTP请求上。因此，改善响应时间最简单的办法就是减少组件数量并由此减少HTTP请求数。减少组件数量通常会和产品设计的初衷相矛盾，因此，此处给出了一些技术：\n图片地图（Image Maps）联合多个图片到一个单独的图片中。下载图片大小的总和保持不变，但是，通过减少HTTP请求数的方式加速了页面。图片地图适用于导航栏或其他超链接中使用多个图片的情形。但是，在定义图片地图上的区域坐标时，如果采用手工方式很难完成且容易出错，而且除了矩形外几乎无法定义其他形状。 CSS Sprites使用CSS background-image和background-position属性将多个图片联合成一个独立的图片来显示。它通过合并图片减少了HTTP请求，并且比图片地图更加灵活，同时也降低了图片的下载量。如果在页面中需要为背景、按钮、导航栏、链接等提供大量图片，CSS Sprites是一种优秀的解决方案。 内联图片（Inline images）使用data: URL scheme模式将图片嵌入到HTML文档中。通过此模式嵌入图片，不需要任何额外的HTTP请求开销。但是，目前的主流浏览器（主要是IE）不支持此种方式。 合并文件（Combined files）通过将所有JavaScript脚本合并到一个文件，所有CSS样式表合并到另一个文件的方式来减少HTTP请求的数量。但是简单的合并通常会遇到模块化、页面变化等问题，需要根据页面引用脚本和样式表来具体分析以确定具体的组合方式。 规则2——使用内容发布网络(CDN)（Use a Content Delivery Network） # 用户同Web服务器的距离会对页面响应时间产生影响。网站最初通常将其所有服务器放在同一个地方，当用户群增加时，公司必须面对服务器放置地点不再合适的事实。因此，有必要在多个地理位置不同的服务器上部署内容。\n作为实现地理位置分离的第一步，不应当首先尝试使用分布式架构重新设计Web应用程序。这样的应用程序决定了重新设计将带来如同步会话状态、在服务器放置地点间复制数据库事务等复杂问题。重新设计会推迟甚至根本无法实现缩短用户和网站内容距离的愿望。\n如果应用程序Web服务器里用户更近，则一个HTTP请求的响应时间将被缩短；如果组件Web服务器离用户更近，则多个HTTP请求的响应时间将缩短。因此，与其重新开始设计应用程序，以便将应用程序Web服务器分散开，不如首先将组件Web服务器分散开。这不仅能达到响应时间大幅减少的目的，还很容易实现。\n内容发布网络（CDN）是一组分布在多个不同地理位置的Web服务器，用于更加有效的向用户发布内容。向特定用户发布内容的服务器基于对网络可用度的测量，例如，CDN可能选择网络阶跃数最小的服务器，或者具有最短响应时间的服务器。\n除了缩短响应时间外，CDN还可以带来其他优势，包括备份、扩展存储能力和进行缓存；同时，CDN还有助于缓和Web流量峰值的压力，如在获取天气或股市新闻、浏览体育或娱乐事件时。依赖CDN的一个缺点是网站的响应时间会受到其他网站——甚至可能是竞争对手流量的影响；另一个缺点是无法直接控制组件服务器所带来的问题。\nCDN用于发布静态内容（如图片、脚本、样式表、Flash）。提供动态HTML页面会引入特殊的存储要求——数据库连接、状态管理、验证、硬件和OS优化等，这些复杂性超过了CDN的范围。另一方面，静态文件更容易存储并具有较少的依赖。\n规则3——添加Expires头（Add an Expires or a Cache-Control Header） # Web页面包含大量组件，并且数量在不断增长。页面的初访者会进行很多的HTTP请求，但通过一个长久的Expires头，可以使这些组件被缓存下来，可以在后续的页面浏览中避免不必要的HTTP请求。长久的Expires头最长用于图片，但应该将其用于所有组件上，包括脚本、样式表和Flash。\nWeb服务器使用Expires头告诉Web客户端它可以使用一个组件的当前副本，知道指定时间为止。HTTP规范中简要的称该头为“在这一日期/时间之后，响应将被认为是无效的”。例如：\nExpires: Thu, 15 Apr 2010 20:00:00 GMT\n告诉浏览器该响应的有效性持续到2010年4月15日。\n因为Expires头使用一个特定的时间，它要求服务端和客户端的时钟严格同步；另外，过期日期需要经常检查，一旦过期日期到了，需要在服务器中配置提供一个新的日期。所以，HTTP1.1引入了Cache-Control头来克服Exipres头的限制。Cache-Control使用max-age指令指定组件被缓存多久，它以秒为单位定义了一个更新窗。使用带有max-age的Cache-Control可以消除Expires的限制，但对于不支持HTTP1.1的应用，仍希望使用Expires头。可以同时制定这两个响应头，如果两者同时出现时，HTTP规范规定max-age指令将重写Expires头。\n当出现了Expires头时，直到过期时间为止一直会使用缓存的版本，浏览器不会检查任何更新，直到过了过期时间。为了确保用户能够获取组件的最新版本，需要在所有的HTML页面中修改组件的文件名。Yahoo在此使用了将版本号嵌入在组件的文件名中的方法。\n规则4——压缩组件（Gzip Components) # 注意：在HTTP响应头中可以看到返回的内容通过Gzip算法进行了压缩\n压缩组件通过减少HTTP请求产生的响应包的大小，从而降低传输时间的方式来提高性能。从HTTP1.1开始，Web客户端可以通过HTTP请求中的Accept-Encoding头来标识对压缩的支持：\nAccept-Encoding: gzip, deflate\n如果Web服务器看到请求中的这个头，就会使用客户端列出的方法中的一种来压缩响应。Web服务器通过响应中的Content-Encoding头来通知Web客户端：\nContent-Encoding: gzip\n目前许多网站通常会压缩HTML文档，脚本和样式表的压缩也是值得的（包括XML和JSON在内的任何文本响应理论上都值得被压缩）。但是，图片和PDF文件不应该被压缩，因为它们本来已经被压缩了。\n压缩通常能将响应的数据量减少近70%，但是压缩通常情况下会带来服务端和客户端的CPU开销，要检测受益是否大于开销，需要综合考虑响应大小、连接的带宽和客户端也服务端直接的距离等因素。通常需要对大于1KB或2KB的文件进行压缩。\n当浏览器通过代理来发送请求时，有可能出现浏览器期望接受的压缩后内容和实际接收到的不一致的情况。解决这一问题的方法是在Web服务器的响应中添加Vary头。Web服务器可以告诉代理根据一个或多个请求头来改变缓存的响应。由于压缩的决定是基于Accept-Encoding请求头的，因此需要在服务器的Vary响应头中包含Accept-Encoding:\nVary: Accept-Encoding\n目前大约90%的通过浏览器进行的Internet通信都需要使用gzip，使得服务端和客户端的对等性变得额外重要。无论是客户端还是服务端发送错误，都会造成页面被破坏。避免错误的一种方式是采用“浏览器白名单”方式，即只为经过证实支持压缩的浏览器提供压缩内容，但是当代理缓存加进来以后，处理边缘情形浏览器将变得更加复杂。另一种方式是使用Vary: *或Cache-Control: private头来禁用代理缓存。此种方式会为所有浏览器禁用代理缓存，从而增加带宽开销。如何平衡压缩和代理支持需要在加快响应时间、减小带宽开销和边缘情形浏览器缺陷之间进行权衡：\n如果网站的用户很少，并且他们处于一个小圈子中，边缘情形浏览器不需要太多关注，可以压缩内容并使用Vary: Accept-Encoding。\n如果更注重带宽开销，可以和前一种情形一样，压缩内容并使用Vary: Accept-Encoding。\n如果网站拥有大量的、多变的用户群，能够应付较高的带宽开销，并且享有高质量的名声，需要压缩内容并使用Cache-Control: Private。（Google和Yahoo都使用这种方式）\n规则5——将样式表放在顶部（Put Stylesheets at the Top） # 我们都希望页面能够逐步加载，也就是说，我们希望浏览器能够尽快显示内容。当浏览器逐步加载页面时，页头、导航栏、顶端logo等所有这些都会等待页面的用户提供视觉反馈，这改善了用户体验。将样式表放在底部，为避免当样式变化时重绘页面中的元素，浏览器会阻塞内容逐步呈现。\n样式表在页面中的位置并不影响下载时间，但是会影响页面的呈现。根据HTML规范“和A不一样，[LINK]只能出现在文档的HEAD节中，但其出现次数是任意的”。因此，问题的解决方式应该是遵循HTML规范，使用LINK标签将样式表放在文档的HEAD中。\n规则6——将脚本放在底部（Put Scripts at the Bottom） # 对响应时间影响最大的是页面中的组件数量，而脚本会阻塞组件的并行下载，带来性能上的问题。HTTP1.1规范建议浏览器从每个主机名并行下载两个组件。如果一个Web页面平均将其组件分别放在两个主机名下，整体响应时间可以减少大约一半。我们可以通过对浏览器默认设置的修改来增加每个主机名并行下载组件的数量，也可以使用CNAME（DNS别名）将组件分别放到多个主机名下。但是，增加并行下载数量通常会带来性能上的开销，过多的并行下载有时反而会降低性能。Yahoo!研究表明，使用两个主机名比使用1、4或10个主机名能带来更好的性能。\n需要我们注意的是，下载脚本时并行下载实际上是被禁用的，即使此时使用了不同的主机名，浏览器也不会启动其他下载。因此，如果将脚本放在顶部，脚本会阻塞后面内容的呈现，也会阻塞后面组件的下载。因此，放置脚本最好的地方是页面底部，这不会阻止页面内容呈现，而且页面中的可视组件可以尽早下载。\n规则7——避免CSS表达式(Avoid CSS Expressions) # CSS表达式是动态设置CSS属性的一种强大（并且危险）的方式。它从IE5以后的版本被支持，在IE8中已经被废弃。 表达式的问题在于对其进行的求值频率比人们期望的要高。它们不只在呈现页面和大小改变时求值，当页面滚动，甚至用户鼠标在页面上移过时都要进行求值。\n减少CSS表达式求值次数的一种方式是使用一次性表达式，如果CSS表达式必须被求值一次，可以在这一次中执行重写它本身。除此之外，还可以使用事件处理器来为特定的事件提供所期望的动态行为。\n规则8——使用外部JavaScript和CSS（Make JavaScript and CSS External） # 在现实环境中使用外部文件通常会产生较快的页面，因为JavaScript和CSS有机会被浏览器缓存起来。对于内联的情况，由于HTML文档通常不会被配置为可以进行缓存的，所以每次请求HTML文档都要下载JavaScript和CSS。所以，如果JavaScript和CSS在外部文件中，浏览器可以缓存它们，HTML文档的大小会被减少而不必增加HTTP请求数量。\n决定是否使用外部文件的关键在于被缓存的外部文件占请求的HTML文档数的比重。如果网站用户在每次会话中进行多次页面访问，同时页面重用了多个脚本和样式表，使用外部文件时很好的选择。\n对于大多数网站而言，难以精确度量以判断是否使用内联或外部文件，此时建议是使用外部文件的方式。对于这个问题的一个例外是网站主页，由于主页对于响应时间要求更高，因此更加倾向于内联而不是外部文件。\n对于内联文件而言，由于无法利用浏览器缓存，因此给人感觉依然比较低效。我们可以通过加载后下载和动态内联的方式来使得网站主页既可以获得内联的优势，同时也能缓存外部文件。\n规则9——减少DNS查找（Reduce DNS Lookups） # 注意：从输入网址到呈现网页这一过程也涉及到了DNS定向查找ip的问题\nDNS对于网站来说会带来开销。通常浏览器查找一个给定主机名的IP要花费20～120毫秒的时间。在DNS查找完成之前，浏览器不能从此主机下载任何东西。\nDNS查找可以被缓存起来以提高性能，这种缓存可以发生在ISP或局域网中的一台特殊的缓存服务器上，同时，缓存也会发生在独立的用户机器上。在用户请求一个主机名后，DNS信息会留在操作系统的DNS缓存中，大多数浏览器也拥有自己的缓存，和操作系统缓存相分离。只要浏览器在其缓存中保留了DNS记录，就不会通过操作系统来请求这个记录。\n当客户端浏览器和操作系统中DNS缓存同时为空时，DNS查询的数量等于页面中唯一主机名的数量，这些主机名包括了页面的URL、图片、脚本、样式表、Flash等。所以，减少唯一主机名数量，可以减少DNS查询数。\n减少唯一主机名数量会潜在的减少页面中并行下载的数量。避免DNS查找降低了响应时间，但减少并行下载可能会增加响应时间。对于这种情形，建议将这些组件放在至少2个，但不要超过4个主机名下。\n规则10——精简JavaScript和CSS（Minify JavaScript and CSS） # 精简是从代码中移除不必要的字符以减小其大小，进而改善加载时间。在代码被精简后，所有注释以及不必要的空白字符（空格、换行和制表符）都将被移除。对于JavaScript而言，因为需要下载的文件大小减小了，可以改善响应时间。\n混淆是可以应用在源代码上的另一种优化方式。相比较于精简，混淆更加复杂，因此更容易产生bug。混淆可以更大程度上压缩源代码，但是也存在着一定的风险。\n除了外部JavaScript外，内联在\u0026lt;script\u0026gt;和\u0026lt;style\u0026gt;块中的源代码也需要被精简。即使使用了gzip来压缩JavaScript和CSS，使用精简能够将代码大小再减少5%或者更多。\n规则11——避免重定向（Avoid Redirects） # 重定向用于将用户从一个URL路由到另一个URL。重定向有很多种，其中301和302是最常用的两种。下面是一个301响应头的示例：\nHTTP/1.1 301 Moved Permanently\nLocation: http://example.com/newuri\nContent-Type: text/html\n浏览器会自动将用户带到Location字段给出的URL。重定向所必需的所有信息都包含在这个头中，响应体通常是空的。不管叫什么名字，301或者302响应在实际中都不会被缓存，除非有附加的头（如Expires或Cache-Control等）要求它这么做。meta refresh标签和JavaScript也可以用于重定向，但是最好的技术是使用标准的3xx状态码，以保证后退按钮能够正常工作。\n需要我们记住的是重定向会使页面变慢。在用户和HTML文档间插入一个重定向后，在此HTML文档到达之前页面上不会描绘任何东西，任何组件也不会被下载。\n有一种重定向最为浪费，发生的也很频繁，但是Web开发人员通常都没有意识到它，它发生在URL的结尾必须出现斜线（/）而没有出现的情形。例如访问地址http://astrology.yahoo.com/astrology将导致一个301响应包含重定向至http://astrology.yahoo.com/astrology/。当主机名后缺少结尾斜线时不会发生重定向。在Apache中，我们可以通过Alias指令或者mod_rewrite模块或者DirectorySlash指令来处理缺少结尾斜线时的重定向问题。\n从一个旧的站点链接到新的站点是使用重定向的另一种常见场景。其他形式还包括将一个网站的不同部分连接起来，以及基于一些条件（浏览器类型、用户帐户类型等）来引导用户。使用重定向来连接两个网站很简单而且只需要很少的额外代码。但是，虽然重定向降低了开发的复杂性，也损害了用户体验，通常可以进行其他的选择：如果两个代码的路径在同一台服务器上，可以使用Alias和mod_rewrite；如果域名由于重定向发生改变，可以使用一个CNAME（一条DNS记录，用于创建一个域名指向另一个域名的别名）让两个主机名指向相同的服务器，然后使用Alias和mod_rewrite。\n规则12——移除重复脚本（Remove Duplicate Scripts） # 在一个页面中两次保护同一个JavaScript文件会损伤性能。导致一个脚本重复的因素主要有两个——团队大小和脚本数量。\n当重复脚本的现象发生时，将产生不必要的HTTP请求和浪费执行JavaScript的时间。不必要的HTTP请求会发生在IE中，而不会发生在Firefox中。在IE中，如果脚本被包含两次且没有被缓存，浏览器会在页面加载期间产生两个HTTP请求；即使脚本可以缓存，当用户重新加载页面时也会产生额外的HTTP请求。对JavaScript进行的多余的执行从而浪费时间的现象在IE和Firefox中都存在，与脚本是否被缓存无关。\n避免意外包含同一脚本两次的一种方法是在你的模块系统中实现一个脚本管理模块。包含脚本的典型方式是在HTML页面中使用SCRIPT标签：\n\u0026lt;script type=”text/javascript” src=”menu_1.0.17.js”\u0026gt;\u0026lt;/script\u0026gt;\n另一种选择是在PHP中创建一个函数：\n\u0026lt;?php insertScript(“menu.js”) ?\u0026gt;\n为了防止统一个脚本被重复添加多次，insertScript函数需要添加处理脚本的依赖性和版本的功能。\n规则13——配置Etag（Configure ETags）(好难懂~) # 实体标签（Entity Tag，ETag）是Web服务器和浏览器用于确认缓存组件的有效性的一种机制。ETag在HTTP1.1中引入，用于检测浏览器缓存中的组件与原始服务器上的组件是否匹配。ETag是唯一标识了一个组件的一个特定版本字符串。唯一的约束是该字符串必须用引号引起来。原始服务器使用Etag响应头来指定组件的ETag。\nHTTP/1.1 200 OK\nLast-Modified: Tue, 12 Dec 2006 03:03:59 GMT\nETag: “10c24bc-4ab-457e1c1f”\nContent-Length: 12195\n此后，如果浏览器必须验证一个组件，它会使用If-None-Match头将ETag传回原始服务器。如果ETag是匹配的，就会返回304状态码，在此例中使响应减少12195字节。\nGET /i/yahoo.gif HTTP/1.1\nHost: us.yimg.com\nf-Modified-Since: Tue, 12 Dec 2006 03:03:59 GMT\nIf-None-Match: “10c24bc-4ab-457e1c1f”\nHTTP/1.1 304 Not Modified\nETag的问题在于，通常使用组件的某些属性来构造它，这些属性对于特定的、寄宿了网站的服务器来说是唯一的。当浏览器从一台服务器上获取了原始组件之后又尝试向另一台服务器来验证组件时，ETag是不匹配的。这种情况对于使用服务器集群来处理请求的网站来说是很常见的一种情况。默认情况下，Apache和IIS向ETag中嵌入的数据都会大大降低有效性验证的成功率。\nApache1.3和2.x的ETag格式是inode-size-timestamp。文件系统使用inode来存储诸如文件类型、所有者、组和访问模式等信息。尽管在多台服务器上一个给定的文件可能位于相同的目录、具有相同的文件大小、权限、时间戳等，从一台服务器到另一台服务器，器inode仍然是不同的。 IIS5.0和6.0在ETag上有着类似的问题。IIS上ETag的格式是Filetimestamp:ChangeNumber。ChangeNumber适用于跟踪IIS配置变化的计数器。对于一个网站背后的所有IIS服务器来说，ChangeNumber不大可能相同。\n最终的结果是，对于完全相同的组件，从一台服务器到另一台，Apache和IIS产生的ETag是不会匹配的。如果ETag不匹配，用户就不会按照ETag的设计计划那样接收到更小更快的304响应；相反，它们会收到普通的200响应以及组件的所有数据。如果只在一台服务器上部署网站，这通常不会产生问题；但如果使用了服务器集群，同时使用Apache或者IIS进行默认的ETag配置，用户响应将变慢，服务器负载将变高，将消耗更多的带宽，同时代理缓存的效率也会下降。即使组件具有长久的Expires头，一旦用户单击了Reload或Refresh按钮，依然会产生条件GET请求。\n如果组件必须通过最新修改日期之外的一些东西来进行验证，则ETag是一种强大的方法；如果无须自定义ETag，则最好将其移除。Last-Modified头基于组件的时间戳进行验证，可以提供完全等价的信息，而且移除ETag可以减少响应和后续请求的HTTP头的大小。在Apache中，只要向Apache配置文件中简单地添加下面一行配置就能移除ETag： FileETag none\n规则14——使Ajax可缓存（Make Ajax Cacheable） # Ajax的一个最重要的优点就是向用户提供即时反馈，因为它异步的从后台Web服务器请求信息。但是，使用Ajax并不保证用户不会等到异步的JavaScript和XML返回响应。在很多应用程序中，用户是否需要等待取决于如何使用Ajax。用户是否需要等待的关键因素在于Ajax请求是主动的还是被动的。主动请求是基于用户的当前操作而发起的，被动请求则是为了将来使用而预先发起的。我们需要注意的是，“异步”并没有暗示“实时”。\n为了提升性能，最重要的是优化Ajax响应。而改善这些主动Ajax请求的最重要的方式就是使响应可缓存。如同在“添加Expires头”中讨论的，一些其他规则也适用于Ajax，包括：压缩组件、减少DNS查找、精简JavaScript、避免重定向、配置Etag。\n","date":"April 21, 2015","externalUrl":null,"permalink":"/blog/archives/2015-04-21-yahoo-best-practices-for-speeding-up-web-site-/","section":"Archives","summary":"看完之后很多还是似懂非懂，估计等到有相应的项目做的时候，遇到某些点就会恍然大悟吧！\n规则1——减少HTTP请求（Minimize HTTP Requests） # 只有10%～20%的最终用户响应时间花在接收请求的HTML文档上，剩下的80%～90%时间都花在HTML文档所引用的所有组件（图片、脚本、样式表、Flash等）进行的HTTP请求上。因此，改善响应时间最简单的办法就是减少组件数量并由此减少HTTP请求数。减少组件数量通常会和产品设计的初衷相矛盾，因此，此处给出了一些技术：\n图片地图（Image Maps）联合多个图片到一个单独的图片中。下载图片大小的总和保持不变，但是，通过减少HTTP请求数的方式加速了页面。图片地图适用于导航栏或其他超链接中使用多个图片的情形。但是，在定义图片地图上的区域坐标时，如果采用手工方式很难完成且容易出错，而且除了矩形外几乎无法定义其他形状。 CSS Sprites使用CSS background-image和background-position属性将多个图片联合成一个独立的图片来显示。它通过合并图片减少了HTTP请求，并且比图片地图更加灵活，同时也降低了图片的下载量。如果在页面中需要为背景、按钮、导航栏、链接等提供大量图片，CSS Sprites是一种优秀的解决方案。 内联图片（Inline images）使用data: URL scheme模式将图片嵌入到HTML文档中。通过此模式嵌入图片，不需要任何额外的HTTP请求开销。但是，目前的主流浏览器（主要是IE）不支持此种方式。 合并文件（Combined files）通过将所有JavaScript脚本合并到一个文件，所有CSS样式表合并到另一个文件的方式来减少HTTP请求的数量。但是简单的合并通常会遇到模块化、页面变化等问题，需要根据页面引用脚本和样式表来具体分析以确定具体的组合方式。 规则2——使用内容发布网络(CDN)（Use a Content Delivery Network） # 用户同Web服务器的距离会对页面响应时间产生影响。网站最初通常将其所有服务器放在同一个地方，当用户群增加时，公司必须面对服务器放置地点不再合适的事实。因此，有必要在多个地理位置不同的服务器上部署内容。\n作为实现地理位置分离的第一步，不应当首先尝试使用分布式架构重新设计Web应用程序。这样的应用程序决定了重新设计将带来如同步会话状态、在服务器放置地点间复制数据库事务等复杂问题。重新设计会推迟甚至根本无法实现缩短用户和网站内容距离的愿望。\n如果应用程序Web服务器里用户更近，则一个HTTP请求的响应时间将被缩短；如果组件Web服务器离用户更近，则多个HTTP请求的响应时间将缩短。因此，与其重新开始设计应用程序，以便将应用程序Web服务器分散开，不如首先将组件Web服务器分散开。这不仅能达到响应时间大幅减少的目的，还很容易实现。\n内容发布网络（CDN）是一组分布在多个不同地理位置的Web服务器，用于更加有效的向用户发布内容。向特定用户发布内容的服务器基于对网络可用度的测量，例如，CDN可能选择网络阶跃数最小的服务器，或者具有最短响应时间的服务器。\n除了缩短响应时间外，CDN还可以带来其他优势，包括备份、扩展存储能力和进行缓存；同时，CDN还有助于缓和Web流量峰值的压力，如在获取天气或股市新闻、浏览体育或娱乐事件时。依赖CDN的一个缺点是网站的响应时间会受到其他网站——甚至可能是竞争对手流量的影响；另一个缺点是无法直接控制组件服务器所带来的问题。\nCDN用于发布静态内容（如图片、脚本、样式表、Flash）。提供动态HTML页面会引入特殊的存储要求——数据库连接、状态管理、验证、硬件和OS优化等，这些复杂性超过了CDN的范围。另一方面，静态文件更容易存储并具有较少的依赖。\n规则3——添加Expires头（Add an Expires or a Cache-Control Header） # Web页面包含大量组件，并且数量在不断增长。页面的初访者会进行很多的HTTP请求，但通过一个长久的Expires头，可以使这些组件被缓存下来，可以在后续的页面浏览中避免不必要的HTTP请求。长久的Expires头最长用于图片，但应该将其用于所有组件上，包括脚本、样式表和Flash。\n","title":"Yahoo前端优化性能的十四条规则","type":"archives"},{"content":"1. border-style : dotted的\u0026rsquo;dotted\u0026rsquo;属性不兼容ie6\n2. 不同元素的上下外边距margin会叠压，父子级包含的时候子级的margin-top会传递给父级；（内边距替代外边距）\n3. word-spacing : 30px 单词间距（以空格为解析单位）\n4. 样式优先级 tag(1) \u0026lt; class(10) \u0026lt; id(100) \u0026lt; style行间样式(1000) \u0026lt; js\n5. IE6不支持a以外其它任何标签的伪类；IE6以上的浏览器支持所有标签的hover伪类。\n6. 行内样式 inline 的特点：\n默认同行可以继续跟同类型标签； 内容撑开宽度 不支持宽高 不支持上下的margin和padding 代码换行被解析 7.1 inline-block的特点:\n块在一行显示； 行内属性标签支持宽高； 没有宽度的时候内容撑开宽度 7.2 inline-block存在的问题:\n代码换行被解析；(代码换行的时候样式会有空隙) ie6 ie7 不支持块属性标签的inline-block; 8. img的默认属性为inline-block，img自带边框（经常在css reset中重置边框为 none）\n9. cursor : url(我的鼠标.jpg),pointer;cursor也可以自带图片来模拟鼠标样式，但后面需要附上的默认样式\n10. 谨慎使用通配符*，会影响性能\n11. clear left/right/both/none 是指元素的某个方向不能有浮动元素\n12. 在ie6下高度小于19px的元素，高度会被当做19px来处理,解决办法:给该元素设置overflow:hidden\n13. ie6,7的hasLayout属性使得具有宽度的父级子元素不需要清除浮动\n14. ie6,7下元素浮动，要并在同一行的元素都要加浮动\n15. IE6下的双边距BUG\n在IE6下，块元素有浮动和横向margin的时候，横向的margin值会被放大成两倍 解决办法: display:inline 16. IE6，7下li的间隙\n在IE6，7下li本身没浮动,但是li内容有浮动的时候，li下边就会产生几px的间隙 解决办法: 1.给li加浮动;2.给li加vertical-align:top; 17. 图片下的空隙问题，vertical-align:top解决\n18. position:absolute;绝对定位的注意事项：\n使内嵌支持宽高；(定义了绝对定位后，不用设置display: block) 块属性标签内容撑开宽度 19. ie6不支持固定定位 position: fixed;，固定定位与绝对定位的差别是，固定定位始终相对于整个文档进行定位\n20. ie7以下父级的overflow:hidden;是包不住子级的相对定位的\n21. 在 ** IE6 下** 定位元素的父级宽高都为奇数那么在 IE6 下 定位元素的 right 和 bottom 都有1像素的偏差。\n22. 相对定位和绝对定位也可以用于清除浮动\n23. 英文断句的方式：\nword-break:break-all;所有形式的句子都断句，不考虑单词连贯性 word-break:break-word保持英文单词完整性的断句 24. 文本溢出显示省略号，在相应的显示文本的元素上加上white-space:nowrap; overflow:hidden; text-overflow:ellipsis;样式。\n25. 未知宽高的img如何在容器里水平垂直居中？\n方案一：使用vertical-align属性（多了没用的标签，个人不太推荐使用）\n\u0026lt;!DOCTYPE HTML\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta http-equiv=\u0026#34;Content-Type\u0026#34; content=\u0026#34;text/html; charset=utf-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;无标题文档\u0026lt;/title\u0026gt; \u0026lt;style\u0026gt; .box{ width:800px;height:600px;border:2px solid #000; text-align:center;} span{ display:inline-block; height:100%; vertical-align:middle;} img{ vertical-align:middle;} \u0026lt;/style\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;div class=\u0026#34;box\u0026#34;\u0026gt; \u0026lt;img src=\u0026#34;bigptr.jpg\u0026#34; /\u0026gt;\u0026lt;span\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 方案二：使用表格布局默认居中的特性\n\u0026lt;!DOCTYPE HTML\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta http-equiv=\u0026#34;Content-Type\u0026#34; content=\u0026#34;text/html; charset=utf-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;无标题文档\u0026lt;/title\u0026gt; \u0026lt;style\u0026gt; .box{ width:800px;height:600px;border:2px solid #000;display:table;position:relative; overflow:hidden;} span{ display:table-cell; text-align:center; vertical-align:middle;*position:absolute;left:50%;top:50%;} img{ *position:relative; vertical-align:top;left:-50%;top:-50%;} \u0026lt;/style\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;div class=\u0026#34;box\u0026#34;\u0026gt; \u0026lt;span\u0026gt;\u0026lt;img src=\u0026#34;bigptr.jpg\u0026#34; /\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; ","date":"April 6, 2015","externalUrl":null,"permalink":"/blog/archives/2015-04-06-html-css2-note/","section":"Archives","summary":"1. border-style : dotted的’dotted’属性不兼容ie6\n2. 不同元素的上下外边距margin会叠压，父子级包含的时候子级的margin-top会传递给父级；（内边距替代外边距）\n3. word-spacing : 30px 单词间距（以空格为解析单位）\n4. 样式优先级 tag(1) \u003c class(10) \u003c id(100) \u003c style行间样式(1000) \u003c js\n5. IE6不支持a以外其它任何标签的伪类；IE6以上的浏览器支持所有标签的hover伪类。\n6. 行内样式 inline 的特点：\n默认同行可以继续跟同类型标签； 内容撑开宽度 不支持宽高 不支持上下的margin和padding 代码换行被解析 7.1 inline-block的特点:\n块在一行显示； 行内属性标签支持宽高； 没有宽度的时候内容撑开宽度 7.2 inline-block存在的问题:\n代码换行被解析；(代码换行的时候样式会有空隙) ie6 ie7 不支持块属性标签的inline-block; 8. img的默认属性为inline-block，img自带边框（经常在css reset中重置边框为 none）\n9. cursor : url(我的鼠标.jpg),pointer;cursor也可以自带图片来模拟鼠标样式，但后面需要附上的默认样式\n10. 谨慎使用通配符*，会影响性能\n11. clear left/right/both/none 是指元素的某个方向不能有浮动元素\n12. 在ie6下高度小于19px的元素，高度会被当做19px来处理,解决办法:给该元素设置overflow:hidden\n13. ie6,7的hasLayout属性使得具有宽度的父级子元素不需要清除浮动\n14. ie6,7下元素浮动，要并在同一行的元素都要加浮动\n15. IE6下的双边距BUG\n在IE6下，块元素有浮动和横向margin的时候，横向的margin值会被放大成两倍 解决办法: display:inline 16. IE6，7下li的间隙\n","title":"HTML-CSS的细节与注意事项","type":"archives"},{"content":"本文涉及的代码要放在IE 6，IE 7和IE 8下运行，与标准浏览器进行对比 推荐使用IE浏览器测试程序 IE tester\n常见问题 # 1. 在IE6元素浮动，如果宽度需要将内容撑开，就给里边的块元素都加浮动\n\u0026lt;!DOCTYPE HTML\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta http-equiv=\u0026#34;Content-Type\u0026#34; content=\u0026#34;text/html; charset=utf-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;无标题文档\u0026lt;/title\u0026gt; \u0026lt;style\u0026gt; .box{ width:400px;} .left{background:red;float:left;} .right{float:right; background:blue;} h3{margin:0;height:30px; float:left;}/*如果不加浮动，ie6下元素不会撑开*/ \u0026lt;/style\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;div class=\u0026#34;box\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;left\u0026#34;\u0026gt; \u0026lt;h3\u0026gt;左侧\u0026lt;/h3\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;right\u0026#34;\u0026gt; \u0026lt;h3\u0026gt;右侧\u0026lt;/h3\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 2. 在IE6，7下元素要通过浮动并在同一行，就给这行元素都加浮动\n\u0026lt;!DOCTYPE HTML\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta http-equiv=\u0026#34;Content-Type\u0026#34; content=\u0026#34;text/html; charset=utf-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;无标题文档\u0026lt;/title\u0026gt; \u0026lt;style\u0026gt; /* 由于.right没有加浮动，ie6下就会出现间隙啦 */ .left{width:100px;height:100px;background:Red; float:left;} .right{width:200px;height:100px;background:blue;margin-left:100px;} \u0026lt;/style\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;div class=\u0026#34;box\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;left\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;right\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 3. 注意标签的嵌套规范\n\u0026lt;!DOCTYPE HTML\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta http-equiv=\u0026#34;Content-Type\u0026#34; content=\u0026#34;text/html; charset=utf-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;无标题文档\u0026lt;/title\u0026gt; \u0026lt;style\u0026gt; p{width:100px;height:100px;background:Red;} \u0026lt;/style\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;!-- 这样做很不文明…… --\u0026gt; \u0026lt;p\u0026gt; \u0026lt;h3\u0026gt;\u0026lt;/h3\u0026gt; \u0026lt;/p\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 应该注意的兼容性问题 # 1. IE6下最小高度问题，在IE6下元素的高度的小于19px的时候，会被当做19px来处理，解决办法，给元素加上overflow:hidden;\n\u0026lt;!DOCTYPE HTML\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta http-equiv=\u0026#34;Content-Type\u0026#34; content=\u0026#34;text/html; charset=utf-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;无标题文档\u0026lt;/title\u0026gt; \u0026lt;style\u0026gt; .box{height:2px;background:red;} \u0026lt;/style\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;!-- ie6下.box为19px --\u0026gt; \u0026lt;div class=\u0026#34;box\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 2. border的属性dotted在ie6中不被支持，会被解析成dashed解决方法：使用背景图片平铺\n\u0026lt;!DOCTYPE HTML\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta http-equiv=\u0026#34;Content-Type\u0026#34; content=\u0026#34;text/html; charset=utf-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;无标题文档\u0026lt;/title\u0026gt; \u0026lt;style\u0026gt; .box{ width:100px;height:100px;border:1px dotted #000;} \u0026lt;/style\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;!-- 显示为dashed --\u0026gt; \u0026lt;div class=\u0026#34;box\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 3. 在IE6下解决margin传递要触发haslayout，在IE6下父级有边框的时候，子元素的margin值消失，解决办法:触发父级的haslayout（使用zoom: 1;）\n\u0026lt;!DOCTYPE HTML\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta http-equiv=\u0026#34;Content-Type\u0026#34; content=\u0026#34;text/html; charset=utf-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;无标题文档\u0026lt;/title\u0026gt; \u0026lt;style\u0026gt; body{margin:0;} .box{background:blue;border:1px solid #000;/*zoom:1;*/} .div{width:200px;height:200px;background:red;margin:100px;} \u0026lt;/style\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;!-- ie6下div的margin不见了 --\u0026gt; \u0026lt;div class=\u0026#34;box\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;div\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 4. IE6下双边距BUG，在IE6，块元素有浮动和和横向的margin值 ，横向的margin值会被放大成两倍，解决办法: display:inline;\n\u0026lt;!DOCTYPE HTML\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta http-equiv=\u0026#34;Content-Type\u0026#34; content=\u0026#34;text/html; charset=utf-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;无标题文档\u0026lt;/title\u0026gt; \u0026lt;style\u0026gt; body{margin:0;} .box{width:200px;height:200px;background:Red;float:left;margin:100px;/*display:inline;*/} \u0026lt;/style\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;!-- ie6下，box的margin值变成了两倍 --\u0026gt; \u0026lt;div class=\u0026#34;box\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; \u0026lt;!DOCTYPE HTML\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta http-equiv=\u0026#34;Content-Type\u0026#34; content=\u0026#34;text/html; charset=utf-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;无标题文档\u0026lt;/title\u0026gt; \u0026lt;style\u0026gt; .box{ float:left;border:10px solid #000;} .box div{width:100px;height:100px;background:Red;margin-right:20px;border:5px solid #ccc; float:left;/*display: inline;*/} /* margin-right 一行右侧第一个元素有双边距 margin-left 一行左侧第一个元素有双边距 */ \u0026lt;/style\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;div class=\u0026#34;box\u0026#34;\u0026gt; \u0026lt;div\u0026gt;1\u0026lt;/div\u0026gt; \u0026lt;div\u0026gt;2\u0026lt;/div\u0026gt; \u0026lt;div\u0026gt;3\u0026lt;/div\u0026gt; \u0026lt;div\u0026gt;4\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 5. 在IE6，7下，li本身没浮动，但是li的内容有浮动，li下边就会产生一个间隙，解决办法:\n给li加浮动（当IE6下最小高度问题，和li的间隙问题共存的时候需用这种方法解决） 给li加vertical-align \u0026lt;!DOCTYPE HTML\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta http-equiv=\u0026#34;Content-Type\u0026#34; content=\u0026#34;text/html; charset=utf-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;无标题文档\u0026lt;/title\u0026gt; \u0026lt;style\u0026gt; ul{margin:0;padding:0;width:302px;} li{ list-style:none;height:30px;border:1px solid #000; /*vertical-align:top;*/} a{width:100px;float:left;height:30px;background:Red;} span{width:100px;float:right;height:30px;background:blue;} \u0026lt;/style\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;ul\u0026gt; \u0026lt;li\u0026gt; \u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;\u0026lt;/a\u0026gt; \u0026lt;span\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;/li\u0026gt; \u0026lt;!-- ie6，ie7下两个li之间有间隙啊啊啊啊 --\u0026gt; \u0026lt;li\u0026gt; \u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;\u0026lt;/a\u0026gt; \u0026lt;span\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;/li\u0026gt; \u0026lt;li\u0026gt; \u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;\u0026lt;/a\u0026gt; \u0026lt;span\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;/li\u0026gt; \u0026lt;/ul\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 6. 在IE6下的文字溢出BUG，子元素的宽度和父级的宽度相差小于3px的时候,两个浮动元素中间有注释或者内嵌元素，解决办法:用div把注释或者内嵌元素用div包起来\n\u0026lt;!DOCTYPE HTML\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta http-equiv=\u0026#34;Content-Type\u0026#34; content=\u0026#34;text/html; charset=utf-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;无标题文档\u0026lt;/title\u0026gt; \u0026lt;style\u0026gt; .box{ width:400px;} .left{float:left;} .right{width:400px;float:right;} \u0026lt;/style\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;div class=\u0026#34;box\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;left\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;!-- IE6下的文字溢出BUG，因为有了注释 --\u0026gt;\u0026lt;span\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;!-- 我是正确的 start --\u0026gt; \u0026lt;div\u0026gt;\u0026lt;!-- IE6下的文字溢出BUG，因为有了注释 --\u0026gt;\u0026lt;span\u0026gt;\u0026lt;/span\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;!-- 我是正确的 end --\u0026gt; \u0026lt;div class=\u0026#34;right\u0026#34;\u0026gt;\u0026amp;darr;我会溢出来找你的……\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 7. 当一行子元素占有的宽度之和和父级的宽度相差超过3px,或者有不满行状态的时候,最后一行子元素的下margin在IE6下就会失效，解决方法：尽量用padding来代替\n\u0026lt;!DOCTYPE HTML\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta http-equiv=\u0026#34;Content-Type\u0026#34; content=\u0026#34;text/html; charset=utf-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;无标题文档\u0026lt;/title\u0026gt; \u0026lt;style\u0026gt; .box{border:10px solid #000;width:600px; overflow:hidden;} .box div{width:100px;height:100px;background:Red;margin:20px;border:5px solid #ccc; float:left; display:inline;} \u0026lt;/style\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;div class=\u0026#34;box\u0026#34;\u0026gt; \u0026lt;div\u0026gt;1\u0026lt;/div\u0026gt; \u0026lt;div\u0026gt;2\u0026lt;/div\u0026gt; \u0026lt;div\u0026gt;3\u0026lt;/div\u0026gt; \u0026lt;div\u0026gt;4\u0026lt;/div\u0026gt; \u0026lt;div\u0026gt;1\u0026lt;/div\u0026gt; \u0026lt;div\u0026gt;2\u0026lt;/div\u0026gt; \u0026lt;div\u0026gt;3\u0026lt;/div\u0026gt; \u0026lt;div\u0026gt;4\u0026lt;/div\u0026gt; \u0026lt;!-- 最后一行margin失效了 --\u0026gt; \u0026lt;div\u0026gt;1\u0026lt;/div\u0026gt; \u0026lt;div\u0026gt;2\u0026lt;/div\u0026gt; \u0026lt;div\u0026gt;3\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 8. IE6，7下，子元素有相对定位的话，父级的overflow包不住子元素，解决办法: 给父级也加相对定位\n\u0026lt;!DOCTYPE HTML\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta http-equiv=\u0026#34;Content-Type\u0026#34; content=\u0026#34;text/html; charset=utf-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;无标题文档\u0026lt;/title\u0026gt; \u0026lt;style\u0026gt; .box{ width:200px;height:200px;border:1px solid #000; overflow:auto; /*position:relative;*/} .div{ width:150px;height:300px;background:yellow; position:relative;} \u0026lt;/style\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;div class=\u0026#34;box\u0026#34;\u0026gt; \u0026lt;!-- IE6，7下div溢出了 --\u0026gt; \u0026lt;div class=\u0026#34;div\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 9. 在IE6下绝对定位元素的父级宽高是奇数的时候，元素的right值和bottom值会有1px的偏差（注意：top和left不会出现该问题）\n\u0026lt;!DOCTYPE HTML\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta http-equiv=\u0026#34;Content-Type\u0026#34; content=\u0026#34;text/html; charset=utf-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;无标题文档\u0026lt;/title\u0026gt; \u0026lt;style\u0026gt; .box{width:201px;height:201px;border:1px solid #000; position:relative;} .box span{ width:20px;height:20px;background:yellow; position:absolute;right:0px;bottom:0px;/*right:-1px;bottom:-1px;*/} \u0026lt;/style\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;div class=\u0026#34;box\u0026#34;\u0026gt; \u0026lt;!-- ie6下span离div还有一个像素 --\u0026gt; \u0026lt;span\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 10. position: fixed;在ie6下不能使用\n11. 在IE6，7下输入类型的表单控件上下各有1px的间隙，解决办法:给input加浮动\n\u0026lt;!DOCTYPE HTML\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta http-equiv=\u0026#34;Content-Type\u0026#34; content=\u0026#34;text/html; charset=utf-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;无标题文档\u0026lt;/title\u0026gt; \u0026lt;style\u0026gt; .box{ width:200px;height:32px;border:1px solid red;} input{width:100px;height:30px;border:1px solid #000;margin:0;padding:0; /*float:left;*/} \u0026lt;/style\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;div class=\u0026#34;box\u0026#34;\u0026gt; \u0026lt;!-- ie6,7下有1px的空隙 --\u0026gt; \u0026lt;input type=\u0026#34;text\u0026#34; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 12. 在IE6，7下输入类型的表单控件输入文字的时候，背景图片会跟着一块移动，解决办法: 把背景加给父级\n\u0026lt;!DOCTYPE HTML\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta http-equiv=\u0026#34;Content-Type\u0026#34; content=\u0026#34;text/html; charset=utf-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;无标题文档\u0026lt;/title\u0026gt; \u0026lt;style\u0026gt; .box{ width:100px;height:30px;border:1px solid red;background:yellow; background:url(ball.png) no-repeat;/*把背景加给我，please!*/} input{width:100px;height:30px;border:none;margin:0;padding:0; float:left; background:none;} \u0026lt;/style\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;div class=\u0026#34;box\u0026#34;\u0026gt; \u0026lt;input type=\u0026#34;text\u0026#34; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 13. 想要去掉input的border属性，在IE6，7下输入类型的表单控件加border:none;的同时重置input的背景\n\u0026lt;!DOCTYPE HTML\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta http-equiv=\u0026#34;Content-Type\u0026#34; content=\u0026#34;text/html; charset=utf-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;无标题文档\u0026lt;/title\u0026gt; \u0026lt;style\u0026gt; .box{ width:200px;height:32px;border:1px solid red;background:yellow;} input{width:100px;height:30px;border:none;margin:0;padding:0; float:left; /*background:#fff;*/} \u0026lt;/style\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;div class=\u0026#34;box\u0026#34;\u0026gt; \u0026lt;!-- IE6，7去边框失败…… --\u0026gt; \u0026lt;input type=\u0026#34;text\u0026#34; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 14. IE 6不支持png格式的图片透明，解决方案：开源插件 DD_belatedPNG.js 解决图片透明问题\n\u0026lt;!DOCTYPE HTML\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta http-equiv=\u0026#34;Content-Type\u0026#34; content=\u0026#34;text/html; charset=utf-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;无标题文档\u0026lt;/title\u0026gt; \u0026lt;style\u0026gt; body{ background:#000;} .box{width:400px;height:400px;background:url(img/png.png);} \u0026lt;/style\u0026gt; \u0026lt;!--[if IE 6]\u0026gt; \u0026lt;script src=\u0026#34;DD_belatedPNG_0.0.8a.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script\u0026gt; DD_belatedPNG.fix(\u0026#39;.box\u0026#39;); \u0026lt;/script\u0026gt; \u0026lt;![endif]--\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;div class=\u0026#34;box\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 15. 在IE6下，在important，后边在家一条同样的样式，会破坏掉important的作用，会按照默认的优先级顺序来走\n\u0026lt;!DOCTYPE HTML\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta http-equiv=\u0026#34;Content-Type\u0026#34; content=\u0026#34;text/html; charset=utf-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;无标题文档\u0026lt;/title\u0026gt; \u0026lt;style\u0026gt; .box{width:100px;height:100px;background:red !important; background:pink;} \u0026lt;/style\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;!-- ie6中box为黑色 --\u0026gt; \u0026lt;div class=\u0026#34;box\u0026#34; style=\u0026#34;background:#000;\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 16. CSS hack（危险，尽量不要使用！）\n\u0026lt;!DOCTYPE HTML\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta http-equiv=\u0026#34;Content-Type\u0026#34; content=\u0026#34;text/html; charset=utf-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;无标题文档\u0026lt;/title\u0026gt; \u0026lt;style\u0026gt; .box{ width:100px;height:100px;background:Red;background:blue\\9; +background:yellow;_background:green;} @media screen and (-webkit-min-device-pixel-ratio:0){.box{background:pink}} /* css hack： \\9 IE10之前的IE浏览器解析 +,* IE7包括IE7之前的IE浏览器解析 _ IE6包括IE6之前的IE浏览器 chrome hack 的写法：@media screen and (-webkit-min-device-pixel-ratio:0){.box{background:pink}} */ \u0026lt;/style\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;div class=\u0026#34;box\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 17. ie6不支持fixed，可模拟如下（存在一些小问题，比较少使用）:\n第一种方法：\n\u0026lt;!DOCTYPE HTML\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta http-equiv=\u0026#34;Content-Type\u0026#34; content=\u0026#34;text/html; charset=utf-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;无标题文档\u0026lt;/title\u0026gt; \u0026lt;style\u0026gt; html{height:100%;overflow:hidden;} body{margin:0; height:100%;overflow:auto;} .box{height:2000px;} .div{width:100px;height:100px;background:red; position:absolute;left:100px;top:100px;} \u0026lt;/style\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;!-- 因为.div相对于document定位，当文档大小不变时，.div也不会移动 --\u0026gt; \u0026lt;div class=\u0026#34;box\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;div\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 第二种方法：\n\u0026lt;!DOCTYPE HTML\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta http-equiv=\u0026#34;Content-Type\u0026#34; content=\u0026#34;text/html; charset=utf-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;无标题文档\u0026lt;/title\u0026gt; \u0026lt;style\u0026gt; .box{height:2000px;} .div{width:100px;height:100px;background:red; position:fixed;left:100px;top:100px;_position:absolute;_top:expression(eval(document.documentElement.scrollTop+100)); } \u0026lt;/style\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;!-- css hack模式 --\u0026gt; \u0026lt;!-- ie6下滑动时会颤抖…… --\u0026gt; \u0026lt;div class=\u0026#34;box\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;div\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 番外篇\u0026ndash;布局…… # 圣杯布局（双飞翼布局） # 左右两侧固定，中间自由伸缩，中间先加载\n\u0026lt;!DOCTYPE HTML\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta http-equiv=\u0026#34;Content-Type\u0026#34; content=\u0026#34;text/html; charset=utf-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;无标题文档\u0026lt;/title\u0026gt; \u0026lt;style\u0026gt; body{margin:0;} .center{height:600px;background:#f60;margin:0 200px;} .left{width:200px;background:#fc0;height:600px; position:absolute;left:0;top:0;} .right{width:200px;background:#fcc;height:600px;position:absolute;right:0;top:0;} \u0026lt;/style\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;div class=\u0026#34;center\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;left\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;right\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 等高布局 # 容器高度随着左右两边的增高而变化\n\u0026lt;!DOCTYPE HTML\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta http-equiv=\u0026#34;Content-Type\u0026#34; content=\u0026#34;text/html; charset=utf-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;无标题文档\u0026lt;/title\u0026gt; \u0026lt;style\u0026gt; body{margin:0;} .wrap{ width:900px;margin:0 auto; border:10px solid #000; overflow:hidden;} .wrap:after{content:\u0026#34;\u0026#34;;display:block;clear:both;} .left{width:200px;background:Red;float:left; padding-bottom:10000px; margin-bottom:-10000px;} .center{float: left;padding-bottom: 10000px;margin-bottom: -10000px;background-color: yellow;width: 500px;} .right{width:200px;background:blue;float:right;padding-bottom:10000px;margin-bottom:-10000px;} \u0026lt;/style\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;div class=\u0026#34;wrap\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;left\u0026#34;\u0026gt; \u0026amp;nbsp;页面内容\u0026lt;br/\u0026gt; \u0026amp;nbsp;页面内容\u0026lt;br/\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;center\u0026#34;\u0026gt; \u0026amp;nbsp;页面内容\u0026lt;br/\u0026gt; \u0026amp;nbsp;页面内容\u0026lt;br/\u0026gt; \u0026amp;nbsp;页面内容\u0026lt;br/\u0026gt; \u0026amp;nbsp;页面内容\u0026lt;br/\u0026gt; \u0026amp;nbsp;页面内容\u0026lt;br/\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;right\u0026#34;\u0026gt; \u0026amp;nbsp;页面内容\u0026lt;br/\u0026gt; \u0026amp;nbsp;页面内容\u0026lt;br/\u0026gt; \u0026amp;nbsp;页面内容\u0026lt;br/\u0026gt; \u0026amp;nbsp;页面内容\u0026lt;br/\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 可能还有别的知识，未完待补充！！\n如果你有一些其他的跟兼容性有关的知识，请分享给我，谢谢！\n","date":"April 6, 2015","externalUrl":null,"permalink":"/blog/archives/2015-04-06-ie-compatibility/","section":"Archives","summary":"本文涉及的代码要放在IE 6，IE 7和IE 8下运行，与标准浏览器进行对比 推荐使用IE浏览器测试程序 IE tester\n常见问题 # 1. 在IE6元素浮动，如果宽度需要将内容撑开，就给里边的块元素都加浮动\n\u003c!DOCTYPE HTML\u003e \u003chtml\u003e \u003chead\u003e \u003cmeta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"\u003e \u003ctitle\u003e无标题文档\u003c/title\u003e \u003cstyle\u003e .box{ width:400px;} .left{background:red;float:left;} .right{float:right; background:blue;} h3{margin:0;height:30px; float:left;}/*如果不加浮动，ie6下元素不会撑开*/ \u003c/style\u003e \u003c/head\u003e \u003cbody\u003e \u003cdiv class=\"box\"\u003e \u003cdiv class=\"left\"\u003e \u003ch3\u003e左侧\u003c/h3\u003e \u003c/div\u003e \u003cdiv class=\"right\"\u003e \u003ch3\u003e右侧\u003c/h3\u003e \u003c/div\u003e \u003c/div\u003e \u003c/body\u003e \u003c/html\u003e 2. 在IE6，7下元素要通过浮动并在同一行，就给这行元素都加浮动\n\u003c!DOCTYPE HTML\u003e \u003chtml\u003e \u003chead\u003e \u003cmeta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"\u003e \u003ctitle\u003e无标题文档\u003c/title\u003e \u003cstyle\u003e /* 由于.right没有加浮动，ie6下就会出现间隙啦 */ .left{width:100px;height:100px;background:Red; float:left;} .right{width:200px;height:100px;background:blue;margin-left:100px;} \u003c/style\u003e \u003c/head\u003e \u003cbody\u003e \u003cdiv class=\"box\"\u003e \u003cdiv class=\"left\"\u003e\u003c/div\u003e \u003cdiv class=\"right\"\u003e\u003c/div\u003e \u003c/div\u003e \u003c/body\u003e \u003c/html\u003e 3. 注意标签的嵌套规范\n","title":"低版本IE为主的兼容性问题╮(╯﹏╰）╭","type":"archives"},{"content":" SEO的基础知识 # SEO的类别\n黑帽 SEO（欺骗） 白帽 SEO（正规） 黑帽SEO # 通过罗列与网站无关的关键字，或者不良关键字，来欺骗搜索引擎来增加网站的访问率\n白帽SEO # 网站标题、关键字、描述。 网站内容优化。增加长尾关键字，增加关键字密度。 Robot.txt 文件。 网站地图。 增加外链引用。到各个网站上宣传。 网站地图 一个网站所有链接的容器。很多网站的连接层次比较深，蜘蛛很难抓取到 ，网站地图可以方便搜索引擎蜘蛛抓取网站页面，通过抓取网站页面，清晰了解网站的 架构，网站地图一般存放在根目录下并命名为sitemap，为搜索引擎蜘蛛指路，增加网站 重要内容页面的收录。网站地图就是根据网站的结构、框架、内容，生成的导航网页文 网站地图网站地图件。大多数人都知道网站地图对于提高用户体验有好处：它们为网站 访问者指明方向，并帮助迷失的访问者找到他们想看的页面。\n网站结构布局优化 # 控制首页链接数量（中小型网站建议一百个左右） 扁平化的目录层次（使内容的跳跃尽量不超过三次，尽量三次就能到达目的地） 导航的SEO优化（主导航，副导航，加入面包屑导航（导航尽量用文字表示，图片也要加上alt和title等属性）） 不可忽略的细节：LOGO，导航条，分页目录，页面大小 面包屑导航\n让用户了解当前所处的位置 使用户可以了解网站的组织形式，提高抓取，降低跳出率 类似于：首页\u0026gt;篮球\u0026gt;NBA 这种形式的放在页首的超链接\n分页目录（SEO效果逐级增强）\n首页-上页-下页-尾页 首页 1 2 3 4 5 下一页 首页 1 2 3 4 5 ·下拉框· 代码上的SEO优化 # 在搜索引擎得到高度重视 ：在seo没有起到很好的效果 ：重要程度仅次于strong title:只强调重点，不要重复出现；每个页面不设置相同的title meta keywords:不要堆砌 meta description:每个页面都要不同 语义化代码，详见HTML5 重要内容HTML代码放在最前面。小蜘蛛抓取内容是从上到下 重要内容不要用JS输出。 小蜘蛛看不懂js 谨慎使用display:none;可以把他放到外面 精简代码。 友情链接上的优化：给a标签添加rel=\u0026ldquo;nofollow\u0026quot;属性，防止蜘蛛跳转到别的网站\n\u0026lt;a href=\u0026#34;www.qq.com\u0026#34; rel=\u0026#34;nofollow\u0026#34; title=\u0026#34;腾讯\u0026#34;\u0026gt;腾讯\u0026lt;/a\u0026gt; 让br标签只出现在p标签中\n\u0026lt;p\u0026gt;\u0026lt;br/\u0026gt;\u0026lt;/p\u0026gt; HTML5 语义化:\n\u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html lang=\u0026#34;en\u0026#34;\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026#34;UTF-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;Document\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;header\u0026gt; \u0026lt;nav\u0026gt; \u0026lt;ul\u0026gt; \u0026lt;li\u0026gt;...\u0026lt;/li\u0026gt; \u0026lt;li\u0026gt;...\u0026lt;/li\u0026gt; \u0026lt;li\u0026gt;...\u0026lt;/li\u0026gt; \u0026lt;/ul\u0026gt; \u0026lt;/nav\u0026gt; \u0026lt;/header\u0026gt; \u0026lt;div id=\u0026#34;main\u0026#34;\u0026gt; ... \u0026lt;/div\u0026gt; \u0026lt;footer\u0026gt; ... \u0026lt;/footer\u0026gt; \u0026lt;script\u0026gt;...\u0026lt;/script\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; ","date":"March 27, 2015","externalUrl":null,"permalink":"/blog/archives/2015-03-27-web-seo/","section":"Archives","summary":"SEO的基础知识 # SEO的类别\n黑帽 SEO（欺骗） 白帽 SEO（正规） 黑帽SEO # 通过罗列与网站无关的关键字，或者不良关键字，来欺骗搜索引擎来增加网站的访问率\n白帽SEO # 网站标题、关键字、描述。 网站内容优化。增加长尾关键字，增加关键字密度。 Robot.txt 文件。 网站地图。 增加外链引用。到各个网站上宣传。 网站地图 一个网站所有链接的容器。很多网站的连接层次比较深，蜘蛛很难抓取到 ，网站地图可以方便搜索引擎蜘蛛抓取网站页面，通过抓取网站页面，清晰了解网站的 架构，网站地图一般存放在根目录下并命名为sitemap，为搜索引擎蜘蛛指路，增加网站 重要内容页面的收录。网站地图就是根据网站的结构、框架、内容，生成的导航网页文 网站地图网站地图件。大多数人都知道网站地图对于提高用户体验有好处：它们为网站 访问者指明方向，并帮助迷失的访问者找到他们想看的页面。\n网站结构布局优化 # 控制首页链接数量（中小型网站建议一百个左右） 扁平化的目录层次（使内容的跳跃尽量不超过三次，尽量三次就能到达目的地） 导航的SEO优化（主导航，副导航，加入面包屑导航（导航尽量用文字表示，图片也要加上alt和title等属性）） 不可忽略的细节：LOGO，导航条，分页目录，页面大小 面包屑导航\n","title":"SEO在网页制作中的应用","type":"archives"},{"content":"一直觉得自己在人际交往方面有很大的缺点，嘴笨的人总是容易说错话，经常会有因为不善于表达而造成一些不必要的误会，也因为不善于交际，而错失很多让自己变得更加优秀的机会。\n很大的缺点，也就表示还有很大的提升空间。这些年一直在这方面做着努力，虽然性格决定，收效甚微，但还是看到了一点点变化。从以前或是愣愣的发呆看着别人聊天，或是不经思考开口时的结结巴巴，或是交流结束后对自己言语的后悔，逐渐转变为在思考后去行动的模式。开始尝试在脑海里去琢磨自己的一言一行，然后在尝试着去组织自己的言论；开始在说错话后思考为什么会有后悔的心理，后悔的原因是什么。\n今天恰好看到一篇文章，谈论的是人在有基本的礼貌和社交礼仪的情况下，当一个内向的人并不是不可取的。豁然开窍~ 一个标榜自己不善于交际的人，如果不具备做人所应有的基本的礼貌，那这个人是否不是一个内向的人，而是一个缺乏自信的人？\n在承认自己是否为内向的人的时候，只有先看清自己的本质，才能对症下药吧！\n基本的礼仪，或者说基本的礼貌是什么，知乎（感觉怎么变成搜索引擎了……）上面有一些很好的回答，贴在此，以警示自己，努力成为一个彬彬有礼亦不卑不亢的人~\n知乎推荐：哪些礼仪是一个人必备的？\n","date":"March 22, 2015","externalUrl":null,"permalink":"/blog/archives/2015-03-22-jotting/","section":"Archives","summary":"一直觉得自己在人际交往方面有很大的缺点，嘴笨的人总是容易说错话，经常会有因为不善于表达而造成一些不必要的误会，也因为不善于交际，而错失很多让自己变得更加优秀的机会。\n很大的缺点，也就表示还有很大的提升空间。这些年一直在这方面做着努力，虽然性格决定，收效甚微，但还是看到了一点点变化。从以前或是愣愣的发呆看着别人聊天，或是不经思考开口时的结结巴巴，或是交流结束后对自己言语的后悔，逐渐转变为在思考后去行动的模式。开始尝试在脑海里去琢磨自己的一言一行，然后在尝试着去组织自己的言论；开始在说错话后思考为什么会有后悔的心理，后悔的原因是什么。\n今天恰好看到一篇文章，谈论的是人在有基本的礼貌和社交礼仪的情况下，当一个内向的人并不是不可取的。豁然开窍~ 一个标榜自己不善于交际的人，如果不具备做人所应有的基本的礼貌，那这个人是否不是一个内向的人，而是一个缺乏自信的人？\n在承认自己是否为内向的人的时候，只有先看清自己的本质，才能对症下药吧！\n基本的礼仪，或者说基本的礼貌是什么，知乎（感觉怎么变成搜索引擎了……）上面有一些很好的回答，贴在此，以警示自己，努力成为一个彬彬有礼亦不卑不亢的人~\n知乎推荐：哪些礼仪是一个人必备的？\n","title":"一个内向，有礼貌的人","type":"archives"},{"content":"从大二下学期偶然的机会，一个朋友推荐我知乎这个软件后，就导致了现在的三天一小窥，五天一沉沦。那些博学多才，见多识广的大牛们，总是让你佩服得五体投地。不管是什么领域上都会有那么个独特的人能够交出自己的满腹经纶，大到国际局势，小到街边琐事，即使是一句不经意的调侃，也会让你由衷地崇拜~\n逛多了知乎，自卑多了心。于是乎，我也开始向往着能够成为那么一个有才识又风趣幽默的人。在获取知识的路上屡屡碰壁，才发现才识对于我这个普通本科院校的低智商高等动物来说实在难于上青天~ 于是乎，我开始努力地，使劲吃……吃奶的力气把自己往幽默风趣的地盘上扔，才发现，幽默风趣居然也一脚把我踹到了门外边，而且连门都懒得关。才明了，即使是低俗不堪的黄段子，也需要有一嘴铁齿铜牙来驾驭。\n小人物的故事都是平平凡凡的，可当一个小人物开始去思考，我是谁，我从哪里来，我要到哪里去时，那么这个小人物呢，也还是一个小人物……可小人物有了思考之后，就变身成了一个高效率的小人物，而高效率的小人物是什么呢，那就要问问屎壳郎推粪球的速度提升了一倍是怎么样的体验~\n","date":"March 20, 2015","externalUrl":null,"permalink":"/blog/archives/2015-03-20-jotting/","section":"Archives","summary":"从大二下学期偶然的机会，一个朋友推荐我知乎这个软件后，就导致了现在的三天一小窥，五天一沉沦。那些博学多才，见多识广的大牛们，总是让你佩服得五体投地。不管是什么领域上都会有那么个独特的人能够交出自己的满腹经纶，大到国际局势，小到街边琐事，即使是一句不经意的调侃，也会让你由衷地崇拜~\n逛多了知乎，自卑多了心。于是乎，我也开始向往着能够成为那么一个有才识又风趣幽默的人。在获取知识的路上屡屡碰壁，才发现才识对于我这个普通本科院校的低智商高等动物来说实在难于上青天~ 于是乎，我开始努力地，使劲吃……吃奶的力气把自己往幽默风趣的地盘上扔，才发现，幽默风趣居然也一脚把我踹到了门外边，而且连门都懒得关。才明了，即使是低俗不堪的黄段子，也需要有一嘴铁齿铜牙来驾驭。\n小人物的故事都是平平凡凡的，可当一个小人物开始去思考，我是谁，我从哪里来，我要到哪里去时，那么这个小人物呢，也还是一个小人物……可小人物有了思考之后，就变身成了一个高效率的小人物，而高效率的小人物是什么呢，那就要问问屎壳郎推粪球的速度提升了一倍是怎么样的体验~\n","title":"优秀的人太多了，逛多了知乎，自卑多了心~","type":"archives"},{"content":" 创建仓库与初始化 # 创建新的文件夹，进入文件夹\n$ git init 检出远程仓库 # $ git clone /path/to/repository $ git clone username@host:/path/to/repository 添加与提交 # $ git add \u0026lt;filename\u0026gt; $ git add * $ git commit -m \u0026#34;代码提交信息\u0026#34; 推送到服务器 # $ git push origin master $ git remote add origin \u0026lt;server\u0026gt; ","date":"February 16, 2015","externalUrl":null,"permalink":"/blog/archives/2015-02-16-git-note/","section":"Archives","summary":"创建仓库与初始化 # 创建新的文件夹，进入文件夹\n$ git init 检出远程仓库 # $ git clone /path/to/repository $ git clone username@host:/path/to/repository 添加与提交 # $ git add \u003cfilename\u003e $ git add * $ git commit -m \"代码提交信息\" 推送到服务器 # $ git push origin master $ git remote add origin \u003cserver\u003e","title":"Git常用语法笔记","type":"archives"},{"content":"Markdown只是一种适用于网络的书写语言，而不是编程语言\n兼容 # 在HTML区块标签间的Markdown格式语法将不会被处理，而在Markdown格式语法里的HTML区段标签是有效的\n区块元素 # 段落 # 普通段落：\n这是一个普通的段落 啦啦啦，将会被转换为p标签 连续的 不连续的\\(^o^)/~ 标题 # # h1 ## h2 ### h3 ... ###### h6 区块引用 blockquote # \u0026gt; Proin eget tortor risus. \u0026gt; Curabitur aliquet quam id dui posuere blandit. 连续区块引用：\n\u0026gt; Proin eget tortor risus. \u0026gt; Curabitur aliquet quam id dui posuere blandit. \u0026gt; Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. \u0026gt; Donec sollicitudin molestie malesuada. \u0026gt; Praesent sapien massa, convallis a pellentesque nec, egestas non nisi. 嵌套：\n\u0026gt; \u0026gt; Proin eget tortor risus. Curabitur aliquet quam id dui posuere blandit. 在区块中引用其他Markdown语法:\n\u0026gt; #### h4 \u0026gt; \u0026gt; * list 1 \u0026gt; * list 2 \u0026gt; * list 3 列表 # 无序列表：\n* list1 * list2 * list3 有序列表：\n1. list 1 2. list 2 3. list 3 将列表用p标签分隔开：\n* list 1 * list 2 多段落列表：\n(单个列中多段落以段前4个空格符或者间隔符来区分，段落间空行使p标签包裹文本)\n1. Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a.Proin eget tortor risus. Proin eget tortor risus. Cras ultricies ligula sed magna dictum porta. 2. Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a.Proin eget tortor risus. Proin eget tortor risus.Crasultricies ligula sed magna dictum porta. 3. Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a.Proin eget tortor risus. Proin eget tortor risus. Cras ultricies ligula sed magna dictum porta. 在列表中加入引用blockquote：\n（需要缩进）\n* Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. \u0026gt; This is a blockquote \u0026gt; inside a list item. 用反斜线来防止列表的生成：\n1\\.this blockquote looks pretty good. 分隔线 # （三个以上的星号、减号、底线）\n* * * *** - - - --------------------------------------- 区段元素 # 链接 # 行内链接：\nit links to [google](http://www.google.com/ \u0026#34;the title of this link\u0026#34;) , click it! [yahoo](http:www.yahoo.com) has no title attr. 参考链接：\nThis is [an example] [id] reference-style link. \u0026lt;!-- 然后在任意的地方把链接定义出来，不区分大小写，有点像书本的脚标 --\u0026gt; [id]: \u0026lt;http://example.com/\u0026gt; \u0026#34;Optional Title Here\u0026#34; e.g.\nI get 10 times more traffic from [Google] [1] than from [Yahoo] [2] or [MSN] [3]. [1]: http://google.com/ \u0026#34;Google\u0026#34; [2]: http://search.yahoo.com/ \u0026#34;Yahoo Search\u0026#34; [3]: http://search.msn.com/ \u0026#34;MSN Search\u0026#34; 强调 # 一个星号包裹是em，两个是strong，同样反斜线输出正常的星号\n*single asterisks* _single underscores_ **double asterisks** __double underscores__ 代码 # 用反引号包裹形成code代码\n`printf()` 图片 # 行内图片：\n![Alt text](/path/to/img.jpg) ![Alt text](/path/to/img.jpg \u0026#34;Optional title\u0026#34;) 参考图片：\n![Alt text][id] [id]: url/to/image \u0026#34;Optional title attribute\u0026#34; 其他 # 自动连接 # \u0026lt;address@example.com\u0026gt; 反斜杠 # Markdown 支持以下这些符号前面加上反斜杠来帮助插入普通的符号：\n\\ 反斜线 ` 反引号 * 星号 _ 底线 {} 花括号 [] 方括号 () 括弧 # 井字号 + 加号 - 减号 . 英文句点 ! 惊叹号 以上仅方便个人使用，一些自己觉得不需要的就没有加入了。\n强大的Markdown肯定有一些更好的语法，请自行google哈！\n","date":"February 13, 2015","externalUrl":null,"permalink":"/blog/archives/2015-02-15-markdown-note/","section":"Archives","summary":"Markdown只是一种适用于网络的书写语言，而不是编程语言\n兼容 # 在HTML区块标签间的Markdown格式语法将不会被处理，而在Markdown格式语法里的HTML区段标签是有效的\n区块元素 # 段落 # 普通段落：\n这是一个普通的段落 啦啦啦，将会被转换为p标签 连续的 不连续的\\(^o^)/~ 标题 # # h1 ## h2 ### h3 ... ###### h6 区块引用 blockquote # \u003e Proin eget tortor risus. \u003e Curabitur aliquet quam id dui posuere blandit. 连续区块引用：\n","title":"Markdown的语法学习总结","type":"archives"},{"content":" 博客 1.0 # 哈哈，历经千幸万苦（其实也就是花了几天的时间……），我的博客的第一个版本终于完成了！！！（其实也就是一个十分简易，简陋的小博客……）\n这几天各种google资料，真的是累得够呛，其实总结了下也不是很难，只是英文不行，一堆的英文资料看着头就大了，加上断断续续的家里总有一些事发生，没能够在连续的时间段里（感觉有程序猿的调调）完成博客的基本功能，所以才拖了这么久~\n现在！总结一下这个博客完成的过程……\nGithub账号是必不可少的 学习Git相关知识 在Github提供的主机上搭建jekyll模板 学习Markdown语言 学习Liquid语言模板 完成博客的基本主题 加入disqus评论功能 加入代码高亮 搞定啦！！！！！！！（原来这个过程这么短的啊……为何我搞了那么多天）\n辛苦总是有回报的T T\n先说到这里吧~（我也不知道说点啥好啊我去，等这一刻等得那么艰苦……）\n","date":"February 13, 2015","externalUrl":null,"permalink":"/blog/archives/2015-02-13-my-blog-version1.0-description/","section":"Archives","summary":"博客 1.0 # 哈哈，历经千幸万苦（其实也就是花了几天的时间……），我的博客的第一个版本终于完成了！！！（其实也就是一个十分简易，简陋的小博客……）\n这几天各种google资料，真的是累得够呛，其实总结了下也不是很难，只是英文不行，一堆的英文资料看着头就大了，加上断断续续的家里总有一些事发生，没能够在连续的时间段里（感觉有程序猿的调调）完成博客的基本功能，所以才拖了这么久~\n现在！总结一下这个博客完成的过程……\nGithub账号是必不可少的 学习Git相关知识 在Github提供的主机上搭建jekyll模板 学习Markdown语言 学习Liquid语言模板 完成博客的基本主题 加入disqus评论功能 加入代码高亮 搞定啦！！！！！！！（原来这个过程这么短的啊……为何我搞了那么多天）\n辛苦总是有回报的T T\n先说到这里吧~（我也不知道说点啥好啊我去，等这一刻等得那么艰苦……）\n","title":"终于完成一个很简易的博客了！","type":"archives"},{"content":"","externalUrl":null,"permalink":"/blog/search/","section":"阿伟的部落格","summary":"","title":"搜索","type":"page"}]