javascript之错误篇


<!–markdown–>抛出你的错误

我们每天都书写着成千上万行的代码,每天都制造着各种千样的bug,或者是这样子,又或者是那样的。他们有些是可以预知的,往往更多又是不可预知的,唯一可以确定的是,它们不可避免,只要代码还存在。庆幸的是,像汽车拥有安全汽囊一样,我们可以在设计之初又或者在修改一段陈年代码时给代码加入错误处理机制。在错误发生的时候像安全汽囊一样“救客(zi)户(ji)一命”。
<p><!–more–></p>

如何抛出错误

javascript的错误消息以信息稀少、隐晦含糊而臭名昭著,再加上web端调试的复杂性,这使得在javascript中抛出错误要比在任何其他语言中做同样的事情都更有价值。javascript 为我们提供了 throw 操作符,将提供的一个对象抛出。

throw new Error(&#34;T^T出错了&#34;);
//并不好的写好,一些浏览器仅会显示uncaught exception(IE,没错,我说得就是你)
throw &#34;T^T出错了&#34;;
throw new Date();
throw 111

抛出错误的好处

如上所述,由于web端调试的复杂性,我们经常需要花上两三天的时间才能定位一个错误发生的原因,再加上一些无理的浏览器的蛮横,往往前端狗们只能默默的把烧烤酱往自己身上抹。经常,我会觉得图片比文字更有说服力。

请输入图片描述

刚好,合适的抛出错误帮我们完成了这个理想。

错误类型

javascript一共为我们提供了七种错误类型。当不同的错误条件发生时,便会触发不同的错误类型,当然我们也可以手工的去创建。

  • Error 所有错误的基本类型。实际上javascript引擎从来不会抛出该类型的错误
  • EvalError 当eval()函数中的语句发生错误时抛出
  • RangeError 当一个数字超过它的边界时抛出 例 var ary = new Array(-20);
  • ReferenceError 当期望的对象不存在时抛出 例 alert(nil)//nil对象并没被定义
  • SyntaxError 当发生语法错误时抛出 例如 var str = adfadfadfl,.xo33;,,,
  • URIError 当encodeURI uncodeURI 等函数中的uri格式错误时抛出
  • TypeError 当发生类型错误时抛出 如 var str = 10 ;

多样的错误类型能让我们更好的对错误进行定义与处理。

try{
    Boom(&#34;危险&#34;);
}
catch (e) {
    if (e instanceof TypeError) {
        //TypeError get
    }else if (e instanceof RangeError){
        //RangeError
    }
    ...
}

自定义错误类型

自定义错误类型是对系统已定义错误类型的一个补充,我们通过继承系统中的Error对象来扩充错误类型以更好的处理错误。例如:

function MyError(message) {
    this.message = message;
}
MyError.prototype = new Error();
throw new MyError(&#34;T^T这个错误类型是怎么回事阿~怎么和别人不一样&#34;);

try 和 catch

和其他语言一样,我们通过try 和catch 来捕捉错误。当try 代码块和出现错误时,会立马跳转到catch 代码块中进行错误处理。

try {
    throw new Error(&#34;T^T出错了&#34;);
}
catch (e) {
    handle(e)
}

何时抛出错误

我们抛出错误往往是为了更加清晰的获取错误信息或者在catch块中更好的进行错误救援措失。胡乱的抛出错误并不能解决以上问题,很可能还会让错误变得难以发现,还会在代码中浪费不必要的性能。关于如何抛出错误有一些很好的经验法则

  • 当修复了一个很难调试的错误,尝试增加一两个自定义错误。当再次发生错误 的时候,将更容易解决问题
  • 在编写代码时,思考一下:“我希望某些事情不会发生,如果发生,我的代码会一团糟”。这时,如果“某些事情”发生,抛出一个错误
  • 如果正在编写的代码别人也会使用。思考一下他们的使用方式,在特定的情况下抛出错误。

我们应该牢记,我们的目的不是为了防止错误,而是在错误发生的时候能更加容易地调试。
一般情况下,我们会在堆栈最深层,如类库中 抛出错误,在逻辑代码中将他们捕获。

ps:此篇内容为个人对 《javascript编码规范》 第十章的总结

在微信扫一扫中直接安装 iosapp


<!–markdown–>概述

这篇文简单的实现了在微信扫一扫中直接安装app,主要的实现思路是通过一个空白页面的跳转。启发来自fir.im

meta跳转

&lt;meta http-equiv=&#34;Refresh&#34; content=&#34;0;url=itms-services:///?action=download-manifest&amp;url=https://www.ylsaas.com/d/ios/ylcc.plist&#34; /&gt;

<p><!–more–></p>

href跳转

&lt;script type=&#34;text/javascript&#34;&gt;

        window.location.href = &#34;itms-services:///?action=download-manifest&amp;url=https://www.ylsaas.com/d/ios/ylcc.plist&#34; ;
&lt;/script&gt;

其他

理论上所有页面跳转的方式都可以实现微信中直接安装app。其他方式探求中…
在线demo–点我扫一扫

javascript模块化之通用模块


<!–markdown–> 概述

js的模块花开发确实不管在开发上海市维护上都给我们带来了很大的便利。但是,不管是AMD又或者CMD都依赖于相关的类库。为了支持相关的标准而被其所绑定并不是很明智的行为。像没有使用模块化开发的老系统要使用某个模块却还要引用相关的类库也是很郁闷。
<p><!–more–></p>

思路

我们知道,不管amd还是cmd都通过define来定义模块,不同的是。cmd通过module.exports来向使用者提供接口,amd直接向使用者返回对象来提供接口。所以,只要我们向module.exports输出接口,同时也直接在define方法里返回对象,那么我们的老模块(组件)可以很轻松的就同时支持了这两种标准。代码如下。demo点我

(function(){

    var root=this,
        obj={
            name:&#39;hijimo&#39;,
            age:13
        };
        root.m=obj;

    if ( typeof  module ===&#39;object&#39; &amp;&amp; module &amp;&amp; typeof module.exports ===&#39;object&#39;){
        module.exports = obj;
    }else{
        if( typeof define === &#39;function&#39; &amp;&amp; define){
            define([], function(){ return obj;});
        }
    }

}.call(this));

大家怎么做

jquery

if ( typeof module === &#34;object&#34; &amp;&amp; module &amp;&amp; typeof module.exports === &#34;object&#34; ) {
        module.exports = jQuery;
} else {
        if ( typeof define === &#34;function&#34; &amp;&amp; define ) {
            define( &#34;jquery&#34;, [], function () { return jQuery; } );
        }
}
if ( typeof window === &#34;object&#34; &amp;&amp; typeof window.document === &#34;object&#34; ) {
    window.jQuery = window.$ = jQuery;
}

underscore

// Export the Underscore object for **Node.js**, with
  // backwards-compatibility for the old `require()` API. If we&#39;re in
  // the browser, add `_` as a global object.
  if (typeof exports !== &#39;undefined&#39;) {
    if (typeof module !== &#39;undefined&#39; &amp;&amp; module.exports) {
      exports = module.exports = _;
    }
    exports._ = _;
  } else {
    root._ = _;
  }
// AMD registration happens at the end for compatibility with AMD loaders
  // that may not enforce next-turn semantics on modules. Even though general
  // practice for AMD registration is to be anonymous, underscore registers
  // as a named module because, like jQuery, it is a base library that is
  // popular enough to be bundled in a third party lib, but not be part of
  // an AMD load request. Those cases could generate an error when an
  // anonymous define() is called outside of a loader request.
  if (typeof define === &#39;function&#39; &amp;&amp; define.amd) {
    define(&#39;underscore&#39;, [], function() {
      return _;
    });
}

javascript模块化实现CMD篇


<!–markdown–>概述

CMD(通用模块定义)规范是seajs在推广中产生的产物,比起AMD规范,CMD的源生中文简直是福音啊。seajs作为一个js模块加载器(也能加载css),其基本原理离不开拼script 异步加载 js模块(文件)。其主要目的是为了解决js命名冲突、文件依赖、版本管里,还有更重要的代码维护问题。最后便是javascript模块化的另一个愿景,模块共享,通过同一个标准,使得大家的代码可以通用。
<p><!–more–></p>

加载模块

  1. 传统意加载方式,如:

    <script type="text/javascript" src="http://static.hijimo.com/js/jquery1.11.1.min.js"></script>
    <script type="text/javascript" src="http://hijimo.com/usr/themes/yi/js/comm.js"></script>

  2. CMD加载方式

 &lt;script src=&#34;sea-debug.js&#34; type=&#34;text/javascript&#34;&gt;&lt;/script&gt;
 &lt;script type=&#34;text/javascript&#34;&gt;
     seajs.config({
        base: &#34;./js&#34;,
        alias: {
             &#39;jquery&#39;:&#39;lib/jquery1.11.1.min/jquery.js&#39;,
             &#39;haha&#39;:&#39;app/haha&#39;,
             &#39;hehe&#39;:&#39;app/hehe&#39;
            }
        });
        seajs.use([&#39;haha&#39;], function(haha){
        //console.log(&#39;哈了悠哈&#39;);
        alert(haha.obj);
       });

&lt;/script&gt;

将seajs引用到页面后,通过简单的配置后便可以使用seajs.use来动态异步加载模块,通过一个回调函数进行后续的操作。

定义模块

在引用seajs之后,我们会得到一个define函数,这个好玩的define函数不仅仅可以帮我们定义一个模块,还可以帮我们定义一个对象?

define({ &#34;foo&#34;: &#34;bar&#34; });

一个字符模板?

define(&#39;I am a template. My name is {{name}}.&#39;);

又或者,一个正经的模块?

define(&#39;moduleid&#39;, [deps], function(require, exports, module) {

  // 模块代码
  return exports;//or reutrn {object};
});

值得注意的是,require这个参数有点小娇气,他必须书写为require,还不能被赋值,还有其他一些奇怪的要求
其中moduleid和deps(依赖列表)是可以被省略的,具体参照官方.
seajs通过返回exports对象来向外提供模块接口。也可以直接返回对象。

exports 仅仅是 module.exports 的一个引用。在 factory 内部给 exports 重新赋值时,并不会改变 module.exports 的值。因此给 exports 赋值是无效的,不能用来更改模块接口

依赖

在上面的列子中,我的comm.js依赖于jquery.js。在传统方式中,我们必须先引用jquery后再引用comm.js(这是废话..),在实际的情况中,常常会有依赖没有被引用而造成comm.js无法正常工作的情况,特别是维护他人的项目或使用他人的组件时。

seajs(模块化)在定义模块时便将依赖引用,这样子使用者只需要知道 moduleid,并不需要管模块的依赖问题。

define(function(require, exports, module){
   require(&#39;jquery&#39;);    
    $(&#39;#console&#39;).html(&#39;哈11哈&#39;);
   return exports.obj = JSON.parse(&#39;{&#34;obj&#34;: &#34;123&#34;}&#39;);

   });

预加载

在seajs的配置项中有一个preload配置项,需要注意的是,他的作用可能和预想的有些出入。直接上例子

seajs.config({
      base: &#34;./js&#34;,
      alias: {
        &#39;jquery&#39;:&#39;lib/jquery1.11.1.min/jquery.js&#39;,
        &#39;haha&#39;:&#39;app/haha&#39;,
        &#39;json&#39;:&#39;lib/json2.js&#39;
          },
      preload: [
           window.JSON ? &#39;&#39; : &#39;json&#39;
      ]
});
seajs.use([&#39;haha&#39;, &#39;jquery&#39;], function(haha){
            //console.log(&#39;哈了悠哈&#39;);
            alert(haha.obj);
});

在之前的列子(依赖)中,在haha.js中我使用了JSON库。在ie中并不包含这个库,肯定是会出错的,但是我又不想在ff,chorme的环境下引用josn2.js。为了让IE6/7/8 支持 JSON的一些API,我们会引入json2.js来修补,而在这些古老的浏览器使用JSON API必然需要预先加载好才可以继续执行。 通过preload 配置中的空字符串会被忽略掉这一约定,我们不仅可以预加载,还可以按特征检测来智能加载的,让高级浏览量无需额外的请求。

值得注意的是,seajs 中并不是读取到有preload配置就立即加载的,只能是通过 seajs.use 这个入口来触发执行预加载, 如上示例 seajs 会在执行 haha模块前确保 预加载完 JSON, 其他方式都无法保证 JSON模块已经加载并执行好

加载非CMD标准的库

一般来说官方是不建议使用seajs去加载非cmd标准的模块的。在2.1.1之前,可以通过preload 和seajs.modify的方式来加载非标准库。代码如下

  seajs.config({
    alias:{
        &#39;jquery&#39;: &#39;//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min&#39;
    },
    preload:[&#34;jquery&#34;]
})

seajs.modify(&#39;jquery&#39;, function (require, exports, module) {
     module.exports = jQuery;
});

如果非要引用的话

  1. 使用传统方式script引入
  2. 在原先类库外面包一层define,代码如下(我的jquery这些都在cdn上呀= - =)
define(function(require, exports, module){
       return $;
   });
  1. 监听事件,将对象暴露到全局(为什么不用一呢)
seajs.on(&#39;exec&#39;, function(module) {
         if (module.uri === seajs.resolve(&#39;jquery&#39;)) {
            window.$ = window.jQuery = module.exports
         }
      })

在线demo

因Mime异常的JS脚本出错


<!–markdown–><h3>症状</h3>

blog新主题里的自定义滚动条突然无法使用了,花费了一些时间排除了css于脚本异常后问题依旧没有得到解决

<h3>过程</h3>

1.chrome 提示 xxxx.js明明是app/javascript类型确返回了app/stream
2.想起今日将静态文件迁移到了阿里云oss
<p><!–more–></p>
3.进入到阿里云修改了mime type
4.问题得到了解决

<h3>结果</h3>
使用的js脚本在出错了已经正确的生成了相关的 dom元素和css,却无法正常工作害我花了大量的时间去检查css\js冲突。
若不注意这些小细节,着实让人吃了不少苦头啊

关于ie下iframe内存泄露


<!–markdown–>契机

近日,手头上负责的一个项目被报告在IE下会产生严重的内存泄露,在对某一复杂表单进行若干次操作后(大约十次左右)IE进程的内存占用就达到了600M之多,恐怖之极。

过程

由于泄露页面表单中使用了extjs,第一时间怀疑是否是extjs代码内存没有及时释放。但是这个设想很快就被我否定了,因为我发现在发生泄露后不管我怎么跳转页面、重登,甚至关闭ie,进程还是占用着内存。

基于IE的CollectGarbage机制,我们知道,当对象不在他当前上下文的时候,即会失效。
<p><!–more–></p>
本着70%的问题别人都是已经碰到过的原则,我找到了神奇海螺。最后我发现,<font color='red'>IE并没有办法正确的释放iframe所占用的内存</font>,一旦碰到这种iframe里藏着个怪兽的情况下。内存泄露将会非常严重。

解决方案

手工释放iframe的内存。

iframe_child=obj.getElementsByTagName(&#39;iframe&#39;)[0];
    //清空iframe 减轻ie下内存泄露
    if(iframe_child){
        iframe_child.src=&#39;about:blank&#39;;
        iframe_child.contentWindow.document.write(&#39;&#39;);
        iframe_child.contentWindow.document.clear();
        iframe_child.contentWindow.document.close();
        iframe_child.removeNode(true);
    }
    setTimeout(function(){

        obj.wintitle.removeNode(true) ;
        obj.removeNode(true) ;
        obj = null;


    },200);
    if(CollectGarbage){//IE强制回收内存
    CollectGarbage();
    }

javascript模块化编程( 上)


<!–markdown–>ps:写这篇文章的时候,我的大脑已经是处于加班了一个多月的情况下。文章写得很乱,会不定时修改,也有直接删除的可能

#什么是javascript模块化

引用一下阮一峰前辈的一句话:理想情况下,开发者只需要实现核心的业务逻辑,其他都可以加载别人已经写好的模块。
广义上而言。.css的重复利用,.js文件的组件封装都可以算是前段的模块化实现。
早在2012初的时候(我用的时候),extjs的前端mvc框架就是很先进的javascript模块化加载了。
同样的还有YUI的add 和namespace等。所以,javascript其实离我们并不远,更不陌生。

#为什么需要模块化编程

<del>十年前,网页还只是简单的承载着一些多媒体信息,甚至于我们观看视频还要安装各种各样的视频播放插件(如 windows player、realplayer等等)。当时的网页页面并不复杂,甚至很多页面都没有出现javascript语句。现在绝对会被杀千刀的一些代码写法在当时确非常流行,举个栗子

&lt;input type=&#39;button&#39; onclick=&#34;handler()&#34; /&gt;

更有甚至

&lt;input type=&#39;button&#39; onclick=&#39;function(){log(&#39;hahahahah&#39;);}&#39; /&gt;

<del>随着我们的硬件发展越来越好,加上各种现代浏览器助力,我们的网页已经早已不满足只是简单的呈现信息。b/s应用早已侵吞了c/s应用的半壁江山。随之而来的便是团队协作,代码越来越发杂,逻辑越来越发杂,代码维护越来麻烦。那么和其他语言一样,模块化是必然。

看这里就好。网页越来越复杂,越来越像桌面程序,需要一个团队分工协作、进度管理、单元测试等等......开发者不得不使用软件工程的方法,管理网页的业务逻辑。

#AMD规范和CMD规范

要想要随意的使用别的代码,有一个前提便是,大家都用一样的规范或标准去写代码。CMD规范AMD规范 。两者最大的区别就是AMD是异步加载的,而CMD是同步加载的。CMD比起AMD规范要早不少,主要用于服务端。
CMD是一个很好的规范。但是,它并不适用于客户端。原因如下:
1.javascript是单线程语言
2.由于一,在加载时浏览器会假死
3.网络无法控制,若需要10s加载,参照2

#模块化的弊端

1.模块化的前提是规范,为了符合规范,程序员需要更改编程习惯
2.老项目想要模块化需要成本
3.最重要的,模块化难以避免的会造成文件碎片化,造成请求数剧增。

在AMD规范里,在请求模块的时候会同步加载模块所需的类库,这进一步加剧了碎片,增加请求数量。如果不解决这个问题,一个页面3位数的请求数是一件很恐怖的事情。
百度fis对此提出的解决方案是将所有依赖打包以减少请求数。同时,依赖和模块分开打包,可以很好的利用文件缓存。但是,每次发布都要打包实在太麻烦,于是,他们就正大光明的给自己的fis打广告了 -0 -

javascript前端模版实现


<!–markdown–>刚刚修复的小站,界面还未完工,现在发现markdown也跪了。真是悲剧,请大家将就着看吧

远程服务器方式

即从服务端获取模版文本填充到客户端的做法,因为这种做法很容易造成xss漏洞,所以我们还需要花费大力气在前后端做处理,所以不是很推荐。但是,将大量无用的标签存在dom或内存中也是很糟糕的做法,如果你有大量的html标签要处理,不妨试试这种方式。

简单客户端模版

1.注释流
<p><!–more–></p>

注释文本和其他html标记或文本一样是会被记录到dom树里,可以被javascript获取,以下是简单示例。

    &lt;input type=&#39;button&#39; id=&#39;btn_ent&#39; value=&#39;我按&#39; /&gt;

    &lt;div id=&#39;container&#39;&gt;
        &lt;!--&lt;ul&gt;
                &lt;li&gt;吃饭么今天&lt;/li&gt;
                &lt;li&gt;今天只有面唉&lt;/li&gt;
                &lt;li&gt;那怎么办&lt;li&gt;
                &lt;li&gt;我们之间有一个人是食物,我不是&lt;/li&gt;
                &lt;li&gt;我也不是&lt;/li&gt;
                &lt;li&gt;你不是个鬼啊.快点给我变成食物&lt;/li&gt;
                &lt;li&gt;我不要,你去死吧&lt;/li&gt;
                &lt;li&gt;啊~~啊~~ 啊~~ 啊~~~ 啊~~~~&lt;/li&gt;
        &lt;ul&gt;--&gt;
    &lt;/div&gt;
&lt;script&gt;

    document.getElementById(&#39;btn_ent&#39;).onclick=function(){

        var container=document.getElementById(&#39;container&#39;),
            script=document.getElementById(&#39;template&#39;);

        /*
         * 注释大法 
         * 有些浏览器可以通过 container.firstNode.nodeValue直接取出注释= - = 
         * 有些浏览器却不能 = - =
         */    
                container.insertAdjacentHTML(&#39;beforeend&#39;,getTemplate(container));

    }
&lt;/script&gt;

2.自定义script标签流
一般情况下我们的script标签默认是text/javascript类型的,但是同时我们是可以自定义type='jm-tempalte',来返回一段文本,通过这个特性我们可以更简单的获取到我们的模版。以下是简单示例。

    &lt;div id=&#39;container&#39;&gt;

    &lt;/div&gt;

&lt;script type=&#39;text/jm-template&#39; id=&#39;template&#39;&gt;
    &lt;ul&gt;
        &lt;li&gt;吃饭么今天&lt;/li&gt;
        &lt;li&gt;今天只有面唉&lt;/li&gt;
        &lt;li&gt;那怎么办&lt;li&gt;
        &lt;li&gt;我们之间有一个人是食物,我不是&lt;/li&gt;
        &lt;li&gt;我也不是&lt;/li&gt;
        &lt;li&gt;你不是个鬼啊.快点给我变成食物&lt;/li&gt;
        &lt;li&gt;我不要,你去死吧&lt;/li&gt;
        &lt;li&gt;啊~~啊~~ 啊~~ 啊~~~ 啊~~~~&lt;/li&gt;
    &lt;ul&gt;
&lt;/script&gt;
&lt;script&gt;

    document.getElementById(&#39;btn_ent&#39;).onclick=function(){

        var container=document.getElementById(&#39;container&#39;),
            script=document.getElementById(&#39;template&#39;);

        /*
         * script 模版大法
         * script里取出来的文本前面总是会多一个空格,所以要记得去掉
         */
                                    container.insertAdjacentHTML(&#39;beforeend&#39;,script.text.replace(/^s*/,&#39;&#39;));

    }

&lt;/script&gt;

复杂客户端模版

复杂客户端模版一般使用Js类库实现,比如handlebars,比如backbone,又比如underscore。

最后,根据传统,放上完整demo[点我吧]
在线demo稍后上线.

javascript中的asi机制


<!–markdown–>庆祝自己小站复活,先写个文章压压惊。
在此感谢木工(木木工程师)的技术支持。

什么是Automatic semicolon insertion机制呢。中文一般翻译成自动插入分号,简称asi机制。功能呢,也很简单,就是智能的判断语句是否插入分号,示例如下

function(){
    reutrn  &#39;hehe&#39;
}

<p><!–more–></p>

由于有asi机制的存在,以上代码是合法而合理的。大概javascript的设计师是为了帮助一些粗心的小猴子温馨的加个分号。但是呢,也会发生一些神奇的事情。比如如下示例

function(){
        return
        {
            today:&#39;才周二&#39;
        }
    }

或许,我们希望返回一个对象,但是由于asi机制的存在,这段代码并不能返回我们想要的结果。正确示例应如下

function(){
      return{
          today:&#39;不是周五&#39;
      }
  }

像方括号、逗号、大括号、小括号等这些常用符号并不会被自动加上分号而被断句。
asi机制的规则花样繁多,去记住他们的所有规则有点不大现实,所以大部分情况下我们还是老老实实的在句末加上分号吧。同时,留个小心情,避免发生如上bug。