일.in 의 용법
- for...in
객체의 모든 열거 가능 속성을 열거
-
DOM/BOM 속성 검출
if ("onclick" in elem) { // 요소는 onclick 을 지원 } if ("onerror" in window) { // window 는 onerror 을 지원 } -
JS 객체의 프로토타입 속성 검출 (hasOwnProperty() 함수와 결합)
if ("attr" in obj && !obj.hasOwnProperty("attr")) { // obj 는 프로토타입 속성 attr 을 가짐 }
주의:프로토타입 속성은 동명의 인스턴스 속성에 의해 쉐도잉되므로, 이러한 검출은 불가능:
function Super(){
this.attr = 1;
}
function Sub(){
this.attr = 2;
}
Sub.prototype = new Super();
var obj = new Sub();
alert(("attr" in obj && !obj.hasOwnProperty("attr"))); /// false
이.||연산자와&&연산자
-
|| 는 기본값 채우기에 사용 가능. 예를 들어:
var obj = {}; obj.name = ""; // 주의 JS 에는 거짓값의 산:+0, -0, 0, NaN, null, undefined, "", '', false var name = obj.name || "ayqy"; alert(name);
P.S. 개인적으로 이 것은 그다지 좋지 않다고생각. 원래 속성 값이 어떠한 형태의 거짓값도 아님이 확정되지 않는 한, 기본값이 원래 값을 덮어씀
-
&& 는 undefined 값에서 속성을 읽어 TypeError 를 일으키는 것을 회피 가능. 예를 들어:
var obj = {}; //var attr = obj.obj.attr; // 에러, TypeError, undefined 값에서 속성을 읽을 수 없음 var attr = obj.obj && obj.obj.attr; // obj.obj 가 존재하고 거짓값이 아닐 때만 그 attr 을 취득
P.S. 개인적으로 && 와 별로 다르지 않고, 직접 if 로 검출하는 것이 더 좋음. 더 명확한 로직 플로우를 보일 수 있을 뿐만 아니라, 더 많은 검출 조건을 추가하기 쉬움
P.S. 이 두 가지 용법은 책의 첫 번째 부분에서 소개되었으며, 언어 특성 전시에 치우친 가능성이 있고, 권장 용법은 아님
개인적으로 이렇게 사용하지 않기를 제안. 물론, 이러한 코드를 보면 그 작용과 취약점을 이해할 수 있어야 함
삼.전역 변수 오염 감소
-
「네임스페이스」사용. 즉 빈 객체. 실질은 생성하는 전역 변수를 1 개로 줄이는 것. 예를 들어 YUI 의 Y 객체, JQuery 의 JQuery 와$객체.. 예를 들어:
var app = {}; // 네임스페이스:app app.Consts = { // 자식 네임스페이스:상수 URL : { // 자자식 네임스페이스:URL } } app.Modules = { // 자식 네임스페이스:모듈 } app.Data = { // 자식 네임스페이스:데이터 } -
IIFE(익명 함수 즉시 실행) 사용. 실질은 전역 변수를 전혀 생성하지 않음, 0 오염
(function(){ // Module1 })(); (function(){ // Module2 })();
그러나단점은 명확. 객체 캐시와 공유를 실현할 수 없고, 클로저를 나오면 없어짐
따라서일반적인做法는 네임스페이스와 IIFE 를 결합. 전체를 네임스페이스로 조직하고, 모듈 내부의 적절한 곳에서 IIFE 사용
사.4 종의 호출 모드
-
메서드 호출 모드:obj.fun(); 또는 obj"fun";
-
함수 호출 모드:fun(); 이때 this 는 global 객체를 가리킴
-
생성자 호출 모드:new Fun(); 새로운 객체의 prototype 은 Fun 의 prototype 을 가리키고, this 는 이 새로운 객체를 가리킴
-
apply 호출 모드:fun.apply(this, arrArgs);
오.arguments 객체에 대하여
arguments 객체는 배열 객체가 아니며, 모든 배열 메서드를 지원하는 것도 아님. 그냥 조금 특별한 일반 객체. 특별한 것은 그 length 속성 (동적 업데이트)
다음과 같이 테스트 가능:
function fun(){
var x = Object.prototype.toString.call(arguments);
alert(x);
var arr = [];
alert(Object.prototype.toString.call(arr));
};
fun();
// IE8:[object Object] 와 [object Array], Chrome:[object Arguments] 와 [object Array]
// 달라도 문제없음,反正둘 다 Array 는 아님
작은 테크닉:slice 메서드로 arguments 객체를 배열로 변환 가능. 예를 들어:
function fun(){
//arguments.sort(); // 에러, sort() 함수를 지원하지 않음
var arr = Array.prototype.slice.call(arguments); // 변환
arr.sort(); // 에러가 나지 않음, 변환 성공을 나타냄
alert(arr);
};
fun(3, 2); // 2, 3
주의:slice 만이 이 기효를 가짐. concat 에는 없음. 무参의 slice 와 무参의 concat 은 배열에 대한 효과는 같지만 (둘 다 복사)
육.캐스케이드 (체인 호출) 의 실현 방식
반환 값이 없는 함수에 this 를 반환하게 함. 따라서 체인 호출을 지원. 예를 들어 JQuery 중의:
$("#adBlock").removeClass("active").hide();
칠.생성자 호출 방식이 부적절하면 스코프 오염을 일으킴
new 연산자를 사용하지 않고, 직접 생성자를 호출하면, 예를 들어 Fun(); 이때 this 는 global 속성, 즉 브라우저 환경 하의 window 객체를 가리키고, 전역 스코프를 오염시킬 수 있음
팔.형인수 리스트를 단일 객체로 간소화
예를 들어:
function fun(height, width, margin, padding){ // 파라미터가 너무 김, 순서를 기억할 수 없음
// do something
}
/*
* @param {number} arg.height 高度
* @param {number} arg.width 宽度
* @param {number} arg.margin 留白
* @param {number} arg.padding 补白
*/
function fun(arg){ // 파라미터 순서를 기억할 필요가 없음
// do something
}
장점은 다음과 같음:
-
호출 시에 파라미터 순서를気に할 필요가 없어지고, 인터페이스가 더 사용하기 쉬워짐
-
직접 JSON 객체를 전달할 수 있음
단점:구체적인 파라미터가 필요한지를 설명하는 주석을 많이 써야 함. arg 만으로는 전혀 알 수 없음
구.위조 방지 객체 (지속적 객체)
위조 방지 객체의 속성은 치환 또는 삭제 가능하지만, 해당 객체의 완전성은 손상되지 않음
지속적 객체라고도 불림. 1 개의 지속적 객체는 1 개의 단순 기능 함수의 집합
P.S. 위조 방지 객체의 개념은 책의 [함수화] 부분에 나타남. 현재는 아직 함수화가 표현하고 싶은 의미를 완전히 이해할 수 없음. 자신을 살찌운 후再看
함수화 부분은 [黯羽轻扬:《JavaScript 언어 정수》之함수화](http://ayqy.net/blog/《JavaScript 언어 정수》之함수화/) 참조. 내용은 이미 보충 완료
십.배열
본질은 키 - 밸류 페어. 즉 객체. 따라서접근 속도상의 우위는 없음
차이는 Array.prototype 이 많은 배열 조작 함수를 제공한다는 것. 또한 특수한 length 속성이 있음
주의:
-
배열이 실제로 점유하는 메모리 공간
var arr = [1]; arr[99] = 100;
이때 arr 이 가리키는 값은 100 개 요소의 공간을 점유하지 않음. 위의 결과는 다음과 같음:
var arr = {
"0" : 1,
"99" : 100
};
2. length 속성은 쓰기 가능
arr.length = n; 로 인덱스 값 >= n 의 모든 요소를 삭제 가능
- 일반적인 length 속성의 용법
카운터 방식 생략:arr[arr.length] = value;
또는 더 간단:arr.push(value);
- 배열 타입 검출
typeof arr 은 "object" 를 반환하고, instanceof 연산자는跨 frame 시 무효해지므로, 배열 타입 검출은 그다지 쉽지 않음
책에는 초귀찮은 방식이 기재:
function isArray(value){
return value &&
typeof value === "object" &&
typeof value.length === "number" &&
typeof value.splice === "function" &&
!(value.propertyIsEnumerable("length"));
}
実は 저자가 책을 썼을 때는 아직 존재하지 않았던 간단한 방법이 있음:
Object.prototype.toString.call(value) === '[object Array/Function...]' 로 타입 체크 가능. 네이티브 객체와 커스텀 객체를 구분하는 데에도 사용 가능. 예를 들어:
[object JSON] // 네이티브 JSON 객체
[object Object] // 커스텀 JSON 객체
타입 검출에 대한 상세 정보는 [黯羽轻扬:JS 학습 노트 11_고급 테크닉](http://ayqy.net/blog/JS 학습 노트 11_고급 테크닉/) 참조
십일.정규 표현식
JS 의 정규 표현식 기능은 완전하지 않지만, 기본적으로 충분. 예를 들어 3 종의 모드만 지원:g/i/m 〜 글로벌 모드/대소문자 무시 모드/다행 모드
서포트하는 특수 메타 문자 (\d, \s 등) 도 비교적 적지만, 비캡처형 그룹과 정규 표현식 룩어헤드를 서포트하는 것은 매우 좋음
따라서 JS 에서 정규 표현식을 사용하려면 더 많은 테스트가 필요. 정규 표현식에 대한 상세 정보는 [黯羽轻扬��정규 표현식 학습 노트](/articles/정규 표현식 학습 노트/) 참조
또 하나자주 사용하지 않는 점:RegExp 객체의 속성
-
global:g 모드를 켰는지
-
ignoreCase:i 모드를 켰는지를 반환
-
lastIndex:다음 exec 매치의 시작 위치를 반환
-
multiline:다행 모드를 켰는지를 반환
-
source:정규 표현식 문자열을 반환
특별 주의가 필요:리터럴 방식으로 생성한 RegExp 객체경우에 따라같은 인스턴스를 참조. 예를 들어:
var regex1 = /abc/g;
var regex2 = /abc/g;
var str = "aaa\r\nabc\r\naaa";
alert([regex1.lastIndex, regex2.lastIndex]); // 0, 0
regex1.test(str);
alert([regex1.lastIndex, regex2.lastIndex]); // 8, 0
alert(regex1 === regex2); // false
구 버전 브라우저에서는 마지막 2 행은 8, 8 과 true 를 출력. 정규 표현식 리터럴 인스턴스 공유는 ES3 의 규정とのこと.本机 테스트에서는 IE8 은 문제 존재하지 않지만, 이론적으로는 IE6 은 이 문제가 존재 (IE6 이 나왔을 때, ES5 는 아직 나오지 않음)
십이. 네이티브 객체 조작 함수
P.S. 여기서는 특히 주의가 필요한 점만 소개. 완전한 각종 조작 함수는 [黯羽轻扬:JS 학습 노트 1_기초와 상식](http://ayqy.net/blog/JS 학습 노트 1_기초와 상식/) 참조
###1.배열 조작 함수
-
arr1.push(arr2); 는 arr2 를단일 요소로서 삽입. 예를 들어:
var arr1 = [1]; var arr2 = [2, 3]; arr1.push(arr2); alert(arr1[2]); // undefined alert(arr1[1][1]); // 3 -
arr.shift() 는 arr.pop() 보다 매우 느림. 선두 요소를 삭제하려면 모든 요소의 인덱스를 업데이트해야 하지만, 말미 요소를 삭제하려면 불필요
-
arr.unshift(). 배열 선두에 요소를 삽입. IE6 은 undefined 를 반환. 본래는 새로운 배열의 길이를 반환해야 함
###2.객체 조작 함수
obj.hasOwnProperty("hasOwnProperty") 는 false 를 반환
###3.정규 표현식 객체 조작 함수
regex.exec(str) 함수의 기능이 가장 강력. 물론, 실행 속도도 가장 느림
regex.test(str) 가 가장 간단하고 가장 빠름.주의:g 모드를 켜지 말 것. 순수한 낭비 (test 는 매치/매치 안 함만 반환. 1 회의 스캔으로 충분)
###4.문자열 조작 함수
- str.replace(regex, fun); 는 매우 사용하기 좋은 함수. regex 로 타겟 부분을 매치할 수 있고, fun 으로 매치 부분을 더 처리할 수 있음
특별 주의:regex 가 g 모드를 켜지 않은 경우, 첫 번째 매치 부분만 치환.전 문자열 치환이 아님
fun 의 각 파라미터 의미는 다음과 같음:
<table class="fullwidth-table"><tbody><tr><td class="header">Possible name</td><td class="header">Supplied value</td></tr><tr><td><code>match</code></td><td>The matched substring. (Corresponds to <code>$&</code> above.)</td></tr><tr><td><code>p1, p2, ...</code></td><td>The <em>n</em>th parenthesized submatch string, provided the first argument to <code>replace()</code> was a <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp" title="The RegExp constructor creates a regular expression object for matching text with a pattern."><code>RegExp</code></a> object. (Corresponds to <code>$1</code>, <code>$2</code>, etc. above.) For example, if <code>/(\a+)(\b+)/</code>, was given, <code>p1</code> is the match for <code>\a+</code>, and <code>p2</code> for <code>\b+</code>.</td></tr><tr><td><code>offset</code></td><td>The offset of the matched substring within the total string being examined. (For example, if the total string was <code>'abcd'</code>, and the matched substring was <code>'bc'</code>, then this argument will be 1.)</td></tr><tr><td><code>string</code></td><td>The total string being examined.</td></tr></tbody></table>
-
str.replace(regex, replacement); 의 replacement 부분 중의$는 특수한 의미를 가짐:
-
$$:$를 나타냄. 치환 부분 중에$가 있는 경우$로 이스케이프 필요
-
$&:매치한 텍스트 전체를 나타냄
-
$n:예를 들어$1, $2, $3... 는 캡처한 텍스트를 나타냄
-
$`:매치 부분より前の 텍스트를 나타냄
-
$':매치 부분より後の 텍스트를 나타냄
-
더 상세한 예는 W3School:JavaScript replace() 메서드 참조
-
str.split(regex); 에는 특례가 존재.특별 주의가 필요:
var str = "a & b & c"; // & 는 구분자 var arr = str.split(/\s*(&)\s*/); // 캡처 포함 var arr2 = str.split(/\s*&\s*/); // 캡처 포함하지 않음 var arr3 = str.split(/\s*(?:&)\s*/); // 비캡처 포함 alert(arr); // [a, &, b, &, c] alert(arr2); // [a, b, c] alert(arr3); // [a, b, c]실수로 캡처형 그룹을 포함한 정규 표현식을 사용하면, 결과가 예상과 다를 수 있음
십삼.糟粕
P.S. 여기서는 책의 내용을 그대로 복제할 생각은 없고, 필자가 동의하는 가장 최악인 부분만 열거
-
자동 세미콜론 삽입. 확실히 좋지 않음. 전형적인 ASI 에러:
function fun(){ return { attr : 1; } } alert(fun().attr); // TypeError: Cannot read property 'attr' of undefined -
typeof. 사용하기 어려움. 이는 설계상의 실수. 역사적 원인
-
paseInt() 와 8 진수.매우 숨겨진 에러:
var year = "2015"; var month = "08"; var day = "09"; alert(parseInt(year)); // IE8:2015 Chrome:2015 alert(parseInt(month)); // IE8:0 Chrome:8 alert(parseInt(day)); // IE8:0 Chrome:9
0 으로 시작하는 수치는 8 진수로 간주되고, 2 번째 파라미터를 생략한 parseInt() 는 08/09 를 8 진수로 해석하므로, 결과는 0
시간 날짜를 처리하는 것은 해석 에러를 일으키기 쉬우므로,最好parseInt() 의 2 번째 파라미터를 생략하지 않음
P.S. Chrome 중은 정상. 아마도 어떤 ES 버전에서 parseInt() 에 변경이 있고, Chrome 이 실현한 것. 따라서糟粕은 영원히 존재하는 것은 아님. 물론,糟粕인지도 programmer 가 어떻게 사용하는지에 달림..
- Unicode.JS 의 Unicode 서포트는 좋지 않음. 역사적 원인 but, 서포트하지 않는 것은 서포트하지 않음
P.S. JS 가 나왔을 때 Unicode 자신도 100 만 개의 문자가 될 것이라고는 생각하지 못했음. JS 의 문자는 16 비트로, 최초의 65536 개의 Unicode 문자에 대응 가능. Unicode 는 확장을 위해, 나머지 각 문자를 1 쌍의 문자로 나타냄. 그러나 JS 는 이러한 문자를 2 개의 문자로 간주. 따라서..
십사.鷄肋
P.S. 상동. 필자가 동의하는 것만 열거
-
continue 퍼포먼스가 낮음. if 로消除 가능
-
비트 연산 퍼포먼스가 낮음.(다른 책은 이 점에 언급하지 않음) double -> int -> 비트 연산 -> double 과折腾할 필요가 있음
-
new 는 도대체 사용해야 하는가 사용하지 않아야 하는가. 사용하면 스코프를 오염시킬 수 있음
-
void 연산자. 단항 연산. undefined 를 반환. 따라서 IIFE 실현에 사용 가능. 예를 들어:
void function(){ // do something }();
저자는 void 를 사용하지 않기를 제안. 무엇을 하는 것인지 아는 사람이 거의 없기 때문
십오.JSON
정수의 선두는 0 이 될 수 ���음. 더글러스 (JSON 을 발명한 할아버지) 본인이 말함. 수치는 정수, 실수 또는 과학 계수 가능. 그러나 선두는 0 이 될 수 없음. 8 진수의 애매함을 피하기 위해
테스트 코드는 다음과 같음:
var json = '{"number" : 9}';
var obj = JSON.parse(json);
alert(obj.number); // 9
var json = '{"number" : 09}'; // 선두 0 에 주의
var obj = JSON.parse(json);
alert(obj.number); // 에러
// Chrome:SyntaxError: Unexpected number
// IE8:문법 에러
// FF:SyntaxError: JSON.parse: expected ',' or '}' after property value in object
참고 자료:
- 《JavaScript 언어 정수》
아직 댓글이 없습니다