Skip to content

HTML canvas框架汇总

HTML canvas的框架很多,而且侧重不同,比如 Three.js 是有名的3D框架,d3.js 偏重于数据展示。

名称 领域 框架 移动端 文档 其他
ThreeJS 3D N Y 强大的绘图引擎
playCanvas 3D Y Y 游戏引擎,提供了一个可视化的编辑工具 playCanvas Editor
Egret 2D+3D Y Y 国产,多端游戏引擎
PixiJS 2D Y WebGL引擎
zrender 2D N Y 为ECharts而生
KONVA 2D en
KineticJS 2D N 缺乏示例
Fabric.js 2D en Github 11679 stars
CREATEJS
Paper.js
Two.js 2D
spritejs 2D 国产优秀库,偏重展示和动画
jCanvas 基于jQuery
oCanvas
Processing.js
RGraph 2D 图表解决方案
Zebkit
matter.js
Phaser HTML5游戏框架
heatmap.js 2D 热力图

谜案馆:故宫《迷宫·如意琳琅图籍》攻略玩法

UPDATE 2019-01-08 09:17

本文基于故宫出版社互动解谜游戏书《如意琳琅图籍》,目前还在更新,也欢迎大家加我微信进微信群交流(群二维码在最下面)。

在最后附目标成就线索:目标达成、解锁结局、史实收集、物品收集。

初章 梦入紫禁

太和异象

太和异象答案:

1) 下面两个选项没区别,殊途同归:

  • 继续待在广场 ✔
  • 仔细聆听音乐 ✔

2) 根据诗句分辨出一个地名:

  • 断虹桥

本节收集史实:画画人、太和殿、漆黑之书(如意琳琅图籍)

十八棵槐

1) 《快雪时晴贴》隐藏了什么秘密?

  • 琳琅宝藏

2) 字帖背面透露着信息…

  • 就在此宫

本节收集史实:西华门、启祥宫、金水河、十八棵槐

第一章 万寿盛筵

殿前观礼

1) 图籍中云锣图看出四个字来:

  • 寻找八音

本节收集史实:卯时三刻、如意馆、采冰(宫中采冰)、崇庆皇太后、上徽号、大朝、工尺谱、中和韶乐

礼乐度量

1) 图籍中翻到八音那几页,开始思考其中的含义…

  • 太和殿前嘉量

(这个要注意,图籍中已经把“量”字圈出来了,答案也要写上)

2) 选择:两个答案都可以

  • 找姚文瀚问万寿图 ✔
  • 太和殿附近找机会 ✔

3) 选择:

这里如果选择第1、3项,会终止游戏回到之前的关卡,但是可以解锁不同的结局,所以建议先选1,再选3,最后选择第2项继续往下进行。

  • 从右翼门直接上三台 → 解锁“逐出紫禁城”
  • 走中右门叠落墙台阶 ✔
  • 从正中央大胆登三台 → 解锁史实“丹陛石”和结局“杖刑致死”

4) 嘉量容量单位与图籍中剖面图一一对应暗藏的信息:

  • 庆隆尊养

本节收集史实:敔、嘉量、三台、姚文瀚、起稿(万寿图起稿)、八旬万寿图、庆隆尊养

一等画师

无题目

腰牌买卖

1) 选择:

可以先选择“回启祥宫…”解锁结局,然后回到本关卡继续。

  • 回到启祥宫等机会 → 解锁“碌碌无为”
  • 在慈祥门伺机而动 ✔

本节收集史实:腰牌、内务府、胡世杰、献舞(乾隆献舞)

慈宁画样

1) 对联暗藏玄机,想到了什么…

  • 瑞兽化龙

2) 图籍四只瑞兽组合一条龙

  • 慈宁宫脊兽无鳞者

3) “无鳞脊兽”到底藏了什么秘密呢?

  • 纵有百闻千问

4) 选择:

因为我们前面解锁过“碌碌无为”了,所以这里直接选择“本来就不必…”

  • 明天再去做详细记录 → 解锁“碌碌无为”
  • 本来就不必仔细刻画 ✔

5) 图籍中《聚瑞图》题诗用意为何?

  • 如意门

6) 四张宝物小画拼图:(这个题注意下,解谜要求撕开图籍中的一页,不是从书口那里撕!是从里面有一个刻好的虚线撕开。然后根据圈圈的形状找出拼图的对应文字。但是很多朋友舍不得撕开,那就直接输入下面的答案!)

  • 永寿宫彩云图

本节收集史实:寿康宫、屋顶(紫禁城屋顶)、脊兽、齐闵王(骑凤仙人)、如意门

第二章 宫女禾心

嘉祉初遇

本节三个选择题,连续三次拒绝禾心,可以解锁目标“宁不相识”,建议先连续拒绝。不过!这里先选“真烦人…”再选“逗逗她”,才能收集史实“贝勒”

1) 选择:

  • 真烦人,把她打发走吧 ✔
  • 先忍了,打探她的事儿 ✔

2) 选择:

  • 冷淡拒绝 ✔
  • 逗逗她 ✔

3) 选择:

  • 和她聊聊 ✔
  • 撒腿就跑 ✔

4) 《永寿宫彩云图》指向这个地方…

  • 漱芳斋博古格

本节收集史实:漱芳斋、书福(漱芳斋书福)、赐福苍生笔(清宫毛笔)、贝勒

淑芳听戏

1) 四张宝物小图对应博古格的文字:

  • 白石道人

2) 选择

因为前面已解锁“杖刑致死”,所以建议“好声央求…”

  • 好声央求归还 ✔
  • 一把夺回图籍 → 解锁“杖刑致死”

本节收集史实:封印休假、门神、三清茶

戏里玄机

1) 白石道人乐谱一萼红错误的玄机:

  • 储秀宫永琰像

2) 选择:

建议先解锁“满门抄斩”,再重回本关卡选择“大胆撒谎”,因为前面已解锁“碌碌无为”,不用选“推出禾心”

  • 走为上策 → 解锁“满门抄斩”
  • 大胆撒谎 ✔
  • 推出禾心 → 解锁“碌碌无为”

本节收集史实:永琰、西洋画法、上元(上元节)、武英门、大小金川(大小金川之役)

上元之约

1) 选择:

这里前两个选项都选过后才能解锁目标“逛吃逛吃”,建议先选“去逛庙会”

  • 去逛庙会 → 解锁史实“庙会”
  • 去小吃街 → (解锁目标“逛吃逛吃”)
  • 去猜灯谜 ✔

如果选择“去猜灯谜”,会出现猜灯谜系列,谜底分别是:

  • 对仗
  • 羊入虎口

其实上面的谜底,都在之前关卡的图画里面,可以点击发现的。但是就算填错了,解谜也会继续。都填对了可以解锁“灯笼”

2) 选择:没有区别

  • 屋顶看天灯 ✔
  • 回老宅休息 ✔

本节收集史实:灯会、庙会

第三章 结伴寻宝

皇十五子

1) 《永琰图》没出现图籍中的元素猜文字:

  • 五时乐图

2) 宣纸画拼合《婴戏图》,结合节气诗线索:

  • 匠作处档房

本节收集史实:皇贵妃、一幅大画(庆妃永琰像轴)、婴戏图

档房探秘

1) 五言局棋盘,根据档房的画在图籍中棋盘上依次链接棋子,形成不同的汉字图案:

  • 一览众山小

2) 选择:

因为前面已解锁“私刑致死”,但是我们可以先选择“学猫叫”,然后回到关卡“第二章 上元之约”,选择“去小吃街”,解锁目标“逛吃逛吃”,然后再选一次“学猫叫”,回到关卡“第二章 上元之约”,选择“去猜灯谜”,会切换到不同解谜路线。

  • 冲出去 ✔
  • 学猫叫 → 解锁“私刑致死”

本节收集史实:

一路狂奔

1) 选择:

建议选“左边走”

  • 左边走 ✔
  • 右边走 → 解锁“私刑致死”

2) 选择:

建议选择“进右门”

  • 进左门 → 解锁“私刑致死”
  • 进右门 ✔
  • 径直走 → 解锁“逐出紫禁城”

2) 选择:

建议先选“东一门”,解锁“颠沛流离”,再重回此关卡

  • 东一门 → 解锁“颠沛流离”
  • 东二门 ✔
  • 东三门 → 解锁“逐出紫禁城”

本节收集史实:

逢凶化吉

本小节无题目

本节收集史实:李文照、伊兰泰

五行八卦

1) 造办处和内务府格局对比对应的八卦

  • 艮兑离坤震坎

2) 连线八卦图对应的点,穿过关键字:

  • 万春亭前人下

本节收集史实:八方、五行(阴阳五行)、万春亭

夜探御图

1) 使用有墨迹的宣纸和铜钱对应文字:

  • 铜下墨渍孔中奥秘

2) 继续上题,使用铜钱放在墨迹上,孔中文字:

  • 偏桃花开枝叶繁

3) 选择:

  • 躲着原地不动 ✔
  • 追随禾心下山 → 解锁“满门抄斩”
  • 从侧边绕下山 → 解锁“满门抄斩”

本节收集史实:一棵侧柏(人字柏)、乾隆通宝

第四章 琳琅宝藏

花叶之谜

1) 选择:

建议选择“继续在…”

  • 继续在画作库房找 ✔
  • 回启祥宫库房看看 → 解锁“逐出紫禁城”

2) 拼好偏桃之后,偏桃每个部位都有四字提示,顺序是姚文涵说的“初生花、后生叶…”,然后根据四字提示去上方诗里找对应的文字:

  • 泰斯米叶

3) 根据图籍中沙枣树页折叠沙枣花缎带:

  • 寻找友仁

本节收集史实:前人的画稿(妃嫔画稿)、威弧获鹿(威弧获鹿图卷)、木兰秋狝、平定回部(平淮平回之役)

宫中怪人

1) 选择:

建议先选择“为学习…”解锁“欺君大罪”,然后重回此关卡。

  • 为增饰万寿图御容 ✔
  • 为学习西洋画技法 → 解锁“欺君大罪”

2) 选择:

  • 满足好奇 ✔
  • 学到画法 ✔
  • 夸赞圣颜 → 解锁“欺君大罪”

3) 选择:

建议选择“去掉脸部阴影”

  • 完全写实来画 ✔
  • 去掉脸部阴影 → 解锁“乾隆赏赐”
  • 画出天子威仪 → 解锁“碌碌无为”

本节收集史实:潘廷璋

图籍作者

1) 图籍中天体仪上的图案寻找房间什物猜测字谜:

  • 日月双全

2) 御制星晷仪迷宫解谜:

  • 北斗七星

3) 御制简平仪藏字诗解谜:

  • 紫微星内

4) 折地图拼出日月双全字样,得到地点:

  • 雨花阁

本节收集史实:御制星晷仪、御制简平仪

祸不单行

1) 选择:

建议选择“帮忙”

  • 帮忙 → 选择此项才能解锁下面两个选择
  • 不帮忙 → 选这个才能解锁史实收集“郎世宁卖地”

2) 选择:

建议先选择“拖工期”,继续进行可以收集史实“卖地”,然后在下一选择选“琉璃重新烧”,解锁“打入大牢”,回到本关卡,在这里再选择“重新做”。

  • 重新做 → 解锁本节下一个选择。
  • 拖工期 → 选这个才能解锁史实收集“郎世宁卖地”
  • 删掉龙 → 解锁“欺君大罪”

3) 选择:

建议先选“琉璃重新烧”,解锁“打入大牢”,然后重回本关卡,再选择“木雕作龙腹”。

  • 我来画一条 → 待确认。(第一次玩的时候解锁了“欺君大罪”,后来却可以直接过去。)
  • 木雕作龙腹 → 选择此项才有本节下一个选择。
  • 琉璃重新烧 → 解锁“打入大牢”

4) 选择:

建议选择“涂上颜色”

  • 心存侥幸 → 解锁“欺君大罪”
  • 涂上颜色 → 解锁“贾仁悟的谢礼”

本节收集史实:卖地(郎世宁卖地)

五蕴皆空

1) 藏头诗:

  • 镂空红纸同花相贴

2) 使用镂空红纸道具

  • CROSS

3) 使用镂空纸蒙在图籍页面:

  • 水落字出

4) 去心经上找对应的字,沾湿(水落字出):

  • 丙子丁亥乙巳己未

5) 在图籍依次找到丙子、丁亥、乙巳、己未页,然后用镂空红纸和三张宣纸依次对应上去:

  • 德行层东

本节收集史实:佛龛、心经

第五章 将破未破

孤注一掷

1) 选择:

建议先选“我全都要”,解锁“流放边疆”,然后回本关卡,选择“扔下包裹”,解锁多项之后,最后到“名留青史”,再回本关卡选择“扔下书籍”

  • 扔下书籍 → 解锁“五十两银子”
  • 扔下颜料 → 解锁“打入大牢”
  • 扔下包裹 → 解锁“视学手稿”,选此项才能收集史实“嘉庆图”,然后在下面的图中,可以解锁“黄马褂”,然后继续解谜,最后解锁“名留青史”
  • 我全都要 → 解锁“流放边疆”

2) 选择:

建议先选择“贿赂胡公公”,然后到下一选择选“皇上的赏赐”解锁“普通画师”再回此关卡,选择“拖延下时间”

  • 拖延下时间 ✔
  • 贿赂胡公公 → 下一选择解锁“普通画师”

3) 选择:(上一选择选择“贿赂胡公公”才有)

  • 五十两银子 → 解锁“杖刑致死”
  • 皇上的赏赐 → 解锁“普通画师”

3) 选择:

  • 交出柜中黄布包裹 → 解锁“奏折之危”
  • 交出银两藏起折子 → 解锁“杖刑致死”
  • 告发胡世杰贪污案 → 解锁“颠沛流离”

有的人玩到这里已经结束了,解锁隐藏章节的方法就是回到初章点那只白色的鸟!!!

本节收集史实:

隐藏章节 多年以后

众筹专属

《如意琳琅图籍》如何解锁众筹专属章节?

解锁众筹专属章节,需要用到众筹专属的赠品,小袋子里面有徽章、书签、胶带、明信片,而明信片的宫墙上印有“五六工尺上”,这五个字是敲击云锣的顺序,在“第一章 万寿盛宴 殿前观礼”一节,找到图中云锣,按照上面五字顺序敲击即可解锁。云锣的位置如下图:

目标达成

结局解锁

遇到选择的时候,选择不同的选项可能会走向不同的结局。各个结局的解锁已经在上面攻略中的各个选择中标记出来了。

史实收集

史实收集非常简单,只需要在解谜过程中,随时点击带有下划线的文字即可。点开之后的页面如果很长,要拉到底才有效。

物品收集

黄马褂:第五章 孤注一掷 选择“扔下包裹”之后,再下面的图中拉到左侧找到。

绿狮子:第四章 宫中怪人 第三个选择中选“去掉脸部阴”

灯笼:第二章 上元之约 连续猜对三个灯谜

八卦镜:第四章 祸不单行 帮助了贾仁悟木雕完成九龙壁。

视学手稿:第五章 孤注一掷 选择“扔下包裹”

五十两银子:第五章 孤注一掷 选择“扔下书籍”

猫:第二章 礼乐度量 答案“太和殿前嘉量”之后的图片拉到最右边,猫在右下角

微信群二维码:

如何设置浏览器允许自动播放?

从2017年6月份开始,一些浏览器陆续升级,禁止网页中的音频自动播放,防止网页自动播放音视频对用户造成的干扰。但是这也给一些需要自动播放的网站带来了不便。

下面我们将介绍如何在 Chrome 和 Safari 中设置,允许网页中的音视频自动播放。

Chrome

如下图,在浏览器地址栏输入“chrome://flags”,然后回车。打开配置界面搜索“autoplay”,找到“Autoplay policy”,将下拉框选为第二项“No user gesture is required.”,点击右下角“RELAUNCH NOW”重启即可。

Safari

1 点击左上角菜单栏“Safari浏览器”找到“此网站的设置…”。

2 在弹出框的“自动播放”一项,选择“允许全部自动播放”。

Google Web 更新(2017年6月):DOMException The play() request was interrupted

英文原文:DOMException: The play() request was interrupted


你是不是刚刚在Chrome浏览器的控制台Console里遇到了这个未预料的媒体错误?

Uncaught (in promise) DOMException: The play() request was interrupted by a call to pause().

或者

Uncaught (in promise) DOMException: The play() request was interrupted by a new load request.

不要怕, 下面就来解释错误的原因和解决方法。

错误原因

下面的JavaScript代码再现了你遇到的“Uncaught (in promise)”错误:

错误代码:

<video id="video" preload="none" src="https://example.com/file.mp4"></video>

<script>
  video.play(); // <-- This is asynchronous!
  video.pause();
</script>

上面代码运行后在Chrome控制台的错误信息:

Uncaught (in promise) DOMException: The play() request was interrupted by a call to pause().

由于标签属性 preload=”none” 视频没有加载,在执行了 video.play() 之后,视频不一定会立即播放。

而且,从 Chrome 50 开始, <video>或<audio>元素调用play() 方法将会返回一个Promise,一个异步返回单一结果的函数。如果播放成功执行,Promise 会执行(fulfilled),同时会触发媒体的playing事件。如果播放失败,Promise会被拒绝执行,并且返回错误信息阐述失败原因。

这个过程中发生了这些事情:

  1. video.play() 试图异步加载视频内容。
  2. video.pause() 中断视频加载,因为并没有真正的准备好播放。
  3. video.play() 显式地拒绝异步加载。

这时候因为我们没有在代码里对视频播放的Promise进行处理,所以Chrome控制台抛出一个错误。

提示:唤起 video.pause() 并不是中断视频的唯一途径。你可以用 video.load()video.src=” 完完全全地重置视频的播放状态,包括视频缓冲。

解决方法

现在我们了解了问题的根源,然后来看看如何解决。

首先,不要恣意的去播放网页中的媒体文件。查看 play 方法返回的Promise是否被拒绝执行。如果播放没有真正的开始,Promise不会有任何值,也不会往下执行,下面示例中 then() 里面的代码也不会执行。

示例:自动播放(Autoplay)

<video id="video" preload="none" src="https://example.com/file.mp4"></video>

<script>
  // Show loading animation.
  var playPromise = video.play();

  if (playPromise !== undefined) {
    playPromise.then(_ => {
      // Automatic playback started!
      // Show playing UI.
    })
    .catch(error => {
      // Auto-play was prevented
      // Show paused UI.
    });
  }
</script>

示例:播放(Play)和停止(Pause)

<video id="video" preload="none" src="https://example.com/file.mp4"></video>

<script>
  // Show loading animation.
  var playPromise = video.play();

  if (playPromise !== undefined) {
    playPromise.then(_ => {
      // Automatic playback started!
      // Show playing UI.
      // We can now safely pause video...
      video.pause();
    })
    .catch(error => {
      // Auto-play was prevented
      // Show paused UI.
    });
  }
</script>

这个示例很棒,你以后是不是知道如何用 video.play() 来播放视频了?

告诉你一个秘密,不需要 video.play(),你可以用 video.load()。像这样:

示例:获取(Fetch)和播放(Play)

<video id="video"></video>
<button id="button"></button>

<script>
  button.addEventListener('click', onButtonClick);

  function onButtonClick() {
    // This will allow us to play video later...
    video.load();
    fetchVideoAndPlay();
  }

  function fetchVideoAndPlay() {
    fetch('https://example.com/file.mp4')
    .then(response => response.blob())
    .then(blob => {
      video.srcObject = blob;
      return video.play();
    })
    .then(_ => {
      // Video playback started ;)
    })
    .catch(e => {
      // Video playback failed ;(
    })
  }
</script>

播放的promise支持

目前,在Chrome, Firefox, Opera, and Safari浏览器中 HTMLMediaElement.play() 可以返回promise。Edge还在研发中。

警告

包含在<video>里面的<source> 会让 play() 的 promise 永远不会拒绝

比如<video src=”not-existing-video.mp4″>, 如果视频不存在,play() promise 会拒绝执行. 而<video><source src=”not-existing-video.mp4″ type=’video/mp4′></video>中 play() promise 不会拒绝。这种情况只会在没有可用的资源文件时触发。

Chromium Bug

1999年,已经是二十年前了!

原来,2009年已经10年前了

原来,1999年已经过去20年了

1999年,最火的大概就是那首《常回家看看》和《山路十八弯》了,可如今已经物是人非,除了蔡叔忙于做评委,其他人已经很少露面了。

那年春晚,小品女王高秀敏还没跟着本山大叔卖拐,而是和范伟一起出演小品《将心比心》,饰演小媳妇的黑妹明明很出戏,却再也没有登上春晚舞台。

那时候不止高秀敏,赵丽蓉也还在,牛群也还在说相声,赵本山和宋丹丹第一次春晚搭档,上演了《昨天今天明天》,那年的中老年偶像还是赵忠祥和倪萍。

那年,朴树说《我要去2000年》,萧亚轩唱起《最熟悉的陌生人》,而五月天、周杰伦都刚刚发行第一张专辑。

羽泉还在唱《最美》,谢霆锋还是个没结婚的小伙子,年少轻狂的谢霆锋还在犹豫的唱“别问最爱我的人,伤我有多深”,刘若英的一首《后来》传遍大街小巷,炙手可热的加油男孩的队长王俊凯那年刚刚出生……

那一年,央行开始在全国50多个城市,调查居民对物价的满意程度。60%的城里人觉得,嗯,物价还不错,自己挺满意。人民日报报道说,20块钱,足够家庭主妇们操持一顿“营养均衡、新鲜美味的午餐”。可在那时,浙江职工的平均年薪是10632元,算下来,工作一天,能挣出来两顿豪华午餐就不错了。而且,1999年首都北京的月平均工资也才1148元。

那年星巴克在北京国贸开了第一家店,一小杯拿铁要19块,就好比放在现在让你花150喝一杯咖啡,想想还真是滴血。

平均工资虽低,但有一家公司打出了年薪十万的价码,这家公司“致力于为全球商人提供基于Internet的商务服务”,在报纸上发了条招聘广告,那条招聘语如今看来仍振聋发聩,“If not now,When?If not me,Who?”

这个公司叫做阿里巴巴。


1999年,北漂创业失败的马云,回到杭州家中,召唤了“十八罗汉”,开始有了阿里巴巴。

马云1999年家中演讲视频

20年前,互联网还仅仅是个概念。北京大学成为国内首所有校园网的高校,但仍有20%的北大学生从来没上过网。

那时候,访问量已经破万的“网上超市”,居然没有卖出去一台电脑。专家们纷纷表示,网上购物前景不乐观,只不过是“水中月,镜中花”。

而现在,网上购物彻底改变了中国人的消费方式。中国人习惯了从网上买东西,也习惯了从网上卖东西。

如今的电商巨头京东,在那一年还是中关村里面一个卖电脑的小摊位:

1999年 京东“年会”视频

不过,在1999年,老一代的互联网企业已经初具规模了,搜狐的张朝阳刚刚被美国《时代周刊》选为年度风云人物。而网易和新浪马上就要去纳斯达克上市了。

28岁的马化腾,虽然IT创业多年,但是1999年他照着ICQ开发的OICQ聊天软件,还远远看不到前景。1999年最后一夜,腾讯的员工决定好好搞一次聚餐,结果一出门就被迎接千禧年的人潮堵在路上。而Pony没有参加,因为“千年虫”捣蛋,他自己一个人留在了公司改Bug。



1999年,还发生了很多事:

第一届新概念作文比赛举行,还是高中生的韩寒,一战成名。


那年5月,北约轰炸了南斯拉夫中国使馆,上万人去了美国大使馆游行。



7月,证甫决定在全国范围内取缔珐仑工,那年下半年,每天的新闻莲波都在报道。


10月,是国庆五十周年,那年的大阅兵一下子轰动了整个世界。


12月,澳门回归,一首“你可知Macao不是我真姓”那时候一直在脑子里回转


……


还有网友们经常调侃的“99年的事情,看来是瞒不住了”……现在来看,是“1999年的事情,再也回不去了”。

1999年,我很怀念它。

20年过去了,

曾经美好的记忆沉淀在心底,

但愿我们依旧怀着希望,

继续前行。



人类第一张在外星球的照片

这张照片原图是我从 NASA 官网找的。

因为据说用的是蔡司镜头,所以去NASA检索了半天,这是一组图片中的一张,并不一定是第一张。

NASA 原图大小,未压缩,右键保存,手机长按。

百度重回百度:陆奇出走 智能驾驶业务或被分拆

来源:腾讯《潜往》
作者:韩依民

种种细节表明,百度公司最重要的业务之一智能驾驶正在发生变化。

2017年3月,在百度整合自动驾驶事业部(L4)、智能汽车事业部(L3)、车联网业务(Car Life etc.)成立智能驾驶事业群组(IDG),且高级副总裁王劲不再担任L4总经理职务的敏感时刻,百度CEO李彦宏在接受彭博社采访时表示:当百度无人驾驶汽车业务走向成熟,且需要更多资金和合作伙伴时,可能考虑分拆该项业务。

李彦宏的该番表态为彼时百度无人车分拆的喧嚷传闻划下了句点,现在,这个时机似乎又已经到来。

据腾讯《潜望》了解,作为陆奇就职百度期间最为看重的两大人工智能业务之一,百度智能驾驶业务被重新列入分拆计划。

不仅仅是计划拆分智能驾驶业务,半年以来百度对于“主航道”的瘦身和重磅押注信息流都表明百度对陆奇路线的修正仍在继续。而取代这一路线的,是以李彦宏、马东敏夫妇为中心的公司整体对百度传统核心业务的亲近。

image

换句话说,百度在渐渐“回归”,曾面对的风险,也可能再次浮现。

QI的出走 QI的问题

在百度,为了表示亲切,员工一般不呼高管的职位,取而代之是他们的简称,比如李彦宏被称为Robin,王劲被称为劲,陆奇则是QI。

离开百度后,QI很快确定了自己的下一站。

8月15日,全球知名的创业加速器Y Combinator宣布正式进入中国,陆奇担任YC中国创始人及CEO,并任YC全球研究院院长。

公布新去向后,陆奇表示:考虑完全部因素,YC 是我唯一剩下的选择。

事实上,摆在陆奇面前的选择并不少。腾讯《潜望》获悉,在百度发出陆奇将离任集团总裁及COO职位的消息后,便有多家知名企业及投资基金萌生邀其加盟的想法,有百度高管对腾讯《潜望》透露,包括他在内的多位百度高管都接到了猎头对于陆奇的背景调查。而据腾讯《潜望》了解,陆奇与百度的竞业协议只到今年年底,明年初陆奇便可自由择业。

该位百度高管表示,“陆奇如果下一站还是去一家互联网公司,尤其是中国的互联网公司,那百度会被打脸的一塌糊涂,去一个投资公司还好说。”

与李彦宏的私人情谊曾是陆奇加入百度的重要原因,在陆奇加入百度第二天举行的媒体沟通会上,李彦宏如此解释为何能顺利邀得陆奇加入:“我们将近20年的交往,相互之间的信任还是起了非常大的作用。”

这份私人情谊最终让陆奇没有做出加入企业的决定,也让陆奇从百度的去职得以保持表面的和平。

在百度的对外公告中,陆奇离任是由于其“个人和家庭原因”,随后有报道称,陆奇家人的变故是促使陆奇转变生活重心的导火索,但据腾讯《潜望》了解,陆奇家人的变故发生在今年4月,而陆奇最早于今年2月份便向李彦宏提出离职,离职过程由其夫人专程回国操作,谈判进行了三个多月,直至5月18日当天,百度的最高管理层——Estaff成员才被告知相关信息,提前一晚获知此变动的百度高管唯有向海龙。

一位接近李彦宏的百度高层人士对腾讯《潜望》表示,陆奇的去职,本质上是因为其与李彦宏的核心价值观不一样,而对于百度商业化“度”的把控,是二者分歧的矛盾集中点。

就职百度期间,陆奇的工作狂风格曾获得李彦宏“工作极其玩命、上下有口皆碑”的评价,尽管是被邀请加入的职业经理人,但在一位百度高管的感受中,陆奇更加看重长远利益。曾与陆奇共事过的一位百度前员工如此记录:他很在意对与错,对的事情,再难也要去做;错的事情,诱惑再大也不能做。

而在该位百度高管的感受中,相较而言,李彦宏与陆奇有着完全不同的风格,李非常关注华尔街,经常表达“我们是一家上市公司,我们要维护我们投资人的利益,所以我们不能做这些事情。”

尽管在陆奇入职之初,李彦宏曾给予其“一人之下,万人之上”的权力,但一旦事涉具体营收,陆奇便会遭遇重重掣肘。据接近陆奇的人士透露,陆奇任内手机百度APP曾有一个很大的商业推广,需要一个很大的弹窗,对此陆奇觉得并不合适,而李彦宏绕过陆奇,直接与向海龙商议,并上线了该弹窗。

加入百度后,陆奇积极与华尔街对话,阐述百度在人工智能上投入的决心和可能获得的巨大回报前景,但随着去年9月中旬原新浪微博CFO余正钧加盟百度出任集团CFO后,陆奇与投资人沟通的职能便被逐渐代替。

上述百度高管向腾讯《潜望》表示,“新CFO过来,任务很明显:砍成本、涨股价,以前陆奇会很积极的去跟投资人聊,Herman(余正钧)来了之后这部分职能就被替代了,Herman聊的话题和陆奇聊的非常不一致。”

今年一月份,李彦宏在参加活动时公开表达从未说过“All in AI”,价值观上的分歧最终导致陆奇与李彦宏分道扬镳。

在很多百度员工眼中,陆奇代表着一种希望,但并非所有人都持有相同看法。据接近百度高层的人士向腾讯《潜望》透露,2017年初回归百度的李彦宏夫人马东敏便对陆奇的管理方式存在异议。

异议主要围绕陆奇的西方式管理文化,入职百度后,陆奇对具体业务负责人进行充分授权,但在复杂的中国市场环境下,一些决策如高价采买内容等,只能放在公司层面推动才可行。

同时,在人才储备方面,在陆奇所熟悉的美国公司治理结构中,集团层面与业务层面有比较明确的分工,但在中国的互联网公司治理结构下,公司决策大多仍集中于核心管理层,其他人主要做执行,人才储备上不够完整,因此陆奇原先的管理方法便可能遭遇水土不服。

去年2月,在宣布全资收购渡鸦科技的同时,百度内部通告将度秘团队升级为度秘事业部,景鲲任事业部总经理。这被认为是陆奇上任后的第一个大动作。在去年举行的首届百度AI开发者大会上,景鲲作为DuerOS的代言人于主论坛环节登场演讲,负责全面介绍DuerOS的开放路线。能够看出,在陆奇的排兵布阵中,具体业务负责人被赋予了更高的地位。

采取如此做法的出发点不难理解:离业务最近的人,做决定平均而言比离业务远的人要好。但上述人士表示,“景鲲当然是一位非常优秀的产品经理,但他从来没有运作过商业,突然间把他放在全局观的业务上,这是需要实践和经验的。”

正因如此,相对陆奇大刀阔斧的明面动作,置身幕后的马东敏也在调动资源进行布局。

过去一年多,马东敏通过其统领的百度战略投资从集团层面在内容与渠道两大端围绕DuerOS做了资源投入:增持了百度视频、纵横文学;战略投资了蜻蜓 FM、梨视频、人人视频等一系列内容产业公司;10月12日,百度还宣布战略领投网易云音乐新一轮融资,根据百度方面提供的信息,双方战略合作首先在智能硬件领域落地,网易云音乐将接入 DuerOS 平台。

除了内容产业外,在终端侧,百度先后投资了极米科技和小鱼在家等终端厂商,马东敏还亲自出面洽谈与华为等手机大厂的合作。在内容产业上的布局以及对终端厂商的投资,均意在为DuerOS铺设更好的发展环境。

百度在集团层面为DuerOS倾斜了大量资源,获得资源倾斜并非没有代价。

在接近百度高层的人士看来,以李彦宏重视华尔街、短期回报的风格来看,景鲲的压力会很大,“接下来会问他要两个:要不给用户数据,增长很快、活跃度很高;要不就给我挣钱。”

事实上,在10月31日举行的百度2018年第三季度财报电话会议上,李彦宏便在回答分析师提问时明确了DuerOS的商业化时间点:DuerOS的增长十分迅猛,预计这种高速增长要一直持续到2019年,商业化将在2020年开始。

显然,作为百度AI战略两大重要业务板块之一,DuerOS已经有了明确的商业化时间表,对于DuerOS团队而言,这意味着更高的增长期许以及随之而来更大的压力。

最近一年,百度投入了大量资源为DuerOS铺量。今年以来,百度推出了多款智能硬件新品:上半年陆续发布了带屏智能音箱小度在家(上市价599元)、自研智能音箱小度音箱(促销价89元);在11月1日举行的2018百度世界大会上,景鲲再次发布了新的智能音箱产品小度智能音箱Pro,以及自研的小度语音车载支架。

延续了上半年推出低价产品的策略,新推出的小度智能音箱Pro定价399元,而尝鲜价只需169元;小度语音车载支架售价99元,即日起将半价优惠49元开始公测。

如此激进的价格策略是为快速获取用户,做大规模,而根本目的则是为商业化做好准备。受促销及渠道成本增加的影响,百度二季度销售成本、综合开销及行政管理费用同比增长了54%;第三季度同比增长51%。

尽管目前DuerOS距离人工智能时代的安卓系统仍有很长的路要走,但在今年7月举行的第二届百度AI开发者大会上,百度发布了DuerOS 3.0版本,根据介绍,3.0版本的最大亮是开始尝试商业模式闭环,将逐渐向用户提供付费应用,在前六个月所有商业收益归开发者所有期限过后,平台将与开发者按比例分成。

一切已在加速。

主航道瘦身

陆奇任内,其曾为百度制定主航道与护城河的大战略。具体来讲,主航道指Feed流和人工智能两大业务,代表百度的未来;护城河是指能够让主航道业务航行更稳健的业务,起到护卫舰队的作用,是百度的现在。

而随着陆奇去职,百度的投入重心明显发生偏移——曾被列入主航道的智能驾驶业务今年的重心已经向实用转化调整。

智能驾驶之于百度意义复杂。

2017年3月1日,百度成立智能驾驶事业群组(IDG),由时任百度集团总裁和首席运营官陆奇兼任总经理。同年4月19日,百度发布Apollo自动驾驶开放平台。

陆奇任内,百度Apollo计划是其公开站台最多的百度业务。在2017年7月举行的第一届百度AI开发者大会上,Apollo与DuerOS被陆奇共同列为百度人工智能战略的两大端,是百度未来从人工智能战略中获取实际回报的重要抓手。在今年四月底陆奇离职传闻愈演愈烈之时,其还出现在北京车展现场与比亚迪董事长王传福会面,成为百度公关对外说明陆奇无职位变动的力证。

更早之前,王劲调动能力范围内重要资源押注百度无人车并推动业务分拆时,这一业务曾被寄予“再造一个百度”的希望。

然而,几经波折后,百度智能驾驶业务虽终被提上分拆日程,但其在内部的地位已经颇为微妙。

与两年前相比,自动驾驶已不再是大公司的保留地,如今,国内外均已出现自动驾驶领域的独角兽,这意味着优秀技术人才的选择面非常广泛,加之陆奇离任,百度吸引优质人才投身智能驾驶业务的难度相较早期大大提升。今年5月18日,百度宣布陆奇卸任集团总裁及COO职位,百度IDG也进行了同步调整:总经理李震宇转向张亚勤汇报。

根据百度Apollo官网上陈列的开放路线图,Apollo今年的目标是量产园区自动驾驶、明年的目标是量产限定区域城市自动驾驶、2020年的目标是量产简单城市道路自动驾驶、2021年的目标是高速和城市道路全网自动驾驶。

image

这与此前陆奇定下来的开放路线图存在差别,首先在时间上,如今的开放路线图时间整体延后了一年;另外,其关键词已由开放技术代码向量产转变。

image

在原有设想中,Apollo最终期待打造的是自动驾驶领域的安卓系统,实现上述目标必须攻克一系列技术难题。而目前,行业在L4的方向上暂时没有取得重大突破,百度同样没有找到特别好的解决方案。

同时,要实现预想中的安卓路径,需要搭建一个完美的生态,然而今年年初,负责百度与车企合作的百度副总裁邬学斌离职,但扩大朋友圈的需求仍驱使着Apollo商务团队每月必须完成一定的合作伙伴数量,在类似KPI的驱使下,部分Apollo的对外合作开始流于表面。据腾讯《潜望》了解,有公司在答应与Apollo合作后,除了Logo被列入展示外,双方无其他实质性接触。

压力当前,Apollo急于展示自己的实用可能,封闭园区成为主要落地场景,如重庆两江新区互联网产业园“百度-盼达自动驾驶示范园区”、湖南湘江新区智能系统测试区、百度与海淀公园合作打造的AI公园等。

更值得关注的变数则是可能进行的分拆。据了解,百度已在与外部潜在投资方进行接触,部分百度智能驾驶团队成员则不再被授予百度公司股票,而是相关业务期权。由于智能驾驶中美两地团队在股票转期权方面暂未达成一致,目前拆分计划尚未进入实质阶段。

随着拆分IDG的设想被重新提上议程,百度对智能驾驶业务的定位和投入已经有了更大不确定性。

转变并非毫无征兆。

今年5月21日,陆奇最后一次参加了百度内部的沟通会,一同参加了沟通会的李彦宏针对现场员工关于“决胜AI时代”战略如何进一步落地的提问时给出的答案是会推出一款名为“简单搜索”的App,让用户能够通过语音、图像等各种各样的输入方式表达需求,而在这背后,则是要做到对用户更加的理解与了解。“这样的创新我认为未来会不断地有,我们会一点一点的把它推向市场”。

同时,李彦宏还向百度员工分享了一个工作细节:“过去这半年的时间不少同学知道,我在亲自带信息流这个团队。每天早上八点半跟核心团队开会,每天都是如此。”

随后在百度2018年二季度财报电话会议上,余正钧向分析师们明确了百度的新策略:“将利润做到最大化而不是利润率最大化是一个更好的策略,我们自今年年初以来开始推行这种策略,只要我们认为还有更多的利润可以获取,我们就会增加流量获取的支出。”

根据百度2018年二、三季度财报披露的信息,百度在内容成本以及销售、总务和行政支出上同比均有大幅增长。

余正钧同时说明了接下来百度预算的两个主要支出方向:在流量获取方面的投入,以及在内容获取方面的投入。这些投入可能会增加百度在市场营销和内容成本上的支出。

相关财务策略已经在被执行。不久前,百度宣布杨紫成为百度APP代言人,借助明星的影响力扩大知名度及市场规模是常见做法,而成为百度APP新代言人的杨紫也非常敬业的在发布会现场演示如何通过百度APP智能小程序获得红包。

据介绍,“对所有新来的百度App用户,红包最少是8.88块,多的可以到880元。”

向用户发红包,这已经是互联网世界里并不新鲜的做法,在前几年的春节空包大战中,互联网巨头们通过向用户发放真金白银的红包,推动了支付等相关业务的快速增长,现在,百度希望复制同行们已被验证过的成功经验。对急于做大信息流业务的百度而言,如此直接的用户补贴手法虽然并不新鲜,但仍然值得一试。

可以看出,相比需要更长远投入的人工智能业务,无论是高层的关注点还是预算支出的方向,百度都在向能看到投入产出的信息流倾斜。

而在百度的最新路线下,来自搜索公司的力量依然被紧紧依赖。

回归搜索核心

被李彦宏寄予厚望的信息流业务始于2016年。

百度信息流业务由用户产品和商业变现两大部分构成,该年9月28日,百度信息流业务用户产品端的重要板块百家号正式上线,11月23日,时任百度副总裁陆复斌宣布,2017年百度将累计向内容生产者分成100亿,所有个人和机构内容生产者都可以入驻百家号,参与百亿分润。百度至此加入内容扶持大战。

2017年5月,入职百度五年的沈抖被晋升为百度公司副总裁,负责手百和Feed事业部,全面统领包括手机百度以及百度Feed流、百家号、好看视频、百度新闻、手机浏览器、Hao123等产品,成为百度信息流用户产品侧的掌门人。

对用户侧产品流量的商业变现由百度高级副总裁、搜索公司总裁向海龙领导的凤巢团队负责。

受2016年年初血友病吧被卖事件负面影响,出于规范管理公司TO B业务的考量,百度于该年上半年将公司旗下原不属于凤巢体系的贴吧、地图等TO B商业变现团队全部合并至凤巢,这支因突发事件而被动调整的团队成为了百度信息流商业变现端的最初执行团队。

与百家号上线初期的大规模推广相比,变现侧的启动显得低调许多。2016年年中,百度搜索公司CTO郑子斌与刚被合并至凤巢的2B端商业变现团队开会,同步了将要做信息流商业化的消息,围绕这项全新的业务,第一次会议重点讨论了接下来的策略,未设定具体KPI。

知情人士对腾讯《潜望》透露,从结果来看,信息流业务第一年的营收事实上很少。为了打开局面,在业务启动初期,百度从公司层面引导广告主投放凤巢推广的时候,搭配投放信息流产品,整合投放在初期占了信息流约50%收入。

2017年初开始,信息流设立单独产品线,广告主可以独立分配预算,与搜索推广完全是并列关系,目前,整合投放的收入对信息流业务而言已经微乎其微。

得益于2016年制定的策略,百度信息流业务在2017年取得了将近100亿的营收,尽管100亿的营收规模在百度2017年全年848亿的营收中占比有限,但对于一项刚刚完成从0到1的业务而言,这一成绩足以让百度高层看到重振的希望。

在可观的变现前景驱动下,凤巢系统的产品和研发被大量抽调,信息流商业变现团队规模从2017年年初开始迅速扩张,目前团队规模已经达到最初规模的20倍,超过200人。

知情人士告诉腾讯《潜望》,在2017年,能感觉到团队每个人都在“ALL in 原生”,负责信息流商业变现的员工经常能接到来自郑子斌或者向海龙下派的紧急项目——在一个月内上线一个新功能,这些新功能将影响到所有投放信息流的广告主。信息流业务加速推进的同时,陆奇也正在百度大施拳脚,然而在相关团队成员的回忆中,他们却几乎很难感受到来自陆奇的影响。

伴随信息流业务初具规模,百度凤巢体系随之拓展,初始只为提升百度搜索推广变现效率的凤巢,现已囊括包括搜索、信息流、网盟在内的百度主要变现方式,成为相当于阿里妈妈之于阿里这样的数字营销大中台,这一关乎百度钱袋子的中枢由向海龙牢牢把控。

一年多前,意图推动百度革新的陆奇曾在一次高管会议上对百度贴吧做出了“关、停、并、转”的建议——为了调配百度内部资源,陆奇触碰到了百度早期赖以成长的根基上。但如今,随着陆奇战略被修正,百度重点押注信息流,以向海龙为代表的百度传统核心势力搜索公司的地位愈加重要。

重入流量赌局

就职百度的一年零六个月内,陆奇为百度做出的最大贡献,一是对百度战略进行了全面梳理,二是通过新风会等方式重塑了百度内部的文化及沟通氛围。不过随着陆奇去职,其留下的痕迹正在被逐渐抹去。

根据多位百度员工提供的信息,在陆奇职位调整消息公布后的一两个月里,百度内部能够明显感觉到一股“去陆奇化”趋势:“陆奇来了有个新风会,现在就说全员沟通我们以前就有了;自动驾驶我们多少年前就开始做了;DuerOS也一样。但其实以前的全员沟通都是假的,陆奇来了才是真的;以前百度的人工智能战略不清晰,现在很清晰。”

陆奇曾向资本市场讲出了百度摆脱对搜索流量单一依赖的另一种可行路径,即ALL in AI,资本市场也曾以助推百度市值接近千亿美元回应了期待。

但随着百度抛弃陆奇路线回归传统,AI故事带来的资本溢价已经蒸发,这意味着怎样获得更大流量、使流量价值最大化再次成为百度的核心,其中暗藏的风险在于,一旦流量的增长未达预期,为了取得足够好看的商业成绩,对现有流量价值的过分榨取可能复现,类似做法曾在两三年前为百度带来了巨大的道德危机。

这是一场赌博,而百度显然已决心重回牌桌。

在百度修正陆奇路线的大背景下,信息流已成百度可供资本市场咀嚼的最重要商业故事。

据腾讯《潜望》了解,百度今年对信息流的营收目标已调高至300亿左右。上述知情人士对《潜望》表示,今年完成200亿左右的营收应该没什么问题,问题是如此巨大的增长规模在明年以及之后能否持续。

在该成员看来,2017年信息流业务业绩好,一方面是因为流量够,另外一方面是因为2016年年底的时候基本定了接下来的策略,2017年照章执行即可。“广告这个业务能预估到有多少流量过来,经过一些数据推导可以算能挤出多少水,我们就一直在朝那个方向努力。”

信息流业务得以持续高速增长的前提是流量也能够同步高速增长,沈抖负责的信息流用户产品承担着吸引更多流量的重责,正因如此,李彦宏从去年下半年开始为之投入大量精力。但是,互联网整体的流量红利已经消退,流量获取的难度和成本均已提升,从目前来看,百度信息流赖以依托的仍旧以百度APP为主,好看视频、百度新闻等应用与同类产品相比尚未显示出明显竞争优势。

为了最大程度转化流量价值,百度在今年推出了智能小程序,目前,最新版的百度APP已经上线了下拉进入小程序的入口。

事实上,类似的做法百度在几年前便有尝试,但此前直达号、轻应用等产品均未能取得理想效果。对此,沈抖的解释是,彼时很多开发者还没有意识到获客成本会这么高,因此时机不对,同时,当时的体验也没有做到现在这么好。

根据百度方面的介绍,百度的智能小程序最大特点是开放,代码开源,让所有合作伙伴可以在他们的自有APP上运行百度的小程序。

百度希望在用户使用百度想完成一个服务时,能够非常自然的进入到小程序中。然而一个依然回避不了的问题是:要让用户体验到百度智能小程序,大前提仍是提升用户使用百度APP的频率,而在移动应用已经如此丰富和发达的现在,百度APP是否还有成为超级入口的机会,充满疑问和挑战。

身肩重任的沈抖正试图为外界建立对百度APP的信心,在最近的一次采访中,他表示“巨型APP的出现在这个时代是成立的,尤其是小程序战略的顺序推进,使得我描述的愿景和目标更容易实现,将来大家可以真的生活在百度APP里面。”

入口问题对于百度并不是一个新问题,而百度在回归百度后,它似乎又一次站在了曾经的起跑线前。

冷知识阶段汇总(第1期)

生活

身份证前2位是省份,3到6位是区县(区县可能会有变更,所以你和你爸妈可能不一样),7-14位是出生年月日,15-16是发证派出所代号(你和你附近的人都是一样的),17是性别(奇男偶女),最后一位是用公式根据前17位计算的,计算结果是0-10,如果是10,就用x表示。

高铁没有E座,(按照国际通用排座方法)A和F是靠窗,C和D是过道,如果AC之间有座就是B,如果DF之间有座就是E。一排四座会没有B和E。

上行电梯扶手比电梯快,为了拉着你前倾,防止后仰摔倒。

(上条:国标要求扶手速度是电梯速度的100%~102%)

马路不是马走的路,因为全称是马卡丹路。

中国第一条马路在上海。

澳洲啤酒瓶是可以拧开的。

啤酒瓶盖有21个褶。

(根据实验)啤酒瓶盖最合理的是24个,能够有效密封并容易开启,但是他们发现24个褶容易卡住他们的机器,就改成了21个。

(上条:为什么不改进机器?)

mp3全称是 Moving Picture Experts Group Audio Layer 3,起初是一种文件格式的协议,并不是播放器。

美国整个20世纪消耗了44亿吨水泥,中国2011-2013三年消耗了66亿吨。

宫保鸡丁和宫爆鸡丁是两道不同的菜,宫保是人名。

蒙娜丽莎画像时24岁。

(根据联合国教科文组织的定义)一本现代意义上的图书最少要包含49页,不含封面封底。

玛丽莲梦露有11个脚趾。

Google 的意思是 googol,10的100次方。

央视天气预报背景音乐《渔舟唱晚》,从1984年开始使用。

北大校徽是鲁迅设计的,鲁迅还设计过国徽,印在某一版中华民国的大洋上。

鲁迅后期的月收入差不多要赶上蒋介石的工资了。

鹤顶红就是含有杂质呈红色的砒霜。

微信添加好友数量的上限是多少?不是5000个,而是一个特殊数字5040。这个数字有说法称是源自古希腊哲学家柏拉图的《理想国》,书中完美城邦人数正是5040人。

自然

玉米横截面的颗粒数是双数。

打喷嚏一定会闭上眼睛。

上海海拔最高的非人工地点只有100米。

(来自《梦溪笔谈》)鲤鱼有36片鳞,每片鳞边上都组成一个“十”字,36乘以10是360,古代360步为1里,所以这个鱼叫鲤鱼。

香芋和芋头没有关系。

青笋和芦笋没有关系,青笋就是莴苣,可以生吃。

日本豆腐是鸡蛋做的,和豆腐没关系。

人物

前总理李鹏女儿叫李小琳,前主席李先念女儿叫李小林,前作协主席巴金女儿叫李小林。

毛泽东的英文老师章含之,养父是民国教育部长章士钊,章士钊做过农大校长,章含之生父是皖军总司令陈调元的儿子,她是爸爸和交际花婚外孕生下的,章含之的丈夫是外交部长乔冠华,因支持四人帮文革后隔离审查,被习大大他爹习仲勋特赦。章含之和前夫有个女儿,女儿的其中一个前夫是陈凯歌。

陈凯歌曾是倪萍前男友,倪萍的初恋是郭达。

王刚(铁三角和珅)的前妻成方圆的妈妈徐琳琳初中最好的伙伴许燕吉的爸爸是《落花生》的作者许地山。

熊希龄(民国第一任总理,双清别墅以前的主人)的四弟的老婆的哥哥的女婿是沈从文。

熊希龄的七弟的老婆的姐姐的儿子是沈从文。

熊希龄的外甥的老婆的弟弟是沈从文。

熊希龄续弦朱氏的侄女朱濂嫁给了一个姓周的做妾,姓周的和原配有一个女儿,生了一个儿子易中天。

迪奥创始人迪奥是男同性恋,圣罗兰创始人伊夫圣罗兰曾是迪奥员工,也是男同性恋。

陈佩斯之所以叫佩斯,是因为他哥哥出生的时候他爸爸在布达佩斯演出,起名陈布达,他出生的时候就补上叫佩斯。

公元989年,范仲淹出生在河北正定一个叫做高平村的小村子里,950年后,这里发生了地道战。

东汉曹腾,家道中落,从小就被送到宫中做太监,陪皇太子读书。曹腾在宫中三十多年,侍四代天子,被升为正一品官。曹腾入宫后与一女吴氏结为“对食”夫妻,收养一子取名曹嵩。曹嵩155年7月18日生下长子曹操。

影视

《武林外传》当中,小郭服装最多,有18套。

历史

1865年,李鸿章在上海创办了江南制造局,诺基亚也诞生在这一年。

叶公好龙发生在河南叶县,属于春秋时期的楚国,主人公沈诸梁,姓芈,沈尹氏,被当今部分叶姓追为始祖。叶作为地名和姓氏在以前读 shè。

叶公好龙首次出现在刘向所著《新序》,刘向是刘邦亲弟弟的孙子的孙子。

每年六月初,各大网站和app可能无法更换头像,以及一些图片评论。

1966年提出废除高考的人是中信董事长,当时是北京四中学生,他的弟弟是中国航空总经理,他爸爸是海关总署署长,他爸和邓小平同一天结婚。

唐代以前的皇帝一般叫什么帝,比如汉武帝、魏孝文帝、隋炀帝。唐朝开始一般叫什么祖什么宗,比如宋徽宗、明成祖。什么帝是谥号,什么宗什么祖是庙号,唐以后之所以不叫谥号,是因为皇帝死后后人给他拍得马屁太多谥号太长了,不好记。比如明太祖朱元璋谥号是钦明启运峻德成功统天大孝高皇帝。

地理

景德镇名字来源于年号,景德元年这里出的瓷器被宋真宗赏识。

美国的名字America来自于意大利商人Amerigo,哥伦布发现新大陆以为是亚洲,Amerigo命名了新大陆。

法国国旗的图案是红、白、蓝三色条纹,最初设计时,国旗上的三条色带宽度完全相等,但是,当制成的国旗升到空中后,人们总觉得这三种颜色在国旗上所占的分量不相等,似乎白色的面积最大,蓝色的最小。为此,设计者们专门招集色彩专家进行分析,才发现这与色彩的膨胀感和收缩感有关。当把这三色的真实面积比例调整为蓝:白:红=30:33:37时,看上去反而相等了。

朝鲜和挪威之间只隔了一个国家俄罗斯。

语言

寿司是音译,刺身是训读汉字。

以前人吃生鱼片,因为去了皮就不认识是什么鱼了,所以用竹签带一块鱼皮在身上。

宋体字诞生在明朝,据说因为常常翻刻宋本得名。并在明朝传入日本,所以在日本称为明体。

牛轧糖,音译自 Nougat。

荷兰豆在中国叫“荷兰豆”,在荷兰叫“中国豆”,原因大概是在荷兰控制海上霸权的时代,荷兰人从东南亚带到了中国,后来中国大量种植并改进影响到了西方的叫法。

北京方言“嗝儿屁”意思是说一个人死了,是德语舶来词,原词krepieren。

蛤蜊,在山东和东北读作“gá la”是因为日殖时期的日语カラ(ke la)残留,而日语读音是来源于英文clam。

文化

为什么唐僧的白龙马是一匹白马?白马在佛教里面是重要的符号,传说东汉时期就是白马把佛经和佛像驼入洛阳建了白马寺,成为佛教进入中国的标志。另外据说佛祖释迦牟尼的坐骑也是一匹白马。更重要的是,以上都不重要,唐僧原型玄奘取经时骑的就是白马,作者沿用事实而已。

寡人,即为寡德之人,意为“在道德方面做得不足的人”。是古代君主、诸侯王对自己的谦称。中国古代讲究“以德治国”,“以德配天”,就是说君主、诸侯王的权位是上天赋予的,但上天只会把天下给有德的人,君主、诸侯王如果失德就会失去尊贵的权位,所以君主、诸侯王就谦称自己是“寡人”。

1997年修订的刑法将「流氓罪」删除了;最后一名流氓罪罪犯将于2020年重获自由。

多米诺骨牌是由意大利传教士多米诺把中国牌九带回去之后发展来的

其他

人在沟通的时候最关注的是面部表情,因为大家希望对方通过面部表情将内心情绪表达出来以便于理解。在东方传统审美当中,会觉得脸大的形象更易于亲近。所以葫芦娃脑袋很大,蛇精就是小脸。年画里的财神观音娃娃,都是肥头大耳的。

便利店711的英文是SEVEn-ELEVEn,并非全部大写。
image

欢迎关注

image

JavaSript模块规范:AMD规范与CMD规范介绍

JavaSript模块化

在了解AMD,CMD规范前,还是需要先来简单地了解下什么是模块化,模块化开发?

模块化是指在解决某一个复杂问题或者一系列的杂糅问题时,依照一种分类的思维把问题进行系统性的分解以之处理。模块化是一种处理复杂系统分解为代码结构更合理,可维护性更高的可管理的模块的方式。可以想象一个巨大的系统代码,被整合优化分割成逻辑性很强的模块时,对于软件是一种何等意义的存在。对于软件行业来说:解耦软件系统的复杂性,使得不管多么大的系统,也可以将管理,开发,维护变得“有理可循”。

还有一些对于模块化一些专业的定义为:模块化是软件系统的属性,这个系统被分解为一组高内聚,低耦合的模块。那么在理想状态下我们只需要完成自己部分的核心业务逻辑代码,其他方面的依赖可以通过直接加载被人已经写好模块进行使用即可。

首先,既然是模块化设计,那么作为一个模块化系统所必须的能力:

  1. 定义封装的模块。
  2. 定义新模块对其他模块的依赖。
  3. 可对其他模块的引入支持。

好了,思想有了,那么总要有点什么来建立一个模块化的规范制度吧,不然各式各样的模块加载方式只会将局搅得更为混乱。那么在JavaScript中出现了一些非传统模块开发方式的规范 CommonJS的模块规范,AMD(Asynchronous Module Definition),CMD(Common Module Definition)等。

AMD 与 RequireJS

AMD

Asynchronous Module Definition,用白话文讲就是异步模块定义,对于 JSer 来说,异步是再也熟悉不过的词了,所有的模块将被异步加载,模块加载不影响后面语句运行。所有依赖某些模块的语句均放置在回调函数中。

AMD规范定义了一个自由变量或者说是全局变量 define 的函数。

define( id?, dependencies?, factory );

第一个参数 id 为字符串类型,表示了模块标识,为可选参数。若不存在则模块标识应该默认定义为在加载器中被请求脚本的标识。如果存在,那么模块标识必须为顶层的或者一个绝对的标识。

第二个参数,dependencies ,是一个当前模块依赖的,已被模块定义的模块标识的数组字面量。

第三个参数,factory,是一个需要进行实例化的函数或者一个对象。

创建模块标识为 alpha 的模块,依赖于 require, export,和标识为 beta 的模块

define("alpha", [ "require", "exports", "beta" ], function( require, exports, beta ){
    export.verb = function(){
        return beta.verb();
        // or:
        return require("beta").verb();
    }
});

一个返回对象字面量的异步模块

define(["alpha"], function( alpha ){
    return {
        verb : function(){
            return alpha.verb() + 1 ;
        }
    }
});

无依赖模块可以直接使用对象字面量来定义

define( {
    add : function( x, y ){
        return x + y ;
    }
});

类似与 CommonJS 方式定义

define( function( require, exports, module){
    var a = require('a'),
          b = require('b');

    exports.action = function(){};
});

require();

在 AMD 规范中的 require 函数与一般的 CommonJS中的 require 不同。由于动态检测依赖关系使加载异步,对于基于回调的 require 需求强烈。

局部 与 全局 的require

局部的 require 需要在AMD模式中的 define 工厂函数中传入 require。

define( ['require'], function( require ){
  // ...
});

or:

define( function( require, exports, module ){
  // ...
});

局部的 require 需要其他特定的 API 来实现。

全局的 require 函数是唯一全局作用域下的变量,像 define一样。全局的 require 并不是规范要求的,但是如果实现全局的 require函数,那么其需要具有与局部 require 函数 一样的以下的限定:

  1. 模块标识视为绝对的,而不是相对的对应另一个模块标识。
  2. 只有在异步情况下,require的回调方式才被用来作为交互操作使用。因为他不可能在同步的情况下通过 require(String) 从顶层加载模块。

依赖相关的API会开始模块加载。如果需要有互操作的多个加载器,那么全局的 reqiure 应该被加载顶层模块来代替。

require(String)
define( function( require ){
    var a = require('a'); // 加载模块a
});

require(Array, Function)
define( function( require ){
    require( ['a', 'b'], function( a,b ){ // 加载模块a b 使用
        // 依赖 a b 模块的运行代码
    }); 
});

require.toUrl( Url )
define( function( require ){
    var temp = require.toUrl('./temp/a.html'); // 加载页面
});

RequireJS

RequireJS 是一个前端的模块化管理的工具库,遵循AMD规范,它的作者就是AMD规范的创始人 James Burke。所以说RequireJS是对AMD规范的阐述一点也不为过。

RequireJS 的基本思想为:通过一个函数来将所有所需要的或者说所依赖的模块实现装载进来,然后返回一个新的函数(模块),我们所有的关于新模块的业务代码都在这个函数内部操作,其内部也可无限制的使用已经加载进来的以来的模块。

<script data-main='scripts/main' src='scripts/require.js'></script>

那么scripts下的main.js则是指定的主代码脚本文件,所有的依赖模块代码文件都将从该文件开始异步加载进入执行。

defined用于定义模块,RequireJS要求每个模块均放在独立的文件之中。按照是否有依赖其他模块的情况分为独立模块和非独立模块。

  1. 独立模块,不依赖其他模块。直接定义:
define({
    method1: function(){},
    method2: function(){}
});

也等价于

define(function(){
    return{
        method1: function(){},
        method2: function(){}
    }
});
  1. 非独立模块,对其他模块有依赖。
define([ 'module1', 'module2' ], function(m1, m2){
    ...
});

或者:

define( function( require ){
    var m1 = require( 'module1' ),
          m2 = require( 'module2' );
    ...
});

简单看了一下RequireJS的实现方式,其 require 实现只不过是将 function 字符串然后提取 require 之后的模块名,将其放入依赖关系之中。

require 方法调用模块

在require进行调用模块时,其参数与define类似。

require( ['foo', 'bar'], function( foo, bar ){
    foo.func();
    bar.func();
});

在加载 foo 与 bar 两个模块之后执行回调函数实现具体过程。

当然还可以如之前的例子中的,在define定义模块内部进行require调用模块

define( function( require ){
    var m1 = require( 'module1' ),
          m2 = require( 'module2' );
    ...
});

define 和 require 这两个定义模块,调用模块的方法合称为AMD模式,定义模块清晰,不会污染全局变量,清楚的显示依赖关系。AMD模式可以用于浏览器环境并且允许非同步加载模块,也可以按需动态加载模块。

CMD 与 seaJS

CMD

在CMD中,一个模块就是一个文件,格式为:

define( factory );

全局函数define,用来定义模块。

参数 factory 可以是一个函数,也可以为对象或者字符串。

当 factory 为对象、字符串时,表示模块的接口就是该对象、字符串。

定义JSON数据模块:

define({ "foo": "bar" });

通过字符串定义模板模块:

define('this is {{data}}.');

factory 为函数的时候,表示模块的构造方法,执行构造方法便可以得到模块向外提供的接口。

define( function(require, exports, module) { 
    // 模块代码
});

define( id?, deps?, factory );

define也可以接受两个以上的参数,字符串id为模块标识,数组deps为模块依赖:

define( 'module', ['module1', 'module2'], function( require, exports, module ){
    // 模块代码
});

其与 AMD 规范用法不同。

require 是 factory 的第一个参数。

require( id );

接受模块标识作为唯一的参数,用来获取其他模块提供的接口:

define(function( require, exports ){
    var a = require('./a');
    a.doSomething();
});

require.async( id, callback? );

require是同步往下执行的,需要的异步加载模块可以使用 require.async 来进行加载:

define( function(require, exports, module) { 
    require.async('.a', function(a){
        a.doSomething();
    });
});

require.resolve( id )

可以使用模块内部的路径机制来返回模块路径,不会加载模块。

exports 是 factory 的第二个参数,用来向外提供模块接口。

define(function( require, exports ){
    exports.foo = 'bar'; // 向外提供的属性
    exports.do = function(){}; // 向外提供的方法
});

当然也可以使用 return 直接向外提供接口。

define(function( require, exports ){
    return{
        foo : 'bar', // 向外提供的属性
        do : function(){} // 向外提供的方法
    }
});

也可以简化为直接对象字面量的形式:

define({
    foo : 'bar', // 向外提供的属性
    do : function(){} // 向外提供的方法
});
```js

与nodeJS中一样需要注意的是,一下方式是错误的:

```js
define(function( require, exports ){
    exports = {
        foo : 'bar', // 向外提供的属性
        do : function(){} // 向外提供的方法
    }
});

需要这么做

define(function( require, exports, module ){
    module.exports = {
        foo : 'bar', // 向外提供的属性
        do : function(){} // 向外提供的方法
    }
});

传入的对象引用可以添加属性,一旦赋值一个新的对象,那么值钱传递进来的对象引用就会失效了。开始之初,exports 是作为 module.exports 的一个引用存在,一切行为只有在这个引用上 factory 才得以正常运行,赋值新的对象后就会断开引用,exports就只是一个新的对象引用,对于factory来说毫无意义,就会出错。

module 是factory的第三个参数,为一个对象,上面存储了一些与当前模块相关联的属性与方法。
– module.id 为模块的唯一标识。
– module.uri 根据模块系统的路径解析规则得到模块的绝对路径。
– module.dependencies 表示模块的依赖。
– module.exports 当前模块对外提供的接口。

seaJS

sea.js 核心特征:

  1. 遵循CMD规范,与NodeJS般的书写模块代码。
  2. 依赖自动加载,配置清晰简洁。

兼容 Chrome 3+,Firefox 2+,Safari 3.2+,Opera 10+,IE 5.5+。

seajs.use

用来在页面中加载一个或者多个模块

// 加载一个模块 
seajs.use('./a');
// 加载模块,加载完成时执行回调
seajs.use('./a',function(a){
    a.doSomething();
});
// 加载多个模块执行回调
seajs.use(['./a','./b'],function(a , b){
    a.doSomething();
    b.doSomething();
});

其define 与 require 使用方式基本就是CMD规范中的示例。

AMD 与 CMD 区别到底在哪里?

看了以上 AMD,requireJS 与 CMD, seaJS的简单介绍会有点感觉模糊,总感觉较为相似。因为像 requireJS 其并不是只是纯粹的AMD固有思想,其也是有CMD规范的思想,只不过是推荐 AMD规范方式而已, seaJS也是一样。

下面是玉伯对于 AMD 与 CMD 区别的解释:

AMD 是 RequireJS 在推广过程中对模块定义的规范化产出。

CMD 是 SeaJS 在推广过程中对模块定义的规范化产出。

类似的还有 CommonJS Modules/2.0 规范,是 BravoJS 在推广过程中对模块定义的规范化产出还有不少??

这些规范的目的都是为了 JavaScript 的模块化开发,特别是在浏览器端的。

目前这些规范的实现都能达成浏览器端模块化开发的目的。

区别:

  1. 对于依赖的模块,AMD 是提前执行,CMD 是延迟执行。不过 RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同)。CMD 推崇 as lazy as possible.
  2. CMD 推崇依赖就近,AMD 推崇依赖前置。看代码:
// CMD
define(function(require, exports, module) {
    var a = require('./a')
    a.doSomething()
    // 此处略去 100 行
    var b = require('./b') // 依赖可以就近书写
    b.doSomething()
    // ...
})

// AMD 默认推荐的是
define(['./a', './b'], function(a, b) { // 依赖必须一开始就写好
    a.doSomething()
    // 此处略去 100 行
    b.doSomething()
    // ...
})

虽然 AMD 也支持 CMD 的写法,同时还支持将 require 作为依赖项传递,但 RequireJS 的作者默认是最喜欢上面的写法,也是官方文档里默认的模块定义写法。

  1. AMD 的 API 默认是一个当多个用,CMD 的 API 严格区分,推崇职责单一。比如 AMD 里,require 分全局 require 和局部 require,都叫 require。CMD 里,没有全局 require,而是根据模块系统的完备性,提供 seajs.use 来实现模块系统的加载启动。CMD 里,每个 API 都简单纯粹。

  2. 还有一些细节差异,具体看这个规范的定义就好,就不多说了。
    另外,SeaJS 和 RequireJS 的差异,可以参考:https://github.com/seajs/seajs/issues/277

总结

本文主要是介绍了一下 AMD CMD的规范,顺便简单的讲述了一下 requireJS 与 seaJS。讲的较为笼统,下面的扩展阅读可以更好的帮助你理解模块化以及各个规范。

扩展阅读

原文链接:JavaSript模块规范 – AMD规范与CMD规范介绍

JavaScript的api设计原则

前言

本篇博文来自一次公司内部的前端分享,从多个方面讨论了在设计接口时遵循的原则,总共包含了七个大块。系卤煮自己总结的一些经验和教训。本篇博文同时也参考了其他一些文章,相关地址会在后面贴出来。很难做到详尽充实,如果有好的建议或者不对的地方,还望不吝赐教斧正。

一 接口的流畅性

好的接口是流畅易懂的,他主要体现如下几个方面:

1 简单

操作某个元素的css属性,下面是原生的方法:

document.querySelector('#id').style.color = 'red';

封装之后

function a(selector, color) {
  document.querySelector(selector).style.color = color
}
a('#a', 'red');

从几十个字母长长的一行到简简单单的一个函数调用,体现了api设计原则之一:简单易用。

2 可阅读性

a(‘#a’, ‘red’)是个好函数,帮助我们简单实用地改变某个元素,但问题来了,如果第一次使用该函数的人来说会比较困惑,a函数是啥函数,没有人告诉他。开发接口有必要知道一点,大多数人都是懒惰的(包括卤煮自己),从颜色赋值这个函数来说,虽然少写了代码,但是增加了单词字母的个数,使得它不再好记。每次做这件事情的时候都需要有映射关系: a—->color. 如果是简单的几个api倒是无所谓,但是通常一套框架都有几十甚至上百的api,映射成本增加会使得程序员哥哥崩溃。 我们需要的就是使得接口名称有意义,下面我们改写一下a函数:

function letSomeElementChangeColor(selector, color) {
  document.querySelectorAll(selector, color).style.color = color; }

letSomeElementChangeColor相对于a来说被赋予了现实语言上的意义,任何人都不需要看说明也能知道它的功能。

3 减少记忆成本

我们刚刚的函数太长了,letSomeElementChangeColor虽然减少了映射成本,有了语言上的意义,但是毫无疑问增加了记忆成本。要知道,包括学霸在内,任何人都不喜欢背单词。不仅仅在此处,原生获取dom的api也同样有这个问题: document.getElementsByClassName; document.getElementsByName; document.querySelectorAll;这些api给人的感觉就是单词太长了,虽然他给出的意义是很清晰,然而这种做法是建立在牺牲简易性和简忆性的基础上进行的。于是我们又再次改写这个之前函数

function setColor(selector, color) {
 xxxxxxxxxxxx
}

在语言意义不做大的变化前提下,缩减函数名称。使得它易读易记易用。

4 可延伸

所谓延伸就是指函数的使用像流水一样按照书写的顺序执行形成执行链条:

document.getElementById('id').style.color = 'red';
document.getElementById('id').style.fontSize = '12px';
document.getElementById('id').style.backgourdColor = 'pink';

如果我们需要实现像以上有强关联性的业务时,用我们之前的之前的方法是再次封装两个函数 setFontSize, setbackgroundColor; 然后执行它们 setColor(‘id’, ‘red’);setFontSiez(‘id’, ’12px’); setbackgroundColor(‘id’, ‘pink’); 显然,这样的做法没有懒出境界来;id元素每次都需要重新获取,影响性能,失败;每次都需要添加新的方法,失败; 每次还要调用这些方法,还是失败。下面我们将其改写为可以延伸的函数 首先将获取id方法封装成对象,然后再对象的每个方法中返回这个对象:

function getElement(selector) {
  this.style = document.querySelecotrAll(selector).style;
}

getElement.prototype.color = function(color) {
  this.style.color = color;
  return this;
}
getElement.prototype.background = function(bg) {
  this.style.backgroundColor = bg;
  return this;
}
getElement.prototype.fontSize = function(size) {
  this.style.fontSize = size;
  return this;
}
//调用
var el = new getElement('#id')
el.color('red').background('pink').fontSize('12px');

简单、流畅、易读,它们看起来就像行云流水一样,即在代码性能上得到了提升优化,又在视觉上悦目。后面我们会在参数里面讲到如何继续优化。

所以,大家都比较喜欢用jquery的api,虽然一个$符号并不代表任何现实意义,但简单的符号有利于我们的使用。它体现了以上的多种原则,简单,易读,易记,链式写法,多参处理。

nightmare:

document.getElementById('id').style.color = 'red';
document.getElementById('id').style.fontSize = '12px';
document.getElementById('id').style.backgourdColor = 'pink';

dream:

$('id').css({color:'red', fontSize:'12px', backgroundColor:'pink'})

二 一致性

1 接口的一致性

相关的接口保持一致的风格,一整套 API 如果传递一种熟悉和舒适的感觉,会大大减轻开发者对新工具的适应性。 命名这点事:既要短,又要自描述,最重要的是保持一致性 “在计算机科学界只有两件头疼的事:缓存失效和命名问题” — Phil Karlton 选择一个你喜欢的措辞,然后持续使用。选择一种风格,然后保持这种风格。

Nightmare:

setColor,
letBackGround
changefontSize
makedisplay

dream:

setColor;
setBackground;
setFontSize
set.........

尽量地保持代码风格和命名风格,使别人读你的代码像是阅读同一个人写的文章一样。

三 参数的处理

1 参数的类型

判断参数的类型为你的程序提供稳定的保障

//我们规定,color接受字符串类型
function setColor(color) {
  if(typeof color !== 'string') return;
dosomething
}

2 使用json方式传参

使用json的方式传值很多好处,它可以给参数命名,可以忽略参数的具体位置,可以给参数默认值等等 比如下面这种糟糕的情况:

function fn(param1, param2...............paramN)

你必须对应地把每一个参数按照顺序传入,否则你的方法就会偏离你预期去执行,正确的方法是下面的做法。

function fn(json) {
//为必须的参数设置默认值
   var default = extend({
    param: 'default',
    param1: 'default'
    ......
   },json)
}

这段函数代码,即便你不传任何参数进来,他也会预期运行。因为在声明的时候,你会根据具体的业务预先决定参数的缺省值。

四 可扩展性

软件设计最重要的原则之一:永远不修改接口,而是去扩展它!可扩展性同时会要求接口的职责单一,多职责的接口很难扩展。 举个栗子:

//需要同时改变某个元素的字体和背景
// Nightmare:
function set(selector, color) {
  document.querySelectroAll(selector).style.color = color;
  document.querySelectroAll(selector).style.backgroundColor = color;
}

//无法扩展改函数,如果需要再次改变字体的大小的话,只能修改此函数,在函数后面填加改变字体大小的代码

//Dream
function set(selector, color) {
  var el = document.querySelectroAll(selector);
  el.style.color = color;
  el.style.backgroundColor = color;
  return el;
}

//需要设置字体、背景颜色和大小
function setAgain (selector, color, px) {
  var el = set(selector, color)
  el.style.fontSize = px;
  return el;
}

以上只是简单的添加颜色,业务复杂而代码又不是你写的时候,你就必须去阅读之前的代码再修改它,显然是不符合开放-封闭原则的。修改后的function是返回了元素对象,使得下次需要改变时再次得到返回值做处理。

2 this的运用

可扩展性还包括对this的以及call和apply方法的灵活运用:

function sayBonjour() {
  alert(this.a)
}

obj.a = 1;
obj.say = sayBonjour;
obj.say();//1
//or
sayBonjour.call||apply(obj);//1

五 对错误的处理

1 预见错误

可以用 类型检测 typeof 或者try…catch。 typeof 会强制检测对象不抛出错误,对于未定义的变量尤其有用。

2 抛出错误

大多数开发者不希望出错了还需要自己去找带对应得代码,最好方式是直接在console中输出,告诉用户发生了什么事情。我们可以用到浏览器为我们提供的api输出这些信息:console.log/warn/error。你还可以为自己的程序留些后路: try…catch。

function error (a) {
  if(typeof a !== 'string') {
    console.error('param a must be type of string')
  }
}

function error() {
  try {
    // some code excucete here maybe throw wrong
  }catch(ex) {
    console.wran(ex);
  }
}

六 可预见性

可预见性味程序接口提供健壮性,为保证你的代码顺利执行,必须为它考虑到非正常预期的情况。我们看下不可以预见的代码和可预见的代码的区别用之前的setColor

//nighware
function set(selector, color) {
  document.getElementById(selector).style.color = color;
}

//dream
zepto.init = function(selector, context) {
  var dom
  // If nothing given, return an empty Zepto collection
  if (!selector) return zepto.Z()
  // Optimize for string selectors
  else if (typeof selector == 'string') {
    selector = selector.trim()
    // If it's a html fragment, create nodes from it
    // Note: In both Chrome 21 and Firefox 15, DOM error 12
    // is thrown if the fragment doesn't begin with <
    if (selector[0] == '<' && fragmentRE.test(selector))
      dom = zepto.fragment(selector, RegExp.$1, context), selector = null
    // If there's a context, create a collection on that context first, and select
    // nodes from there
    else if (context !== undefined) return $(context).find(selector)
    // If it's a CSS selector, use it to select nodes.
    else dom = zepto.qsa(document, selector)
  }
  // If a function is given, call it when the DOM is ready
  else if (isFunction(selector)) return $(document).ready(selector)
  // If a Zepto collection is given, just return it
  else if (zepto.isZ(selector)) return selector
  else {
    // normalize array if an array of nodes is given
    if (isArray(selector)) dom = compact(selector)
    // Wrap DOM nodes.
    else if (isObject(selector))
      dom = [selector], selector = null
    // If it's a html fragment, create nodes from it
    else if (fragmentRE.test(selector))
      dom = zepto.fragment(selector.trim(), RegExp.$1, context), selector = null
    // If there's a context, create a collection on that context first, and select
    // nodes from there
    else if (context !== undefined) return $(context).find(selector)
    // And last but no least, if it's a CSS selector, use it to select nodes.
    else dom = zepto.qsa(document, selector)
  }
  // create a new Zepto collection from the nodes found
  return zepto.Z(dom, selector)
}

以上是zepto的源码,可以看见,作者在预见传入的参数时做了很多的处理。其实可预见性是为程序提供了若干的入口,无非是一些逻辑判断而已。zepto在这里使用了很多的是非判断,这样做的好处当然是代码比之前更健壮,但同时导致了代码的冗长,不适合阅读。总之,可预见性真正需要你做的事多写一些对位置实物的参数。把外部的检测改为内部检测。是的使用的人用起来舒心放心开心。呐!做人嘛最重要的就是海森啦。

七 注释和文档的可读性

一个最好的接口是不需要文档我们也会使用它,但是往往接口量一多和业务增加,接口使用起来也会有些费劲。所以接口文档和注释是需要认真书写的。注释遵循简单扼要地原则,给多年后的自己也给后来者看:

//注释接口,为了演示PPT用
function commentary() {
  //如果你定义一个没有字面意义的变量时,最好为它写上注释:a:没用的变量,可以删除
  var a;

  //在关键和有歧义的地方写上注释,犹如画龙点睛:路由到hash界面后将所有的数据清空结束函数
  return go.Navigate('hash', function(){
    data.clear();
  });
}

最后

推荐markdown语法书写API文档,github御用文档编写语法。简单、快速,代码高亮、话不多说上图

卤煮在此也推荐几个在线编辑的网站。诸君可自行前往练习使用。

https://www.zybuluo.com/mdeditor

http://mahua.jser.me/

参考博文:JavaScript API 设计原则

原文链接:javascript的api设计原则


TOP