Monthly Archives: 一月 2018

Enable jqgrid to do/cancel selecting only selectable items

I had this requirement these days: We have a table with many rows among which some are selectable and some are not selectable, and there is also a selecting-all button which should support do/cancel selecting all selectable rows. But after glancing over the old project, I found no similar ready-made code, so I search for this by Baidu, and found solution like below (modified and beautified):

The above code seems to work well at first attempt. But you will find that you can not undo the selecting-all operation. I cannot use google for several months, therefore I use bing to search for other solutions, and thank goodness, here are the solution:

Haha! I really think the api document for jqgrid plugin is too complicated that it scares me to earnestly read it.

Centos7开机自启动nginx, php-fpm

今天收到邮件才发现VPS到期被停用了o(╯□╰)o,赶紧地,去把费用续了,ssh到服务器发现,还好还好,文件都还在,不用重新装软件,不用重新clone仓库代码。不过发现我的wordpress博客没法用了(nginx + php-fpm + mysql), service nginx restart 没用, service mysqld restart 也没用,后来发现可能是php-fpm模块的问题。解决方案如下:

使用以下命令开启相关服务:

然后让这两个服务开机自启:

重启服务器,确认是否生效:

重新ssh登陆服务器后, systemctl list-units --type=service 查看运行的服务:

*注:请忽略上面启动失败的httpd.service服务,这个服务我暂时是不用的。

InnoDB: Error number 28 means ‘No space left on device’

前几天数据库挂了, ssh 登陆服务器后 service mysqld restart 重启数据库后正常了就没当回事,今天又挂了,然后重启mysql启动不了了。 vi /var/log/mysql.log 查看mysql日志发现下面的内容:

敲黑板,划重点:

于是,来一记 df -h ,卧槽,硬盘容量差不多就是被用光了,15G只剩下10M了,什么东西占了这么多硬盘?——一般不是日志就是备份文件。然后把我的博客备份文件都删掉后再一记 df -h :

非常棒,这样就还有10G可用了。最后运行本文开头的那个命令( service mysqld restart ),重启mysql服务,问题暂时解决。

桌面端浏览器兼容性问题总结

这里有一份IE8份额统计数据,数据来源为百度统计所覆盖的超过150万的站点,样本为2017年6月1日-2017年6月30日这一个月的数据:https://baijiahao.baidu.com/s?id=1571935494090406&wfr=spider&for=pc。根据文中数据,可以看到那个时间段里IE8的份额已经降至9.83%,并且由于微软不再对IE8提供支持等原因,IE8份额在过去的一年里一直处于一个下降的趋势中。但是9.83%的份额还没有小到让所有企业都愿意忽略的地步。

首先,IE8以下的浏览器已经退出历史舞台了,IE8的份额虽然越来越低了,但还没有低到可以忽略的程度,另外,为低端浏览器的使用者提供一个基本的可用性的保障应当是我们开发人员的一个责任所在。所以,现在浏览器兼容最低做到IE8就可以了,公司内容的工具类网站做到IE9便已足够。

如果你的网站只需要兼容到IE9,那基本上可以说你是不需要考虑什么兼容性的问题了。IE9+(含IE9)、chrome、firefox等浏览器在前端领域统称为现代浏览器,它们对html5、ES5、CSS3的大部分特性都有较好的支持。

需要说明一点,本文所指的兼容性处理方案,有一类是为了保持相类似的效果,有一类是做优雅降级/渐进增强处理,如果为了保持相类似的效果而为低版本浏览器做一些很吃性能的处理是不如直接做优雅降级的,因为对用户来说,“卡得要死”是最糟糕的。当然,还有一种方案是直接提示用户使用现代浏览器。

处理兼容性问题的终极大招——项目写好后:

  • 在其他浏览器里也打开看看界面效果是否有问题,如果有问题的话审查下元素看下是样式没生效,还是样式有问题;
  • 再点点按钮,看看控制台是否有报错,如果有报错,排除掉逻辑性问题后基本就是JS的兼容性问题了,而很多兼容性问题就是缺少polyfill,去MDN网站上搜一下相关的polyfill代码复制粘贴一下就好了。

大致例举下常见的兼容性问题:

Vue、Angular不支持IE8

请在项目开发之前先确定浏览器要兼容到的程序,不需要兼容到IE8的项目要及时劝阻,需要兼容到IE8的项目别上Vue、Angular这些,喜欢MVWhateveryoulike框架的话可以考虑上avalon,如果还要同时考虑SEO,那直接上jquery 1+版本其实也挺好。

部分JS API不支持

使用polyfill,这个可以去MDN这类网站上找相关实现。或者使用做了相关兼容性处理的js库文件。

IE6、7、8怪异模式(quirk)下的盒模型

IE6、7、8怪异模式下的盒模型相当于元素应用了CSS3的box-sizing: border-box;后的结果——width/height的计算是包括border、padding在内的。这个只要你在文档头部进行<!DOCTYPE html>这样的声明就可以避免了。所以如果你碰到一个人不知道IE6、7、8怪异模式下的盒模型问题的话,其实没什么,说明他一直有写这些头部文档类型声明。

CSS兼容性

由于各个浏览器对各种html元素的默认样式是不一致的,所以需要一套CSS Reset来进行统一,可以视统一后的样式为浏览器的“默认样式”,然后在这个基础上进行界面开发。

当初学初学前端的时候,我们还会手写一堆css厂商前缀来尽可能保障各个浏览器都能对这些css属性有个较好的识别。现在借助autoprefixer这类node工具,我们可以把这个工作交给工具去处理。

这里有个非常好用的网站:http://caniuse.com,在这上面可以查各种CSS属性的兼容性,比如可以查到微软对border-radius属性的支持是从IE11开始的,这个地方如果考虑为优雅降级处理的话,就是IE11开始的现代浏览器里显示圆角,IE10及以下的浏览器里显示矩形;这个地方如果想都显示圆角的话,可以不使用border-radius属性,改为使用background-image背景图片,然后将圆角图片作为背景使用。

IE8不支持Html5新标签

IE8出现的比Html5还早,不支持header、footer、article、nav这些新标签是能理解的。在IE8中,可以通过 document.createElement('article'); 这样的方式来让IE8能对其进行识别,另外还需要在CSS里对这些元素进行display属性的声明,因为IE8并不知道他们是块级元素还是行内元素。这种重复性的工作早有现成的解决方案——可以直接使用一个叫html5shiv的js库(这种对页面样式潜在影响比较大的库,应当在head头部中就进行引入,而不是至于body结束标签处)。

结语

其实PC端现存的兼容性问题就那么多,你按标准来写代码即可,你能碰到的问题基本上搜一下都有现成的处理方案的。

 

JS原型与原型链

普通对象与函数对象

JS中,对象分为普通对象和函数对象两种,Object和Function是JS自带的函数对象。凡是通过new Function()的创建的对象都是函数对象,其他的都是普通对象。

下面这些是函数对象:

下面这些是普通对象:

原型对象

每当定义一个对象(函数)时,对象中都会包含一些预定义的属性。其中,函数对象会有一个prototype属性,其值就是我们所说的原型对象(普通对象没有prototype,但有__proto__属性;函数对象同时含有prototype和__proto__属性)。注意__proto__这里proto前后分别都是两个下划线,不是一个。

原型对象其实就是普通对象(Function.prototype除外,它是函数对象,但同时它又没有prototype属性)。

原型对象的主要作用是用于继承:

原型链

上面提到原型对象的主要作用是用于继承,其具体的实现就是通过原型链实现的。创建对象(不论是普通对象还是函数对象)时,都有一个叫做__proto__的内置属性,用于指向创建它的函数对象的原型对象(即创建它的函数对象的prototype属性)。

这个由__proto__串起来的直到Object.prototype.__proto__ ==> null对象的链称为原型链。

  • yakima的__proto__属性指向Person.prototype对象;
  • Person.prototype对象的__proto__属性指向Object.prototype对象;
  • Object.prototype对象的__proto__属性指向null对象。

下面有一些比较特别的情况,看完忘掉就可以了,如果是在准备面试,最好别看,别看混了^_^。

Object是函数对象,是通过new Function()创建的,所以Object.__proto__指向Function.prototype:

Function是函数对象,是通过new Function()创建的,所以Function.__proto__指向Function.prototype。本类创建本类。。。大概类似是这么个意思——人是人他妈生的,妖是妖他妈生的。

另外:

constructor

原型对象都有个constructor属性,用来引用它的函数对象。这是一种循环引用。

综合理解

原型和原型链是JS实现继承的一种模型。

对上例的分析:

  • Dog自身没有price属性,沿着Dog.__proto__属性往上找,因为Dog的赋值是通过var Dog = function () {}实现的,所以Dog其实是使用new Function()创建的,所以Dog.__proto__ ==> Function.prototype,Function.prototype.__proto__ ==> Object.prototype,而Object.prototype.__proto__ ==> null。很明显,整条链上都找不到price属性,只能返回undefined;
  • tidy自身没有price属性,沿着tidy.__proto__属性往上找,因为tidy对象是Dog函数对象的实例,所以tidy.__proto__ ==> Dog.prototype ==> Animal,从而tidy.price获取到了Animal.price的值。

promise的使用

基本上可以这么说,promise是一个返回给你的对象,你可以往这个对象上追加一些回调函数,从而避免了以前那种往函数里塞回调函数(callback)的写法。

比如,以前你可能写过类似这样的旧回调风格代码:

用promise的话,你可以这么写:

甚至可以这么写:

我们称之为异步函数调用(asynchronous function call)。这种代码风格有一些优点,下面我们逐一说明。

一些保证

不像旧回调风格代码,promise可以确保下面这些特性:

  • 回调不会在当前事件循环结束之前被调用;
  • 通过.then追加的回调函数在异步操作结束(成功或失败)之后会被调用,即便这个.then是在异步操作已经被resolved之后追加的;
  • 可以通过多次追加.then来添加多个回调函数,这些回调函数会按照被添加的顺序独立执行。

不过promise的最直接的好处还是可链式使用。

链式使用

有一个常见的需求是连续执行两个或者多个异步操作,每个后续操作都在前一个操作成功后才开始执行,并且后续操作可以收到前一个成功操作返回的数据。我们可以通过promise链来实现这个需求。

敲黑板,划重点:这里的重点在于让.then方法返回一个不同于前面的、新的promise。

或者这样写:

这里的promise2不仅仅表示doSomething()的完成,也表示了传入的successCallback或failureCallback的完成——这里的successCallback和failureCallback可以是其他会返回promise的异步函数。如果这里的successCallback、failureCallback是会返回promise的异步函数的话,追加到promise2上的回调函数就会在successCallback或failureCallback执行完后再被调用。

基本上,在由promise构成的链上,每个promise都表示另一个异步操作的结束。

以前,要在一起写好多个异步操作代码很容易出现经典的“回调金字塔厄运”(callback pyramid of doom):

但是现在,我们可以把回调函数追加到返回的promise上,形成一个promise链,就像这样:

敲黑板,划重点:then方法里的参数是可选的。另外, catch(failureCallback) 是 then(null, failureCallback) 的简写形式。如果使用箭头函数,上面的代码可以进一步简写为:

重点提示:总是要返回promise,否则这些回调函数就没办法链在一起了,错误也将没法被捕获。

catch之后的链

在失败(catch)之后也是可以继续链下去的,这可以让你在即使链中的某个行为失败了也可以继续完成新的行为。下面是一个示例代码:

上面的代码执行后会输入下面的内容:

注意,这里“Do this”不会被打印出来,因为”Something failed”这个错误被抛出时会触发一个rejection。

错误传播

还记得前文我们提到“回调金字塔厄运”(callback pyramid of doom)时举例用的代码吗?那块金字塔代码里, failureCallback 一共出现了三次。但是在promise链里failureCallback只需要在最底部出现一次:

基本上,如果promise链中出现了一个异常,这个链就会停止后续的动作,取而代之的是会沿着链往下找绑定catch处理代码块。这很像同步代码的书写模型:

ECMSScript 2017引入了async/await语法糖来实现类似的同步代码书写风格:

async/await语法糖是建立在promise的肩膀之上的,上述代码片段里的 doSomething 跟前面的函数是一样的(返回promise)。

Promise通过捕获所有的错误(包括抛出的异常和程序性错误)初步解决了回调函数金字塔的问题。这对异步操作的代码书写而言是非常有必要的。

将旧API包装为Promise

Promise可以通过对应的构造器非常方便的创建出来。但有些旧的API,需要我们自行将其包裹为一个promise。

在一个理想的代码国度里,所有异步函数应该都会返回promise。唉,但是,一些API仍旧期望使用者按以前的方式传入成功和/或失败回调。一个典型的例子就是setTimeout()函数:

混合使用旧式回调和promise无异于自讨没趣。如果 saySomething 失败了或者包含有语法性错误,并没有相应的代码来捕获它们。

幸运的是,我们可以将它包装为一个promise。这里有个最佳实践,就是尽可能把容易出问题的函数包装在最底层,然后从不直接调用它们:

基本上,promise构造器接受一个执行函数,在这个执行函数里我们可以手动对promise进行resolve或reject。因为setTimeout并不会真的“失败”,所以在上面的这个例子里,我们省略了promise被reject的情况。

成员

Promise.resolve() 和 Promise.reject() 是创建已经被resolved或rejected的promise对象的简便写法。有时这会很方便。

Promise.all() 和 Promise.race() 可用于并行执行多个异步操作。

顺序执行多个异步操作可以借助下面这样“聪明”的代码实现:

上面的代码可以这么理解,我们把一个由异步函数组成的数组通过数组的reduce方法拼接成了一个promise链,它等价于: Promise.resolve().then(func1).then(func2); 。

这个效果也可以通过一个可重复使用的compose函数来实现——这在函数式编程中是非常常见的:

composeAsync函数会接受任意数量的函数作为入参,然后返回一个新的函数,这个新的函数会接收一个初始值。

在ECMAScript 2017中,这个顺序执行的需求可以借助async/await更简单地实现:

时序

不要惊讶,传给then的函数从来都不会被同步调用,即便是在promise都已经被resolved的情况下,比如下面这样的代码,先打印出来的是1,然后才是2,尽管打印2的代码在打印1的代码之前出现:

在上面这个示例代码里,被传入then的函数不会立即被执行,而是会被堆放在一个微任务队列(microtask queue)上——这意味着这个被传入的函数会在当前JavaScript事件循环结束后微任务队列被清空时执行,反正也是相当快就会被执行到的。

下面是另一个例子:

参考:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises

怪异模式与标准模式

很久以前,网页基本上可以说是分两种,一种是针对网景浏览器(Netscape Navagator)而写的,一种是针对微软IE浏览器而写的。当W3C开始制定web标准后,这些浏览器厂商不可能立即采用这些标准,因为这会影响当时网络上存在的大量网页。为此,浏览器厂商决定引入两种模式来有针对性地解析网页。

现在网页浏览器的布局引擎(layout engine)中使用了3中模式:怪异模式(quirks mode)、准标准模式(almost standards mode)和完全标准模式(full standards mode)。在怪异模式下,浏览器会模拟网景4(Navigator 4)和IE5浏览器的非标准行为来解析网页——这是很有必要的,因为需要支持那些在网络标准被广泛采纳之前就已经存在的网站。在完全标准模式下,浏览器会按照HTML和CSS标准来解析网页(但愿如此-_-)。在准标准模式下,浏览器的解析行为已经比较标准了,但是还是会有一小部分的怪异行为。

浏览器如何决定使用何种模式来解析网页

对HTML文档而言,浏览器会根据文件头部的DOCTYPE文档类型声明来判断是采用怪异模式还是标准模式来解析网页。为确保浏览器会使用完全标准模式来解析你的网页,请确保你的网页头部有类似下面这样的DOCTYPE文档类型声明:

上例中的DOCTYPE声明(<!DOCTYPE html>)可能是最简答的文档类型声明了,而且也是HTML5标准推荐的文档类型声明。更早的HTML标准推荐的是其他的文档类型。但是所有现世的浏览器(包括很老的IE6)都会使用完全标准模式来解析这种文档类型声明的html文件,所以使用其他的(同时书写上也更复杂的)DOCTYPE文档类型声明是毫无理由的。如果你使用其他的DOCTYPE文档类型声明,你可能一不小心就写了一个触发了准标准模式或怪异模式的DOCTYPE文档类型了。

请务必确保将DOCTYPE文档声明写于文档的最头部——任何出现在DOCTYPE文档类型声明之前的内容(比如注释、xml声明)都会在IE9及更早的IE浏览器中触发怪异模式。

在HTML5标准里,DOCTYPE文档类型声明的唯一目的就是激活完全标准模式。在更早的HTML标准里,DOCTYPE文档类型声明被赋予了一些其他含义,但是从没有哪个浏览器会把DOCTYPE用于判断是使用怪异模式还是标准模式之外的其他用途。

XHTML

这里应该敲下黑板。如果你的服务器在HTTP响应头的Content-Type里使用 application/xhtml+xml 这个MIME类型来将你的网页声明为XHTML的话,你不再需要通过声明一个DOCTYPE来激活标准模式,因为这类文件总是会被浏览器按完全标准模式进行解析。但是需要注意的是,使用 application/xhtml+xml 这个MIME类型的话,IE8浏览器会出现下载对话框而不是显示对应的网页,因为IE8不认识这个文档格式——第一个支持XHTML格式的IE浏览器是IE9。

如果你使用 text/html 这个MIME类型来返回类XHTML文件的话,浏览器会按照HTML文件来对待这些网页,这时候你就需要显示地进行DOCTYPE文档类型声明来触发标准模式了。

如何知道当前使用的模式

火狐浏览器中,可通过在上下文菜单里选择“查看页面信息”查看“渲染模式”。

IE浏览器中,可通过按F12查看“文档模式”。

这些模式间的区别是什么

详见怪异行为清单准标准模式

参考资料:

https://developer.mozilla.org/en-US/docs/Web/HTML/Quirks_Mode_and_Standards_Mode

 

W3C标准盒模型及IE盒模型

盒模型有两类:W3C标准盒模型和IE盒模型。

W3C标准盒模型

一个元素的W3c标准盒模型由margin、border、padding、content四个区块构成。在box-sizing: content-box;时,元素的宽度、高度指的是content的宽度、高度。在box-sizing: border-box;时,元素的宽度、高度是content、padding、border三者的合计值,与IE盒模型一致。

IE盒模型

如果html文件头部没有有效地声明文件类型的话,IE6、7、8下的盒模型解析情况会跟W3C标准盒模型不一致,我们称之为IE盒模型。由于现在我们写html页面时都会在头部加上html5文档类型声明(<!DOCTYPE html>),所以并不用担心这个问题,知道有这么回事就可以了。

一个元素的IE盒模型也是由margin、border、padding、content四个区块构成的。在IE盒模型里,元素的宽度、高度是content、padding、border三者的合计值。

Margin特别说明

margin-top和margin-bottom对display: inline;的元素无效(margin-left、margin-right是有效的)。

display: inline-block|block;的垂直相邻元素间距离的计算规格:

  • margin-top和margin-bottom均为正数,取最大值;
  • margin-top和margin-bottom均为负数,取最小值(-2px、-10px,取-10px);
  • margin-top和margin-bottom一正一负,取其和(-2px、10px,取8px;2px、-10px,取-8px);
  • 以上三条可简记为“同正负取绝对值大的,一正一负取其和”;

loadScripts方法

北京前端同事有个项目里用了ace-admin这个管理平台框架,不太喜欢这个框架,不过在里面发现了一段异步加载js脚本文件的代码,修改了一下贴到这里,以后自己项目里也可以用:

使用方法很简单,碰到有js文件依赖的时候,这样使用ready方法:

如果没有js文件依赖的话,这样使用ready方法:

下面是根据以前自己写过的方法封装一下得到的代码,作用差不多(没像上面的例子那样判断脚本是否已加载过,不过用document.write生成script标签加载js文件的话,浏览器自己也会做缓存处理的,问题不大吧-_-):