오랫동안 JavaScript 코드를 작성했는데 언제 시작했는지 기억도 나지 않습니다. 저는 최근 몇 년간 JavaScript가 성취한 성과에 대해 매우 기쁘게 생각합니다. 이러한 성과의 수혜자가 된 것은 행운이었습니다. 나는 이 언어에 관한 꽤 많은 기사, 장, 책을 썼지만 여전히 언어에 대한 새로운 것을 발견하고 있습니다. 다음 설명은 과거에 "아!"라고 느꼈던 프로그래밍 기술이며, 나중에 우연히 발견하기를 기다리기보다는 지금 시도해야 할 기술입니다.
간결한 작성
제가 JavaScript에서 가장 좋아하는 점 중 하나는 객체와 배열을 생성하는 단축 방법입니다. 과거에는 객체를 생성하려면 다음을 수행했습니다.
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 var moviesThatNeedBetterWriters = new Array( 2 'Transformers','Transformers2','Avatar','IndianaJones 4' 3 );
축약된 버전은 다음과 같습니다.
1 var moviesThatNeedBetterWriters = [ 2 'Transformers','Transformers2','Avatar','IndianaJones 4' 3 ];
배열의 경우 실제로 문제가 없습니다. 그래프 그룹 기능. 하지만 위의 자동차를 이렇게 정의하는 사람들을 종종 보게 될 것입니다
1 var car = new Array(); 2 car['colour'] = 'red'; 3 car['wheels'] = 4; 4 car['hubcaps'] = 'spinning'; 5 car['age'] = 4;
배열은 전능하지 않습니다. 이것이 올바르게 작성되지 않으면 혼란스러울 것입니다. 그래프 그룹은 실제로 개체의 기능이며 사람들은 두 개념을 혼동합니다.
또 다른 멋진 속기 방법은 삼항 조건부 표기법을 사용하는 것입니다. 이렇게 쓸 필요는 없습니다...
var direction; if(x < 200){ direction = 1; } else { direction = -1; }
삼항 조건부 표기법을 사용하여 단순화할 수 있습니다:
var direction = x < 200 ? 1 : -1;
조건이 true일 때 값을 가져옵니다. 물음표 뒤, 그렇지 않으면 콜론 값 뒤의 값을 취합니다.
JSON 형식으로 데이터 저장
JSON을 발견하기 전에는 배열, 문자열, 중간 등 JavaScript 고유의 데이터 유형으로 데이터를 저장하기 위해 온갖 미친 방법을 사용했습니다. 글리프 및 기타 불쾌한 것들을 분할합니다. Douglas Crockford가 JSON을 발명했을 때 모든 것이 바뀌었습니다. JSON을 사용하면 자바스크립트 자체 기능을 이용해 데이터를 복잡한 형식으로 저장할 수 있고, 별도의 변환 없이 바로 접근하여 사용할 수 있다. JSON은 위에서 언급한 두 가지 약어를 사용하는 "JavaScript Object Notation"의 약어입니다. 따라서 밴드를 설명하려면 다음과 같이 작성할 수 있습니다.
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 웹사이트에서 제공하는 웹 서비스 기능을 호출하여 최신 데이터를 가져오는 것입니다. JSON 형식의 데이터 순서가 지정되지 않은 북마크 목록입니다.
기본적으로 JSON은 복잡한 데이터 구조를 설명하는 가장 이식성이 뛰어난 방법이며 브라우저에서 실행할 수 있습니다. json_decode() 함수를 사용하여 PHP에서 실행할 수도 있습니다. JavaScript의 내장 함수(Math, Array 및 String)에 대해 제가 놀랐던 점 중 하나는 JavaScript의 수학과 문자열 함수를 연구한 후 이러한 함수가 프로그래밍 작업을 크게 단순화할 수 있다는 사실이었습니다. 이를 사용하면 복잡한 루프 처리 및 조건 판단을 저장할 수 있습니다. 예를 들어, 숫자 배열에서 가장 큰 숫자를 찾는 함수를 구현해야 할 때 다음과 같이 루프를 작성하곤 했습니다.
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
이 함수는 숫자를 확인하고 가장 큰 숫자를 반환할 수 있기 때문에 브라우저의 특정 기능 지원을 테스트하는 데 사용할 수 있습니다. status:
1 var scrollTop=Math.max( 2 doc.documentElement.scrollTop, 3 doc.body.scrollTop 4 );
이는 IE 문제를 해결하는 데 사용됩니다. 현재 페이지의 scrollTop 값을 얻을 수 있지만 페이지의 DOCTYPE에 따라 위 두 속성 중 하나만 이 값을 저장하고 다른 속성은 정의되지 않으므로 Math.max를 사용하여 얻을 수 있습니다. () 숫자. JavaScript를 단순화하기 위해 수학 함수를 사용하는 방법에 대해 자세히 알아보려면 이 문서를 읽어보세요.
문자열을 조작하는 데 매우 유용한 또 다른 함수 쌍은 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配置项对象的文章,下面是其中的要点:
스크립트에 구성이라는 개체를 추가합니다.
이 개체에는 이 스크립트를 사용할 때 사람들이 자주 변경하는 모든 항목이 저장됩니다:
CSS ID 및 클래스 이름
버튼 이름, 라벨 단어 등. ;
"페이지당 이미지 수", "이미지 표시 크기"와 같은 값
장소, 위치 및 언어 설정.
사용자가 수정하고 덮어쓸 수 있도록 이 개체를 공용 속성으로 사용자에게 반환합니다.
일반적으로 이는 프로그래밍 프로세스의 마지막 단계입니다. "다음 개발자에게 스크립트를 넘기기 전에 스크립트에 해야 할 다섯 가지 작업"을 예로 요약했습니다.
사실, 일부 수정을 통해 코드를 사용하는 사람들에게도 유용하게 사용되기를 바랍니다. 각자의 필요에 맞게. 이 기능을 구현하면 스크립트에 대해 불평하는 사람들로부터 누군가가 스크립트를 수정했고 잘 작동한다는 혼란스러운 이메일을 덜 받게 됩니다.
배경과의 상호작용
수년간의 프로그래밍 경험에서 제가 배운 중요한 점은 JavaScript가 인터페이스 상호작용을 개발하는 데 탁월한 언어라는 것입니다. 데이터 소스에 액세스하는 것은 약간의 드래그입니다.
처음에는 Perl을 실행하기 위해 코드를 cgi-bin 폴더에 복사해야 하는 것이 싫었기 때문에 Perl을 대체하기 위해 JavaScript를 배웠습니다. 나중에 나는 메인 데이터를 처리하기 위해 JavaScript가 모든 일을 하게 하는 대신 백그라운드 작업 언어를 사용해야 한다는 것을 이해했습니다. 더 중요한 것은 보안과 언어 기능을 고려해야 한다는 것입니다.
웹 서비스에 접속하면 JSON-P 형식의 데이터를 얻을 수 있고 클라이언트 브라우저에서 이에 대한 다양한 데이터 변환을 할 수 있지만 서버가 있으면 변환할 수 있는 방법이 더 많아집니다 데이터를 서버 측에서 JSON 또는 HTML 형식으로 생성하여 클라이언트에 반환할 수 있을 뿐만 아니라 데이터 및 기타 작업도 캐시할 수 있습니다. 이러한 사항을 미리 이해하고 준비하면 장기적으로 이익을 얻고 많은 골치 아픈 일을 피할 수 있습니다. 모든 브라우저에서 작동하는 프로그램을 작성하는 것은 시간 낭비입니다. 툴킷을 사용하세요!
처음 웹 개발을 시작했을 때 페이지에 접근할 때 document.all을 사용할지, document.layers를 사용할지 고민하며 오랫동안 고민했습니다. 저는 모든 레이어가 자체 문서라는 생각을 좋아하기 때문에 document.layers를 선택했습니다(그리고 요소를 생성하기 위해 너무 많은 document.writes를 작성합니다). 레이어 패턴은 결국 실패하여 document.all을 사용하기 시작했습니다. Netscape 6이 W3C DOM 모델만 지원한다고 발표했을 때 기뻤지만 사용자들은 크게 신경 쓰지 않았습니다. 사용자는 이 브라우저가 대부분의 브라우저가 제대로 표시할 수 있는 것을 표시할 수 없다는 것을 알게 됩니다. 이는 우리 코딩에 문제가 있는 것입니다. 우리는 현재 환경에서만 실행될 수 있는 근시안적인 코드를 작성하고 있지만 안타깝게도 우리의 운영 환경은 끊임없이 변화하고 있습니다.
다양한 브라우저와 버전의 호환성 문제를 다루느라 너무 많은 시간을 낭비했습니다. 이런 유형의 문제를 잘 처리하면 좋은 직업 기회를 얻을 수 있습니다. 하지만 이제 우리는 이런 고통을 더 이상 참을 필요가 없습니다.
YUI, jQuery, Dojo와 같은 일부 툴킷은 이러한 종류의 문제를 처리하는 데 도움이 될 수 있습니다. 버전 비호환성, 디자인 결함 등과 같은 다양한 인터페이스 구현을 추상화하여 다양한 브라우저 문제를 처리하여 우리의 고통을 덜어줍니다. 브라우저의 베타 버전을 테스트하려는 경우가 아니면 프로그램에 브라우저의 결함을 수정하는 코드를 추가하지 마세요. 브라우저에서 문제가 해결되면 코드를 삭제하는 것을 잊어버릴 가능성이 높기 때문입니다.
반면에 툴킷에만 의존하는 것도 근시안적입니다. 툴킷은 빠르게 개발하는 데 도움이 될 수 있지만 JavaScript를 깊이 이해하지 못하면 잘못된 일을 할 수도 있습니다.