今天的比赛真精彩,难以言喻。库里再登巅峰,双少无力蔽日。虎扑服务器都被搞挂了。发篇微博纪念下。


小型项目还是 Backbone 用起来比较舒心,所以写着写着又开始用它了。这次还用到 ES2015 和 Babel,于是疑问就来了:怎么在 ES2015 下使用 Backbone 呢?
Backbone 自带 .extend() 方法,在早期的蛮荒岁月可以帮我们方便的创建各种子类。
var MyView = Backbone.View.extend({
events: {
},
tagName: 'div',
initialize: function () {
}
});
以前这样确实很方便,代码也很好读。但是 ES2015 引入了新的 Class 规范,写出来是这样婶儿的:
class MyView extends Backbone.View {
// 其实不能这样写
tagName: 'div'
events: {
}
// 上面这样写是不对的
constructor() {
}
}
这个时候问题来了。ES2015 不支持直接声明类属性,也就是上面代码中的 tagName 和 events,是不能这样写的。因为它实际上只是重新包装了原型继承那一套东西,所以上面的代码实际上等效于:
MyView = function () {
}
MyView.prototype = new Backbone.View();
MyView.prototype.tagName = 'div';
MyView.prototype.events = {
};
这样的结果,不同实例间实际在共享方法和属性,包括 tagName 和 events。如果是简单对象,比如字符串和数字,用 = 赋值还好;如果是复杂对象,比如数组,就很容易出问题。
Backbone 1.0 之前也会有这个问题,但是忘记从哪个版本开始就修复了。
想要在 ES2015 中继续 Backbone 当然也是可以的。目前看来有三种方式:
_.result(),所以属性和方法的效果是一样的。MyClass.prototype.someProp = 'value'; 。我个人比较喜欢第三种方法,因为它更接近之前的写法,而且结合 Babel、Webpack 等编译打包工具,也可以做到私有。
class MyView extends Backbone.View {
constructor(init) {
super(_.extend(init, {
events: {
},
tageName: 'div'
});
}
}
ES2016 中引入了很重要的 Decorators 概念,顾名思义,它会显式的告诉运行时下面是些什么东西,那么运行时自然也就可以按照对应的规则去处理。
@props({
tagName: 'div',
events: {
}
})
class MyView extends Backbone.View {
constructor(init) {
super(init);
}
}
本篇内容主要参考自以下两篇:

Promise 在处理异步的时候是个很好的选择,可以减少嵌套层次,让代码更好读,逻辑更清晰。ES6 将其加入规范,jQuery 3.0 也修改实现向规范靠拢(3.0 发布公告)。一些新增元素比如 .fetch() 原生就 “thenable”,不过大多数以往的 API 还要依赖回调,这个时候,我们只要将它们重新封装,就能避开嵌套陷阱,享受 Promise 带来的愉悦体验。
先来看下 Promise 的一般用法。
// 声明 Promise 对象
var p = new Promise(function (resolve, reject) {
// 不管啥时候,该执行then了,就调用 resolve
setTimeout(function () {
resolve(1);
}, 5000);
// 或者不管啥问题,就调用 reject
if (somethingWrong) {
reject('2');
}
});
// 使用 Promise 对象
p.then(function (num) {
// 对应上面的 resolve
console.log(num); // 1
}, function (num) {
// 对应上面的 reject
console.log(num); // 2
});
Promise 的驱动模型并不复杂:任何操作,假定它只有两个结果,成功或者失败。那么只需要在合适的时间调用合适的程序,进入合适的后续步骤即可。.then() 顾名思义,就是下一步的意思,当前面的 Promise 有了结果——即调用 resolve 或者 reject——之后,就启动对应的处理函数。
Promise 实例创建后就会开始执行,判定结果需要我们自己来,比如加载成功,或者满足某个条件,等等。通过串联 .then() 则可以完成一系列操作。每次调用 .then() 都会创建一个新的 Promise 实例,它会静静等待前面的实例状态改变后再开始执行。
接下来开始封装。思路很简单,FileReader 除了提供各种 read 方法,还有几个事件钩子,其中 onerror 和 onload 很明显可以作为判断任务是否完成的依据。加载成功的话,就需要用到文件内容,所以将文件或文件内容传递到下一步也十分必要。
最后完成的代码如下:
function reader (file, options) {
options = options || {};
return new Promise(function (resolve, reject) {
let reader = new FileReader();
reader.onload = function () {
resolve(reader);
};
reader.onerror = reject;
if (options.accept && !new RegExp(options.accept).test(file.type)) {
reject({
code: 1,
msg: 'wrong file type'
});
}
if (!file.type || /^text\//i.test(file.type)) {
reader.readAsText(file);
} else {
reader.readAsDataURL(file);
}
});
}
为了能真正派上用场,里面还有一些验证文件类型的操作,不过跟本文主旨无关,略过不表。这段代码的核心是创建一个 Promise 对象,等待 FileReader 读取完成后调用 resolve 方法,或者出现问题时调用 reject 方法。
Github Gist 里也放了一份。
接下来就可以在项目中使用了:
reader(file)
.then(function (reader) {
console.log(reader.result);
})
.catch(function (error) {
console.log(error);
});
.then() 支持两个参数,第一个在 Promise 成功时启动,第二个自然在失败时启动。用 .catch() 可以实现同样地效果。Promise 的好处除了可读性更佳以外,返回的 Promise 对象还可以任意传递,继续进行链式调用,有很大想象空间。
.then()于是我们不妨串联更多操作(本来想写个断点续传的,回头再说吧):
reader(file)
.then(function (reader) {
return new Promise(function (resolve, reject) {
// 就随便暂停个5秒吧……
setTimeout(function () {
resolve(reader.result);
}, 5000);
});
})
.then(function (content) {
console.log(content);
});
这其实是我第一次用 Promise,上次翻译 jQuery 发布公告的时候我它也只是一知半解,对它的解读也糊里糊涂。我很喜欢在业余项目中学习使用新技术,最近开发 Chrome 插件的时候就尝试了一把,感觉不错。使用过程比我想象的复杂也比我想象的简单,这套设计很棒,能解决不少实际问题,也给了我很大启发,将来我应该会把很多地方的实现都做这样的修改。
除去第一段的各个链接,还有一些文章值得一看。

以前写的有问题,编辑下。
研究了半天,原来 Chrome 扩展不支持文件存储,只能使用 chrome.storage 保存持久化数据。
提供两种形式,一种可以在浏览器间自动同步,适用登录用户保存设置,比如微博的“眼不见心不烦”,chrome.storage.sync,提供一个 key 8K,最大512个 key,总数据量100K(即不可能512个 key 都装满)的存储。这个方式对读写频率也有限制,想想也好理解,比精要往 Google 的云同步嘛。
另外一种则更接近平时用的 localStorage,叫 chrome.storage.local。它的限制很少,只要总量不大于5M即可(可以通过设置 unlimitedStorage: true 来取消上限)。
使用的时候,需要在 manifest.json 里声明权限
{
"permissions": [
"storage"
]
}
这样的话就比较符合我的预期了,用户可以任意保存字幕到本地,太多了自己删掉就是,如果希望云同步就付费或者看广告。

开发 Chrome 插件时,如果使用 popup.html,调试时反复审查元素很麻烦。此时可以用
chrome-extension://插件id/popup.html
在新标签页打开,就简单多了。

好吧,春节期间继续为公司把官网的响应式改好了。原本计划的东西都没做……
今年特别忙,一直上班到现在。终于把需要弄的都搞完了,可以放松一下。明天开始过年,今年变故比较多,原本计划的去这儿去那儿就算了,在家歇歇,放松放松。
不过还是想干点正事儿,这几周都在赶进度做测试,业余项目弄得比较少,博客也没怎么写。未来七天都在家,没有出远门的计划,就弄一弄吧。
差不多了,希望能完成。
之前可以这样写
$('[href=#hash]')
现在必须这样写
$('[href="#hash"]')
其它属性选择器,如 attr^="#value" 等,也一样。

原创不够,译文来凑。
跟上篇一样是编译,不准备逐字翻。比如,我会把“we”译成“jQuery官方团队”,或者“他们”。
正文开始。
(初译版,待校正)
歪果仁也要双喜临门,于是 jQuery 官方团队选在 jQuery 面世10周年之际发布 3.0 beta。大家还记得上周发布的1.x和2.x小版本更新吧,他们日后会继续维护这俩分支,一段时间,当然只改bug。因为3.0才是未来嘛!
需要支持IE6-8的可怜虫请继续使用1.12分支上的最新版。

看过 alpha 发布公告的同学可能还记得,他们起初准备同时发布3.0和“3.0兼容版”,适配老浏览器。但是现在他们想通了。微软今年1月12日宣布放弃IE8910,jQuery 会保守一些,不过至少不打算支持IE8,所以就放弃所谓的兼容版,以后就只有一个版本了。
尽管大版本号发生变化,jQuery 团队仍然认为升级不会太麻烦。大变化是有,不过影响应该没有很大,而且他们还开发了3.0专用迁移插件,可以帮助我们找到代码中的兼容性问题。所以,请尽早使用新版本,并及时将体验反馈给他们,这样才能让jQuery 越来越好。
你可以直接通过CDN使用:
https://code.jquery.com/jquery-3.0.0-beta1.js
https://code.jquery.com/jquery-3.0.0-beta1.min.js
或者用NPM安装到本地
npm install jquery@3.0.0-beta1
接下来就是需要关注的新功能、升级、以及 Bug 修正了。完整列表见于 Github。
.show() 和 .hide()刚启动3.0的时候,他们尝试将这两个方法修改为“删除行内 display:none 样式”(.show())和 “增加行内 display:none 样式”(.hide())。这样可以极大的简化实现所需的代码,并且显著改善性能(计算量大幅下降了嘛)。但是,这给广大用户带来了不小的麻烦,因为移除 display:none 很多时候并不能让元素显示出来,比如有其它CSS将它置为隐藏。最终 jQuery 团队不得不承认没有办法完成期望中的简化。
于是他们放弃了这次尝试。不过,即便如此,他们还是想办法改善了隐藏大量元素时的性能。
.data() 的注意事项为了兼容 HTML5 dataset 规范,jQuery 团队升级了 .data() 实现。如今所有的 key 都会从短线连接(a-bc-de)转换成驼峰式(aBcDe),数字不再转换。于是,“foo-bar”转换后和“fooBar”是一样的,但“foo-42”和“foo42”就不一样。当用户直接使用 .data() 取所有数据时,就需要注意这个区别,尤其不要再误用 .data('foo42') 取代 .data('foo-42')。
Promise 我用的比较少,看到的文档也不多,不太清楚里面的几个名词怎么翻译,所以我尽量用括号备注。
jQuery.Deferred 得到升级,兼容 Promises/A+ 和 ES2015 Promises,并且已经通过 Promises/A+ Compliance Test Suite 认证。这意味着 .then() 的使用机制发生了非常显著的变动:
.then() 回调函数里抛出的异常,会成为失败(rejection)处理函数的参数。之前,异常会冒泡,中断函数执行,并永久性锁死上下级 Deferred 对象。.then() 返回的 Deferred 对象,如果它的回调函数抛出异常,将会调用失败(rejection)处理函数,并作为参数传进去;如果返回其它不能继续 .then() 的对象,就会调用成功(fulfillment)处理函数,返回值也作为参数传进去。以前,失败处理函数返回任何值都会将其置为失败。以下代码演示当上级 Deferred 触发 rejected 时,下级调用失败回调函数之后的结果:
var parent = jQuery.Deferred();
var child = parent.then( null, function() {
return "bar";
});
var callback = function( state ) {
return function( value ) {
console.log( state, value );
throw new Error( "baz" );
};
};
var grandchildren = [
child.then( callback( "fulfilled" ), callback( "rejected" ) ),
child.then( callback( "fulfilled" ), callback( "rejected" ) )
];
parent.reject( "foo" );
console.log( "parent resolved" );
在 jQuery 3.0 中,会先输出“parent resolved”,然后再执行回调函数;然后下级 Deferred 进入失败状态,执行函数,返回“bar”;“bar”被转化为三级 Deferred 的成功,于是输出“fulfilled bar”;接着,抛出错误“baz”,导致三级函数进入错误处理;最后,输出“rejected baz”。如果是之前的版本,下级 Deferred 会认为上级 Deferred 失败,进入错误处理,输出“rejected bar”;并且在未捕获的错误“baz”被抛出后,整个进程立刻被终止;此时,由于三级函数未处理完,“parent resolved”也不会输出。
捕获异常不仅对在浏览器里调试有帮助,在失败后的回调函数中处理它们,也使得代码更加直观合理。请谨记,这也意味着使用 Promise 模式的时候,要至少设置一个函数处理失败。不然的话,所有错误都会被忽略掉。
如果你还想使用以前的代码,可以用 .pipe() 函数替换 .then()。后者虽然已经被标记为不建议使用,但它接口一样,而且会暂时延续之前的逻辑。
我们还开发了辅助调试调试 Promises/A+ 的工具。如果你觉得有些错误好像没触发出来,可以试用之。
jQuery.when 函数也升级了,现在可以传入任何支持 .then() 的对象,包括原生 Promise 对象。
https://github.com/jquery/jquery/issues/1722
https://github.com/jquery/jquery/issues/2102
.catch() 方法Promise 对象增加 .catch()方法, 作为 .then(null, fn) 的别名,专门处理失败。
jQuery.ajax 的 Deferred 同名方法的特殊用法jqXHR 是 Promise 对象,同时也有一些专有方法,比如 .abort(),用于取消请求。
现在,越来越多的开发者已经在异步中(如AJAX)使用 Promise 模式,如此一来,jQuery.ajax 返回的对象再包含特殊用法就不合时宜了。
success, error, complete(这些将被移除)
done, fail, always(这些应该会保留)
需要注意的是,那些继续存在的回调函数不会有任何变化,只有 Promise 的方法会受影响。
有时候我们难免会想:“window 的偏移值(window.offset)是多少?”等你回过味儿来你会发现这个问题其实挺蠢的,window 怎么会有偏移呢?
过去,jQuery 面对这种情况,从来不会抛出错误,而是尽量返回有意义的值,比如刚才那个问题,就返回 { top: 0, left: 0 }。3.0之后,他们尝试不再什么乱七八糟的代码都兼容,而是直接抛出错误,让用户不要忽略这些问题。大家可以试用 beta 版,看看你的代码有没有“参数无效”之类的错误。
.width(),.height(),.css('width'),.css('height')都将返回小数之前,jQuery 取宽高的时候会返回四舍五入之后的整数值。有些浏览器可以返回次像素,比如 IE 和 Firefox,有些用户需要这些精细的数据来调整布局。jQuery 官方团队认为这项变化对大多数人没有影响,不过如果你受此困扰,也请告知他们。
jQuery 1.8之后弃用的 .load,.unload,.error 方法被正式移除。以后都请使用 .on() 注册事件侦听器。
requestAnimationFrame 改善动画效果新版 jQuery 在支持 requestAnimationFrame 的平台上会自动使用它来改善性能。除去 IE9 和 低于4.4的Android,都可以藉此让动画效果更平滑,占用更少的CPU时间,降低移动设备的电力损耗。
其实 jQuery 团队几年前就曾尝试使用这项技术,但是当时遇到很严重的兼容性问题,以致于不得不放弃。如今他们采用新策略,当浏览器 tab 不显示时挂起动画,这样就可以规避大部分问题。不过这样一来,那些必须依赖动画全局实时播放的功能就无法实现了。
.unwrap( selector )3.0之前,.unwrap() 方法不接受任何参数。如今,用户可以通过传入选择器来移除指定的外部容器。
jQuery.fn.domManip 不能再使用了1.12 和 2.2 版本把 jQuery.dir,jQuery.sibling,jQuery.buildFragment,jQuery.access,和 jQuery.swap 都修改为私有函数。现在,jQuery.fn.domManip 也一样。它们未来都只允许内部使用,不会被载入使用文档。官方团队认为这样做可以避免用户困惑。
https://github.com/jquery/jquery/pull/2182
https://github.com/jquery/jquery/issues/2224
https://github.com/jquery/jquery/issues/2225
拜 Paul Irish 在 Google 的工作所赐,新版 jQuery 可以躲过一些坑,比如前文提到的 :visible,通过减少冗余代码,获得了17倍的速度提升!
但用户仍需小心,即便经过优化,:visible 和 :hidden 选择器仍然会消耗大量的系统资源,因为它们需要浏览器检查元素是否显示在页面上。在最坏的情况下,有可能需要重新计算全部 CSS 样式和页面布局!当然也不是说不要用(不然还写它做甚),只是记得测试一下,看有没有因此导致性能问题。
这项工作他们 1.12/2.2 时就完成了,只不过拿到这里来说。

本文编译自官方博客,不是照字翻译。
新年新气象,jQuery 团队于昨日发布了两个新版本:1.12 和 2.2。这两个版本都包含了大量的Bug修正和功能改进。基本上这会是3.0之前最后一次发布。不过由于3.0不做向下兼容,所以届时 jQuery 团队仍然会继续维护这两个版本,当然肯定只做Bug修正。关于3.0的消息将在不久之后公布。
那么新版本都有些什么变化呢?
此版本缩短了 Sizzle 的引用路径,这样当原生 querySelectorAll 和 matchesSelector 无法使用时,可以带来性能提升。在生产环境中效果明显。
小升级很多,这里只拣要紧的说。
作为 HTML 库,支持 SVG 元素是理所应当的。新版本里大家就可以使用 .addClass() 、 .removeClass() 、 .toggleClass() 、 .hasClass() 操作 SVG 对象的 class 了。不过需要注意的是,因为 SVG 和 HTML 还是有很多不同,所以如果真的要进行复杂操作,还是选用其它更专业的类库比较好。
如题:
jQuery.post({
url: “/example”
});
这样一来好处还比较多,比如设置回调函数的 context,或者跨域 post 时可以 withCredential: true。
支持 ES6/ES2015 引入的新运算符, jQuery 对象可以用 for-of 遍历了。
for (element of $elements) {
console.log(element);
}
jQuery.htmlPrefilter()HTML5不要求标签必须闭合,但是XML要求。这个函数就是用来作转换的。这样我们使用 .html() 、 .append() 、.replaceWith() 时就不需要人工转换了。进而,我们也就不需要那么严格的校验输入了。
jQuery.uniqueSort()jQuery.unique() 命名有点问题,没能体现排序,所以这次新增了 jQuery.uniqueSort()。它们俩其实是一回事儿,不过文档中将只记录后者。
这个函数仍然专注于 DOM 节点的排序和排重,请勿乱用。
这个版本理论上没有太大变化,可以平滑升级。不过如果谁还是脸黑遇到什么问题,也请汇报给官方。
就不一一翻译了,想了解的可以看原文:jQuery 2.2 and 1.12 Released