본문으로 건너뛰기

《JavaScript 언어 정수》학습 노트

무료2015-04-30#JS#JavaScript语言精粹

《JavaScript 언어 정수》중의 정수

일.in 의 용법

  1. for...in

객체의 모든 열거 가능 속성을 열거

  1. DOM/BOM 속성 검출

    if ("onclick" in elem) {
        // 요소는 onclick 을 지원
    }
    if ("onerror" in window) {
        // window 는 onerror 을 지원
    }
    
  2. 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. 「네임스페이스」사용. 즉 빈 객체. 실질은 생성하는 전역 변수를 1 개로 줄이는 것. 예를 들어 YUI 의 Y 객체, JQuery 의 JQuery 와$객체.. 예를 들어:

    var app = {};   // 네임스페이스:app
    app.Consts = {
        // 자식 네임스페이스:상수
        
        URL : {
            // 자자식 네임스페이스:URL
        }
    }
    app.Modules = {
        // 자식 네임스페이스:모듈
    }
    app.Data = {
        // 자식 네임스페이스:데이터
    }
    
  2. 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
}

장점은 다음과 같음:

  1. 호출 시에 파라미터 순서를気に할 필요가 없어지고, 인터페이스가 더 사용하기 쉬워짐

  2. 직접 JSON 객체를 전달할 수 있음

단점:구체적인 파라미터가 필요한지를 설명하는 주석을 많이 써야 함. arg 만으로는 전혀 알 수 없음

구.위조 방지 객체 (지속적 객체)

위조 방지 객체의 속성은 치환 또는 삭제 가능하지만, 해당 객체의 완전성은 손상되지 않음

지속적 객체라고도 불림. 1 개의 지속적 객체는 1 개의 단순 기능 함수의 집합

P.S. 위조 방지 객체의 개념은 책의 [함수화] 부분에 나타남. 현재는 아직 함수화가 표현하고 싶은 의미를 완전히 이해할 수 없음. 자신을 살찌운 후再看

함수화 부분은 [黯羽轻扬:《JavaScript 언어 정수》之함수화](http://ayqy.net/blog/《JavaScript 언어 정수》之함수화/) 참조. 내용은 이미 보충 완료

십.배열

본질은 키 - 밸류 페어. 즉 객체. 따라서접근 속도상의 우위는 없음

차이는 Array.prototype 이 많은 배열 조작 함수를 제공한다는 것. 또한 특수한 length 속성이 있음

주의:

  1. 배열이 실제로 점유하는 메모리 공간

    var arr = [1];
    arr[99] = 100;
    

이때 arr 이 가리키는 값은 100 개 요소의 공간을 점유하지 않음. 위의 결과는 다음과 같음:

    var arr = {
        "0" : 1,
        "99" : 100
    };

2. length 속성은 쓰기 가능

arr.length = n; 로 인덱스 값 >= n 의 모든 요소를 삭제 가능

  1. 일반적인 length 속성의 용법

카운터 방식 생략:arr[arr.length] = value;

또는 더 간단:arr.push(value);

  1. 배열 타입 검출

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.배열 조작 함수

  1. arr1.push(arr2); 는 arr2 를단일 요소로서 삽입. 예를 들어:

    var arr1 = [1];
    var arr2 = [2, 3];
    arr1.push(arr2);
    alert(arr1[2]); // undefined
    alert(arr1[1][1]);  // 3
    
  2. arr.shift() 는 arr.pop() 보다 매우 느림. 선두 요소를 삭제하려면 모든 요소의 인덱스를 업데이트해야 하지만, 말미 요소를 삭제하려면 불필요

  3. 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>$&amp;</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&nbsp;<code>/(\a+)(\b+)/</code>, was given, <code>p1</code> is the match for <code>\a+</code>, and&nbsp;<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 언어 정수》

댓글

아직 댓글이 없습니다

댓글 작성