私は長い間 JavaScript コードを書いてきましたが、いつ書き始めたかさえ思い出せません。私は JavaScript がここ数年で達成したことに非常に興奮しています。私は幸運にもこれらの成果の恩恵を受けることができました。私はこの言語に特化した記事、章、本をかなりの数書いてきましたが、まだこの言語について新しいことを発見しています。以下の説明は、私が過去に「ああ!」と思ったプログラミング手法であり、将来遭遇するのを待つのではなく、今すぐ試しるべき手法です。
簡潔な書き方
JavaScript で私が気に入っていることの 1 つは、オブジェクトと配列を生成するための短縮法です。以前は、オブジェクトを作成したい場合は、次のように実行していました:
1 var car = new Object(); 2 car.colour = 'red'; 3 car.wheels = 4; 4 car.hubcaps = 'spinning'; 5 car.age = 4;
以下のようにしても同じ効果が得られます:
1 var car = { 2 colour:'red', 3 wheels:4, 4 hubcaps:'spinning', 5 age:4 6 }
はるかに単純で、オブジェクトの名前を再利用する必要はありません。このようにして、car が定義されます。この問題は IE を使用する場合にのみ発生します。ただ 1 つだけ注意してください。そうすれば、問題は発生しません。
もう 1 つの非常に便利な略語は、配列です。配列を定義する従来の方法は次のとおりです:
1 var moviesThatNeedBetterWriters = new Array( 2 'Transformers','Transformers2','Avatar','IndianaJones 4' 3 );
短縮版は次のとおりです:
1 var moviesThatNeedBetterWriters = [ 2 'Transformers','Transformers2','Avatar','IndianaJones 4' 3 ];
配列の場合、実際にはグラフ グループ関数がありません。しかし、上で car をこのように定義している人をよく見かけます
1 var car = new Array(); 2 car['colour'] = 'red'; 3 car['wheels'] = 4; 4 car['hubcaps'] = 'spinning'; 5 car['age'] = 4;
配列は万能ではなく、間違って書かれているため、混乱を招きます。グラフ グループは実際にはオブジェクトの関数であるため、人々は 2 つの概念を混同しています。
もう 1 つの非常に優れた短縮方法は、三項条件表記を使用することです。このように記述する必要はありません...
var direction; if(x < 200){ direction = 1; } else { direction = -1; }
三項条件表記を使用して簡略化できます:
var direction = x < 200 ? 1 : -1;
条件が true の場合は疑問符の後の値を取得し、それ以外の場合はコロンの後の値を取得します。
JSON 形式でのデータの保存
JSON を発見する前は、配列や文字列などの JavaScript 固有のデータ型に、分割しやすいフラグやその他の厄介なものを混ぜ合わせて、あらゆる種類のクレイジーな方法を使用していました。 。 Douglas Crockford が JSON を発明したとき、すべてが変わりました。 JSON を使用すると、JavaScript 独自の関数を使用してデータを複雑な形式で保存でき、追加の変換を行わずに直接アクセスして使用できます。 JSON は「JavaScript Object Notation」の略称で、上記の 2 つの略語を使用します。したがって、バンドを説明したい場合は、次のように記述できます:
01 var band = { 02 "name":"The Red Hot Chili Peppers", 03 "members":[ 04 { 05 "name":"Anthony Kiedis", 06 "role":"lead vocals" 07 }, 08 { 09 "name":"Michael 'Flea' Balzary", 10 "role":"bass guitar, trumpet, backing vocals" 11 }, 12 { 13 "name":"Chad Smith", 14 "role":"drums,percussion" 15 }, 16 { 17 "name":"John Frusciante", 18 "role":"Lead Guitar" 19 } 20 ], 21 "year":"2009" 22 }
JSON を JavaScript で直接使用したり、関数でカプセル化したり、API からの戻り値として使用したりすることもできます。これを JSON-P と呼び、多くの API がこの形式を使用します。
データプロバイダーを呼び出して、スクリプトコードで直接JSON-Pデータを返すことができます:
01 <div id="delicious"></div><script> 02 function delicious(o){ 03 var out = '<ul>'; 04 for(var i=0;i<o.length;i++){ 05 out += '<li><a href="' + o[i].u + '">' + 06 o[i].d + '</a></li>'; 07 } 08 out += '</ul>'; 09 document.getElementById('delicious').innerHTML = out; 10 } 11 </script> 12 <script src="http://feeds.delicious.com/v2/json/codepo8/javascript?count=15&callback=delicious"></script>
これは、Delicious Webサイトが提供するWebサービス関数を呼び出して、最近の順序付けされていないブックマークリストをJSON形式で取得します。
基本的に、JSON は複雑なデータ構造を記述する最も移植可能な方法であり、ブラウザーで実行できます。 json_decode() 関数を使用して PHP で実行することもできます。 JavaScript の組み込み関数 (Math、Array、String) について私が驚いたことの 1 つは、JavaScript の math 関数と String 関数を勉強した後、これらの関数によってプログラミング作業が大幅に簡素化されることがわかったことです。これらを利用することで、複雑なループ処理や条件判定を省くことができます。たとえば、数値の配列内の最大の数値を見つける関数を実装する必要がある場合、以前は次のようにループを記述していました:
1 var numbers = [3,342,23,22,124]; 2 var max = 0; 3 for(var i=0;i<numbers.length;i++){ 4 if(numbers[i] > max){ 5 max = numbers[i]; 6 } 7 } 8 alert(max);
ループせずに実行できます:
1 var numbers = [3,342,23,22,124]; 2 numbers.sort(function(a,b){return b - a}); 3 alert(numbers[0]);
注意すべき点 はい、数値文字配列を sort() することはできません。その場合、アルファベット順にのみソートされるからです。さらに詳しい使用法を知りたい場合は、sort() に関するこの優れた記事を読んでください。
もう一つの興味深い関数は Math.max() です。この関数はパラメータ内の数値のうち最大の数値を返します:
Math.max(12,123,3,2,433,4); // returns 433
この関数は数値をチェックして最大のものを返すことができるため、特定の機能に対するブラウザのサポートをテストするために使用できます:
1 var scrollTop=Math.max( 2 doc.documentElement.scrollTop, 3 doc.body.scrollTop 4 );
これは次の目的で使用されます。 IEの問題を解決します。現在のページのscrollTop値を取得できますが、ページのDOCTYPEに応じて、上記の2つのプロパティのうちの1つだけがこの値を保存し、もう1つのプロパティは未定義になるため、Math.maxを使用してこれを取得できます。 () 番号。数学関数を使用して JavaScript を簡素化する方法について詳しくは、この記事を読んでください。
文字列を操作するための非常に便利な関数のもう 1 つのペアは、split() と join() です。最も代表的な例は、ページ要素に CSS スタイルを付加する関数を記述することだと思います。
是这样的,当你给页面元素附加一个CSS class时,要么它是这个元素的第一个CSS class,或者是它已经有了一些class, 需要在已有的class后加上一个空格,然后追加上这个class。而当你要去掉这个class时,你也需要去掉这个class前面的空格(这个在过去非常重要,因为有些老的浏览器不认识后面跟着空格的class)。
于是,原始的写法会是这样:
1 function addclass(elm,newclass){ 2 var c = elm.className; 3 elm.className = (c === '') ? newclass : c+' '+newclass; 4 }
你可以使用 split() 和 join() 函数自动完成这个任务:
1 function addclass(elm,newclass){ 2 var classes = elm.className.split(' '); 3 classes.push(newclass); 4 elm.className = classes.join(' '); 5 }
这会确保所有的class都被空格分隔,而且你要追加的class正好放在最后。
事件委派
Web应用都是由事件驱动运转的。我喜欢事件处理,尤其喜欢自己定义事件。它能使你的产品可扩展,而不用改动核心代码。有一个很大的问题(也可以说是功能强大的表现),是关于页面上事件的移除问题。你可以对某个元素安装一个事件监听器,事件监听器就开始运转工作。但页面上没有任何指示说明这有个监听器。因为这种不可表现的问题 (这尤其让一些新手头疼) ,以及像IE6这样的”浏览器“在太多的使用事件监听时会出现各种的内存问题,你不得不承认尽量少使用事件编程是个明智的做法。
于是 事件委托 就出现了。
当页面上某个元素上的事件触发时,而在 DOM 继承关系上,这个元素的所有子元素也能接收到这个事件,这时你可以使用一个在父元素上的事件处理器来处理,而不是使用一堆的各个子元素上的事件监听器来处理。究竟是什么意思?这样说吧,页面上有很多超链接,你不想直接使用这些链接,想通过一个函数来调用这个链接,HTML代码是这样的:
1 <h2>Great Web resources</h2> 2 <ul id="resources"> 3 <li><a href="http://opera.com/wsc">Opera Web Standards Curriculum</a></li> 4 <li><a href="http://sitepoint.com">Sitepoint</a></li> 5 <li><a href="http://alistapart.com">A List Apart</a></li> 6 <li><a href="http://yuiblog.com">YUI Blog</a></li> 7 <li><a href="http://blameitonthevoices.com">Blame it on the voices</a></li> 8 <li><a href="http://oddlyspecific.com">Oddly specific</a></li> 9 </ul>
常见的做法是通过循环这些链接,将每个链接上附加一个事件处理器:
01 // 典型的事件处理例子 02 (function(){ 03 var resources = document.getElementById('resources'); 04 var links = resources.getElementsByTagName('a'); 05 var all = links.length; 06 for(var i=0;i<all;i++){ 07 // Attach a listener to each link 08 links[i].addEventListener('click',handler,false); 09 }; 10 function handler(e){ 11 var x = e.target; // Get the link that was clicked 12 alert(x); 13 e.preventDefault(); 14 }; 15 })();
我们用一个事件处理器也能完成这项任务:
01 (function(){ 02 var resources = document.getElementById('resources'); 03 resources.addEventListener('click',handler,false); 04 function handler(e){ 05 var x = e.target; // get the link tha 06 if(x.nodeName.toLowerCase() === 'a'){ 07 alert('Event delegation:' + x); 08 e.preventDefault(); 09 } 10 }; 11 })();
因为点击事件就发生在这些页面元素里,你要做的就是比较它们的 nodeName,找出应该回应这个事件的那个元素。
免责声明:上面说的这两个关于事件的例子,在所有浏览器里都能运行,除了IE6,在IE6上你需要使用一个事件模型,而不是简单的W3C的标准实现。这也就是我们推荐使用一些工具包的原因。
这种方法的好处并不是仅限于把多个事件处理器缩减为一个。你想想,举个例子,你需要动态的往这个链接表里追加更多的链接。使用事件委托后,你就不需要做其它修改了;否则的话,你需要重新循环这个链接表,重新给每个链接安装事件处理器。
匿名函数和模块化
在JavaScript里最令人懊恼的事情是变量没有使用范围。任何变量,函数,数组,对象,只要不在函数内部,都被认为是全局的,这就是说,这个页面上的其它脚本也可以访问它,而且可以覆盖重写它。
解决办法是,把你的变量放在一个匿名函数内部,定义完之后立即调用它。例如,下面的写法将会产生三个全局变量和两个全局函数:
1 var name = 'Chris'; 2 var age = '34'; 3 var status = 'single'; 4 function createMember(){ 5 // [...] 6 } 7 function getMemberDetails(){ 8 // [...] 9 }
如果这个页面上的其它脚本里也存在一个叫 status 的变量,麻烦就会出现。如果我们把它们封装在一个 myApplication 里,这个问题就迎刃而解了:
01 var myApplication = function(){ 02 var name = 'Chris'; 03 var age = '34'; 04 var status = 'single'; 05 function createMember(){ 06 // [...] 07 } 08 function getMemberDetails(){ 09 // [...] 10 } 11 }();
但是,这样一来,在函数外面就没有什么功能了。如果这是你需要的,那就可以了。你还可以省去函数的名称:
01 (function(){ 02 var name = 'Chris'; 03 var age = '34'; 04 var status = 'single'; 05 function createMember(){ 06 // [...] 07 } 08 function getMemberDetails(){ 09 // [...] 10 } 11 })();
如果你想在函数外面也能使用里面的东西,那就要做些修改。为了能访问 createMember() 或 getMemberDetails(),你需要把它们变成 myApplication的属性,从而把它们暴露于外部的世界:
01 var myApplication = function(){ 02 var name = 'Chris'; 03 var age = '34'; 04 var status = 'single'; 05 return{ 06 createMember:function(){ 07 // [...] 08 }, 09 getMemberDetails:function(){ 10 // [...] 11 } 12 } 13 }(); 14 //myApplication.createMember() 和 15 //myApplication.getMemberDetails() 就可以使用了。
这被称作 module 模式或 singleton。Douglas Crockford 多次谈到过这些,Yahoo User Interface Library YUI 里对此有大量的使用。但这样一来让我感到不便的是,我需要改变句式来使函数和变量能被外界访问。更甚者,调用时我还需要加上myApplication 这个前缀。所以,我不喜欢这样做,我更愿意简单的把需要能被外界访问的元素的指针导出来。这样做后,反倒简化了外界调用的写法:
01 var myApplication = function(){ 02 var name = 'Chris'; 03 var age = '34'; 04 var status = 'single'; 05 function createMember(){ 06 // [...] 07 } 08 function getMemberDetails(){ 09 // [...] 10 } 11 return{ 12 create:createMember, 13 get:getMemberDetails 14 } 15 }(); 16 //现在写成 myApplication.get()和 myApplication.create() 就行了。
我把这个称作 “revealing module pattern.”
可配置化
一旦我把所写的JavaScript代码发布到这个世界上,就有人想改动它,通常是人们想让它完成一些它本身完成不了的任务—但通常也是我写的程序不够灵活,没有提供用户可自定义的功能。解决办法是给你的脚本增加一个配置项对象。我曾经写过一篇深入介绍JavaScript配置项对象的文章,下面是其中的要点:
configuration というオブジェクトをスクリプトに追加します。
このスクリプトを使用するときに頻繁に変更されるすべてのものが保存されます:
CSS ID とクラス名、
「画像の数」などの値。ページごとに表示されるサイズ」、「画像表示サイズ」の値、
場所、場所、および言語の設定。
このオブジェクトをパブリック プロパティとしてユーザーに返し、ユーザーが変更したり上書きしたりできるようにします。
通常、これはプログラミング プロセスの最後のステップです。これらを例にまとめました: 「スクリプトを次の開発者に引き渡す前にスクリプトに対して行うべき 5 つのこと」
実際、コードは人々にとって使いやすいものであることも必要であり、それぞれの要件に従っています。いくつかの変更が必要です。この機能を実装すると、あなたのスクリプトについて苦情を言う人々から、誰かがあなたのスクリプトを変更したのでうまく機能したという、紛らわしい電子メールを受け取ることが減ります。
バックエンドとの対話
長年のプログラミング経験で私が学んだ重要なことの 1 つは、JavaScript はインターフェイス対話の開発には優れた言語であるが、数値を処理したりデータ ソースにアクセスしたりするために使用される場合は、それはちょっと制御不能です。
当初、私は Perl を実行するためにコードを cgi-bin フォルダーにコピーしなければならないのが嫌だったので、Perl に代わる JavaScript を学びました。その後、JavaScript にすべてをさせるのではなく、バックグラウンド作業言語を使用してメインデータを処理する必要があることがわかりました。さらに重要なのは、セキュリティと言語機能を考慮する必要があるということです。
WebサービスにアクセスするとJSON-P形式でデータを取得でき、それに対してクライアントのブラウザで様々なデータ変換を行いますが、サーバーを持っている場合はさらに多くのデータ変換方法があります。 . サーバー側で JSON または HTML 形式のデータを生成してクライアントに返すことや、データのキャッシュなどの操作を行うことができます。これらを事前に理解して準備しておけば、長期的には大きなメリットが得られ、多くの頭を悩ませる必要がなくなります。すべてのブラウザで動作するプログラムを作成するのは時間の無駄です。ツールキットを使用してください。
Web 開発に携わり始めた頃、ページにアクセスするときに document.all を使用するか document.layers を使用するかという問題に長い間悩みました。私が document.layers を選択したのは、どのレイヤーも独自のドキュメントであるという考えが気に入っているからです (そして、要素を生成するためにあまりにも多くの document.write を記述していました)。 Layers パターンは最終的に失敗したため、document.all を使用し始めました。 Netscape 6 が W3C DOM モデルのみのサポートを発表したとき、私はうれしかったのですが、ユーザーはあまり気にしませんでした。ユーザーには、ほとんどのブラウザが適切に表示できるものをこのブラウザが表示できないことがわかります。これはコーディングに問題があります。私たちは現在の環境でのみ実行できる近視眼的なコードを作成していますが、残念なことに、動作環境は常に変化しています。
さまざまなブラウザやバージョンとの互換性の問題に対処するのにあまりにも多くの時間を無駄にしてきました。この種の問題にうまく対処できると、良い仕事の機会が得られます。しかし今、私たちはもうこの痛みに耐える必要はありません。
YUI、jQuery、Dojo などのいくつかのツールキットは、この種の問題に対処するのに役立ちます。これらは、バージョンの非互換性、設計上の欠陥など、さまざまなインターフェイスの実装を抽象化することでブラウザーのさまざまな問題に対処し、私たちを苦痛から救います。ブラウザのベータ版をテストする場合を除き、ブラウザの欠陥を修正するコードをプログラムに追加しないでください。ブラウザが問題のコードを修正したときにコードを削除するのを忘れる可能性があります。
その一方で、ツールキットだけに依存するのも短絡的です。ツールキットは迅速な開発に役立ちますが、JavaScript を深く理解していないと、間違った作業をしてしまう可能性もあります。