자바스크립트는 데이터타입(자료형)에 대해 상당히 관대한 언어이다. Show 다른 언어가 변수를 선언 할 때 자료형까지 결정 하는 반면, 자바스크립트는 선언시 변수의 스코프(범위)와 존재만 설정하고, 할당 할 때 비로소 자료형이 결정된다. (다른 언어를 했었는지 어떤 개발자가 변수를 선언하고 초기화를 하지 않으면 쓰레기 값이 된다고 하는 것을 보았는데, 자바스크립트에 대해 잘 모르고서 하는 소리인것 같다.) 그래서인지, 자바스크립트를 배우는 대부분의 사람들은, 그리고 가르치는 사람들은 자료형에 대한 고민을 거의 하지 않는 편이다. 고려하지 않아도 프로그래밍을 하는데 큰 문제가 없다(?)고 생각하는 것이다. 하지만 조금만 깊이있게 들어가면 자바스크립트도, 자료형에 대한 고민을 하지 않고서는 문제가 발생하는 경우가 많다. 그 예가 바로 undefined와 null이다. 많은 사람들이 이 두가지를 구분하지 못해서 어려움을 겪는 경우가 있는데, 이 둘은 다음과 같은 차이가 있다. undefined -> 변수를 선언만 하고 값을 할당하지 않음. 즉, 자료형이 결정되지 않은 상태이다. (선언하지 않은 변수도 콘솔이나 기타 메세지에는 undefined라고 뜨지만, undefined라는 값을 가지는 것은 아니다.) null -> 변수를 선언하고, 'null'이라는 빈 값을 할당한 경우이다. (이 '빈 값'의 경우 자료형에 따라 여러가지가 있지만, null은 객체형 데이터-ex: array, object-의 빈 값을 의미한다. 문자열(string)의 경우 '', 숫자(number)의 경우 0이 빈값이고, 이들 빈값 모두는 if문에서 false로 형 변환된다.) 다시말해서, undefined는 자료형이 결정되지 않은 변수이고, null은 자료형은 객체인데, 비어있는 변수이다. ++추가 ------------- 이 글을 본 지인이 왜 그러면 console.log(undefined==null)이 true냐고 반박을 해서 글을 추가합니다. 비교연산자 ==는 자료형이 다르면 자동 형변환으로 자료형을 강제로 맞춰서 비교하는 비교연산자입니다. undefined와 null(object)은 자료형이 다르니 자바스크립트 엔진에서 알아서 통일해서 둘다 값이 없는거니까 true를 반환합니다. 이 경우 === 연산자(자료형까지 비교)를 사용하면 원하는 결과를 얻을 수 있습니다. 어째서 재입문일까요? 왜냐하면, JavaScript는 세계에서 가장 오해받고 있는 프로그래밍 언어로 악명이 높기 때문입니다. 종종 장난감같다고 조롱당하기도했지만, 이 거짓말같은 단순함 아래에는 몇 가지의 강력한 언어 기능이 숨어 있습니다. Javascript는 현재 엄청나게 많은, 요즘 가장 뜨고있는 애플리케이션들에 사용되고 있어서, 웹 또는 모바일 개발자 누구에게라도 이 기술에 대한 깊은 지식이 중요한 기량이 된다는 것을 보여주고 있습니다. 이 이야기를 이해하는데는 이 언어의 역사를 먼저 보는 것이
도움이 됩니다. JavaScript는 1995년 Netscape의 엔지니어 Brendan Eich에 의해 만들어졌고, 1996년 초에 Netscape 2와 함께 처음 릴리즈 되었습니다. 이것은 원래 LiveScript로 불리기로 되어 있었습니다만 Sun Microsystem의 Java 언어의 성공에 편승해보려고 -두 언어 사이의 공통점이 매우 적음에도 불구하고- 불행이 예견된 마케팅 결정에 따라 이름이 바뀌게 됩니다. 이 결정은 역사상 유래가 없는 혼란의 근원이 되어버립니다. 몇 달 후, Microsoft는 IE3와 함께 JScript를 발표했습니다. 이 JScript는 Javascript를 정말 닮았고 호환성이 좋았습니다. 몇 달 뒤에, Netscape는 1997년에 ECMAScript 표준의 첫번째 판이 되는
JavaScript를 유럽 표준화 단체인 Ecma International에 보냅니다. 이 표준은 1999년에 ECMAScript edition 3에 따라 큰 규모의 개정을 거친 후, 유례없이 아주 안정된 상태로 계속 유지되고 있습니다.4번째 판은 중도 포기되었는데, 언어의 복잡성 증가에 관련한 정치적 문제 때문이었습니다. 이 4번째 판의 많은 파트들은 ECMAScript edition 5 (2009년 12월에 출간)와 6번째 개정판 규격(2015년에 출간)의 근간을 형성하고 있습니다. 참고: 이제부터는 ECMAScript를 우리에게 좀 더 친근한
말인 "JavaScript"라고 부르겠습니다. 대부분의 프로그래밍 언어와는 달리, JavaScript 언어는 입출력 개념이 없습니다. 이 언어는 호스트 환경 아래에서 스크립트 언어로서 동작하도록 디자인 되어있고, 따라서 외부 세계와 통신하기위해 호스트 환경이 제공하는 메커니즘에 의존합니다. 대부분의 경우 일반적인 호스트 환경은 브라우저이지만 JavaScript 인터프리터는 Adobe Acrobat, Photoshop, SVG images, Yahoo! 위젯 엔진 등의 제품에서도 발견할 수 있고, node.js 와 같은 서버 측 환경에서도 찾을 수 있습니다. 하지만 JavaScript가 사용되는 분야는 계속 더 넓혀지고 있습니다. NoSQL 데이터베이스, Apache CouchDB,
임베디드 컴퓨터, GNU/Linux OS의 가장 유명한 GUI 인 GNOME 과 같은 데스크톱 환경에서도 JavaScript가 사용됩니다. JavaScript는 유형 및 연산자, 표준 내장 객체 및 메서드가 있는 다중 패러다임, 동적 언어입니다. 구문은 Java 및 C 언어를 기반으로합니다. 이러한 언어의 많은 구조가 JavaScript에도 적용됩니다. JavaScript는 클래스 대신 객체 프로토 타입을 사용하여 객체 지향 프로그래밍을 지원합니다
(프로토 타입 상속 및 ES2015 classes. JavaScript는 함수형 프로그래밍도 지원합니다. 함수는 객체이며, 함수는 실행 가능한 코드를 유지하고 다른 객체와 마찬가지로 전달 될 수 있습니다. 어떤 언어에서라도 기초가 되는 부분인 타입을 살펴보는 것부터 시작해봅시다. JavaScript 프로그램은 값을 다루고 해당 값은 모두 타입을 가지고 있습니다. JavaScript의 타입은 다음과 같습니다. 그리고 약간 특별한 타입인 undefined와 null이 있습니다. 또한 객체의 특별한 종류인
배열(Array) 객체. 그리고 자유롭게 사용할 수 있는 날짜(Date) 객체 와 정규식(RegExp) 객체가 있습니다. 그리고 기술적으로 정확히 말해 함수(Function)는 단지 객체의 특별한 타입으로 취급됩니다. 따라서 타입 구조도를 정리해보면 다음과 같습니다. 그리고 또 몇 가지
오류 타입이 내장되어 있습니다. 그렇지만 처음 구조도를 기억하고만 있으면 다른 것들도 아주 쉽게 이해할 수 있을 것입니다. 설계 명세서에 의하면 JavaScript에서 수는
"이중정밀도 64비트 형식 IEEE 754 값" (numbers between -(2^53 − 1) and 2^53 − 1)으로 정의됩니다. 이것은 몇가지 흥미로운 결과를 가져옵니다. JavaScript에는 정수와 같은 것이 존재하지 않으므로 ( "명백한 정수"는 사실 "암묵적으로 실수"입니다. 또한, 다음과 같은 것들을 주의하세요.
실제로 정수 값은 32 비트 정수로 처리되며 일부 구현은 32 비트 정수가 아닌 숫자에 유효한 명령어를 수행 할 때까지 이러한 방식으로 저장합니다. 이는 비트 단위 작업에 중요 할 수 있습니다. 덧셈, 뺄셈, 계수 (또는 나머지) 연산을 포함하는 표준 산술 연산자가 지원됩니다. 또한 앞에서 언급하는 것을 깜박 잊은 고급 수학 함수와 상수를 다루기 위한 수학(Math)으로 불리는 내장 객체가 있습니다.
내장
구형 브라우저에서 "0"으로 시작하는 문자열은 8 진수 (기수 8)로 가정되지만, 2013 년 이후에는 그렇지 않습니다. 문자열 형식이 확실하지 않으면 이전 브라우저에서 놀라운 결과를 얻을 수 있습니다.
이 같은 결과는
만약 이진수를 정수로 변환하고 싶다면, 밑을 바꾸기만하면 됩니다. 이와 비슷하게, 내장 함수 단항 연산자
문자열이 수가 아닌 경우
내장
하지만
직관적이지 않은 동작을 하는 전역
JavaScript는 또 특별한 값
내장 함수
참고:
문자열 (Strings)JavaScript에서 문자열은 유니코드 문자들이 연결되어 만들어진 것입니다. 이는 국제화(i18n, internationalization) 하려하는 누구에게라도 환영받을만한 소식입니다. 좀 더 정확히 말하자면, 각각이 16비트 숫자로 표현된 UTF-16 코드 유닛이 길게 이어져있는 것입니다. 각 유니코드 문자는 1개나 2개의 코드 유닛으로 표현됩니다. 한 개의 문자를 나타내려면 길이가 1인 문자열을 사용하면 됩니다. 문자열의 길이를 알고싶다면, 해당 문자열의 우리의 첫 JavaScript 객체입니다! 문자열도 역시 객체로 취급된다고 언급했던적이 있죠? 다음과 같이 메서드까지 있는 확실한 녀석입니다.
이외의 타입들JavaScript는 의도적으로 값이 없음을 나타내는 값인 JavaScript는
이 변환은
하지만 이렇게 할 필요는 거의 없습니다. JavaScript는 이러한 변환 작업을 부울 연산자는 논리 연산자 변수 (Variables)JavaScript에서 새로운 변수는
아래는
변수에 값을 지정하지 않고 변수를 선언하면, 타입은 JavaScript와 Java 같은 다른 언어 사이의 중요한 차이점은 블록에 스코프가 없었다는 것입니다. 함수에만 스코프가 있었습니다. 변수를 복합문에서 (예를 들어 연산자 (Operators)JavaScript의 산술 연산자로는
문자열에 어떤 수 (또는 다른 값)를 더하면 일단 모두 문자열로 바뀌게 됩니다. 다음 예를 보시면 무슨 얘기인지 아실 수 있을겁니다.
빈 문자열에 어떤 값을 더하는 것은 해당 값을 문자열로 바꾸는 요령입니다. JavaScript에서 비교는
타입 강제 변환을 하지 않게 하려면, 삼중 등호 연산자 (
이와 비슷하게 JavaScript는 값을 이진 비트 연산자도 가지고 있습니다. 사용하고 싶을 때 언제라도 사용할 수 있도록 말이죠. 제어 구조JavaScript는 C 계열의 다른 언어들과 비슷한 제어 구조를 가지고 있습니다. 조건문은
JavaScript는
JavaScript의
JavaScript에는 두개의 중요한 for 반복문 또한 포함됩니다.
첫번째로
그리고
또는 (거짓 같은 값이 유효하지 않은 값일 때) 캐싱 값에 대해서도 사용합니다.
JavaScript는 한줄로 조건문을 쓸 수 있게 해주는 삼항 연산자도 가지고 있습니다.
default 절은 선택사항입니다. switch와 case 부분에서 둘 다 표현식을 사용할 수도 있습니다. switch 부분과 case 부분의 표현식은
객체 (Objects)JavaScript 객체는 간단히 이름-값 쌍(name-value pairs)의 모임입니다. 그렇기 때문에, JavaScript의 객체의 모임은 다음과 비슷하다고 할 수 있습니다.
이 데이터 구조가 매우 광범위하게 사용된다는 사실은 활용 방도가 다양함을 입증합니다. JavaScript 내 모든 것 (코어 타입들은 제외)은 객체로 취급되기 때문에 어떤 JavaScript 프로그램도 기본적으로 해쉬 테이블을 검색하는데 필요한 출중한 성능을 가지고 있습니다. 매우 빠르기 때문에 장점이 됩니다! 값은 객체를 포함하여 모든 JavaScript 값이 될 수 있는 반면, "이름" 부분은 JavaScript 문자열입니다. 이는 사용자가 원하는 어떠한 데이터 구조로 만들 수 있도록 해줍니다. 빈 객체를 생성하는데 두 가지 방법이 있습니다. 그리고, 이들은 의미적으로 동치입니다. 두 번째 방법은 객체 리터럴 구문이라고 부르며 더 편리합니다. 객체 리터럴 구문은 JSON 구문의 핵심이며 이 방법을 사용한 코드를 더 많이 볼 수 있습니다. 객체 리터럴 구문으로 객체의 전체적인 구조를 초기화 할 수 있습니다.
속성에 연속적으로 접근할 수 있습니다.
아래 예제는 객체 프로토타입(
일단 생성되면, 객체의 속성에 다음의 두 가지 방법들 중 한 가지로 접근할 수 있습니다.
그리고...
이들은 의미적으로 역시 같습니다. 두 번째 방법은 속성의 이름을 실행 시간(run-time)에 계산할 수 있는 문자열로 전달합니다. 하지만 이 방법을 사용하면 일부 JavaScript 엔진과 압축기 최적화(minifier optimizations)를 적용할수 없습니다. 하지만 예약 키워드로 정의된 이름으로 속성을 설정하거나 얻어낼 수 있습니다.
참고: ECMAScript 5부터, 예약어는 객체 항목의 이름으로 "덧붙임없이" 사용할 수 있습니다. 이 말은 객체 리터럴을 정의할 때 따옴표로 "둘러쌀" 필요가 없다는 의미입니다. ES5 Spec을 참고해 보세요. 객체나 프로토타입에 대한 좀 더 상세한 내용은 Object.prototype (en-US)을 참조하세요. 객체 프로토타입과 객체 프로토타입 체인에 대한 설명은 상속과 프로토타입 체인을 참조하세요. 참고: ECMAScript 2015부터, 객체의 키를 대괄호 표기법(bracket notation)으로 변수를 통해 정의할 수 있습니다.
그냥 배열 (Arrays)JavaScript에서 배열은 실제로는 특별한 타입의 객체입니다. (숫자로 나타낸 속성은 자연스럽게 배열을 생성하는 예전 방법은 다음과 같습니다.
더 편리한 배열 표기법은 배열 리터럴을 사용하는 것입니다.
배열의 length 속성은 최대 인덱스에 하나를 더한 값일 뿐이라는 걸 기억해주세요. 존재하지 않는 배열 인덱스를 참조하려고 하면 다음과 같이
ES2015는 배열과 같은 이터러블 객체를 위해 좀더 간결한
또한 배열에 대한 또 다른 반복 방법은 ECMAScript 5에 추가된 forEach()입니다.
배열에 항목 하나를 추가하고 싶으면 이렇게 하면 됩니다. 배열은 몇가지 메서드가 제공됩니다. 배열 메서드에 대한 전체 문서를 참조하세요.
함수 (Functions)객체와 마찬가지로, 함수는 JavaScript를 이해하는데 핵심이 되는 컴포넌트입니다. 가장 기본적인 함수의 예시는 다음과 같습니다.
이 예시는 기본 함수를 보여주고 있습니다. JavaScript 함수는 0개 이상의 이름이 있는 매개변수를 가질 수 있습니다. 함수의 본체에는 갯수 제한없이 구문을 작성할 수 있고 해당 함수에 지역적인 변수를 선언할 수 있습니다.
이름을 가진 매개변수들은 다른 어떤 것보다도 해당 함수가 어떤 함수인지 잘 설명해줄 수 있습니다. 해당 함수가 원하는 매개변수를 전달하지 않고 함수를 호출할 수 있지만 그럴 경우 해당 변수들은
함수가 기대하는 매개변수보다 많은 매개변수를 넘겨줄 수도 있습니다.
이 예는 조금 어리석어 보이지만, 함수는 추가적로 전달한 매개변수를 함수 내부에서 접근할 수 있습니다. 이
확실히
이건 꽤 유용합니다만, 좀 번잡해보입니다. 코드 크기를 조금 더 줄이기 위해, arguments 객체의 사용을
나머지 매개변수 문법으로 변경할 수 있습니다. 이 방법으로, 코드 크기는 최소한으로 유지 하면서, 개수 제한없이 함수로 인자를 전달할수 있습니다. 나머지 매개변수 연산자는
위의 코드에서 매개변수 args는 함수로 전달된 모든 값을 가집니다. 나머지 매개변수 연산자는 함수 매개변수 목록의 마지막에만 위치할 수 있으며, 선언 위치 이전을 제외한 이후의 인자만을 저장합니다.
즉, function avg(firstValue, ...args) 에서 함수에 전달한 첫 번째 값은 firstValue 변수에 저장되며, 남은 인자들이 args에 저장됩니다. 이건 또 다른 유용한 언어 특성이지만 새로운 문제도 발생합니다.
하지만 우리가 이미 만든 함수를 재사용할 수 있으면 좋을 것입니다. 다행히 JavaScript는 함수 객체라면 모두 가지는
함수 호출 시 전개 연산자(spread operator)를 이용하여 익명 함수JavaScript에서는 익명 함수를 만들 수 있습니다. 실제로, 이런 이름없는 함수들은 다른 함수의 인자로 전달하거나 변수에 할당하여 호출할 수 있습니다.
위에 정의된 익명 함수는 인수와 함께 JavaScript에서 익명 함수는 인자로 전달하거나 변수에 할당하는 것 외에도 단일 표현식으로 함수 선언과 동시에 호출하는 방법을 제공합니다. 이 방법을 즉시 실행 함수 표현식(IIFE, Immediately Invoked Function Expressions)이라고 하며, 익명 함수를 포함한 다음과 같은 구문으로 사용할 수 있습니다. IIFE에 대한 더 자세한 설명은 이 입문 내용의 범위를 벗어난 것입니다. 하지만 IIFE가 어떤 상황에 특히 유용한지에 대한 좋은 예제가 클로저 문서의 클로저를 이용해서 프라이빗 메서드 흉내내기 구획에 나와 있습니다. 재귀 함수JavaScript는 재귀적으로 함수를 호출할 수 있습니다. 이는 브라우저 DOM 등에서 볼 수 있는 트리 구조를 다루는데 유용합니다.
다음의 예는 익명 함수를 사용함에 있어 잠재적인 문제점을 보여줍니다. 이름이 없으면 어떻게 재귀적으로 부를 수 있을까요? JavaScript는 이름을 붙인 즉시 실행 함수 표현식(IIFE, Immediately Invoked Function Expressions)을 다음과 같이 지원합니다.
위와 같이 함수 표현식에 제공된 이름은 함수 자체 스코프에서만 사용할 수 있습니다. 이 특징은 엔진에 의한 최적화뿐만 아니라 코드 가독성을 높이는데 도움을 줍니다. 이 이름은 디버거와 스택 추적에서도 나타나므로 디버깅 시간을 줄일 수 있게 합니다. JavaScript 함수는, JavaScript 내의 다른 모든 것들과 마찬가지로, 그 자체가 객체이며, 객체 섹션에서 이미 확인한 것처럼 속성을 추가하거나 변경할 수 있다는 점을 기억해주세요. 사용자 정의 객체고전 객체지향 프로그래밍에서 객체는 데이터와 해당 데이터들을 다루는 메서드의 집합이었습니다. JavaScript는 프로토타입 기반 언어로, C++ 이나 Java에서 발견할 수 있는 클래스 구문이 없습니다(이런 이유로 클래스 구문에 익숙한 프로그래머들이 때때로 혼란을 경험합니다). 그 대신, JavaScript는 함수를 클래스로 사용합니다. 이름과 성을 필드로 가지고 있는 'person' 객체를 고려해보도록 합시다. 이름을 표시하는 두 가지 방법이 있을 수 있습니다. 예를 들어, "이름 성" 또는 "성, 이름" 이런 식으로 말이죠. 이전에 다룬 함수와 객체를 사용해서 이를 표현하면 다음과 같습니다.
이렇게 하면 작동하긴 하지만, 보기 안좋습니다. 이런 방법이라면 전역 이름공간(global namespace)에 관련 함수가 너무 많아집니다. 정말 우리에게 필요한 것은 객체에 함수를 붙여놓는 것입니다. 함수는 객체이기 때문에 이건 별로 어렵지 않습니다.
아래 예시와 같이
위의 예시는 개선된 함수는 여전히 우리의 person 객체가 점점 개선되고 있지만, 아직 좀 보기 안좋은 면이 있습니다. 매번 person 객체를 만들 때마다 내부에서 2개의 새로운 함수 객체를 만들고 있습니다. 이 코드가 객체 간에 공유된다면 더 낫지 않을까요?
더 좋아 보이네요. 메서드 함수를 한번만 만들고, 컨스트럭터 내에 해당 메서드들을 참조하도록 할당합니다. 이보다 더 개선 할 수 있을까요? 네, 그렇게 할 수 있습니다.
이것은 정말 강력한 도구로, JavaScript에서는 프로토타입을 프로그램에서 언제든 변경할 수 있습니다. 기존 객체에 부가적인 메서드를 실시간으로 추가할 수 있다는 것입니다.
흥미롭게도, JavaScript의 내장 객체의 프로토타입에도 뭔가를 더 추가할 수 있습니다.
우리가 추가한 새로운 메서드는 심지어 문자열 상수에서도 동작합니다!
기존에 언급한 바와 같이,
프로토타입은 체인의 한 부분을 이룹니다. 해당 체인의 최상위에는
이 구현은, 설명하긴 어렵지만, 프로토타입 체인을 설정하지 않았으므로 그러므로 이렇게 호출하는 것은
아래와 거의 동일합니다.
내장 함수 (Inner functions)JavaScript의 함수 선언은 다른 함수 내부에서도 가능합니다. 우리는
이런 특성은 유지보수가 용이한 코드를 만드는데 유용합니다. 함수가 다른 한두 개의 함수에서만 호출되며 그 외의 다른 코드에서는 사용이 안되는 경우라면 함수를 중첩시키는 것이 좋습니다. 이런 방법을 통해 전역 스코프에 함수의 개수를 늘리지 않는 것은 좋은 습관입니다. 또한 전역 변수에 대한 유혹을 뿌리칠 수 있는 좋은 대안이 됩니다. 복잡한 코드를 작성하게 될 때면 여러 함수들 간에 값을 공유하기 위한 용도로 전역 변수를 사용하고 싶어집니다. 하지만 전역 변수는 유지보수를 힘들게 합니다. 이런 상황에 중첩 함수는 부모의 변수를 공유함으로써 전역 이름공간을 더럽히지 않고 함수들을 연동할 수 있습니다. "지역 전역"이라고 불러도 괜찮겠네요. 이 기술을 사용할 때는 주의를 요하겠지만, 반드시 알아둬야 할 유용한 기술입니다. 클로저 (Closures)클로저는 JavaScript가 제공하는 가장 강력한 추상화이며, 동시에 잠재적으로 가장 혼란스러울 수 있는 개념입니다. 다음 함수는 무엇을 하는 걸까요?
여기서 일어나는 일은 함수 내부에서 정의된 어떤 함수가 외부 함수의 변수에 접근한다는 점에서 앞서 언급한 내장 함수에서 일어나는 일과 매우 비슷합니다. 유일하게 다른 점은 내장 함수가 반환되었기 때문에 외부 함수의 변수는
사라지는 것처럼 보인다는 것입니다. 하지만, 외부 함수의 변수들은 여전히 "존재합니다". 그렇지 않다면 'adder' 함수는 동작하지 않을 것입니다. 이에 더해서
이것은 실제로 일어나는 일입니다. JavaScript 함수가 실행될 때는 언제나 '스코프' 객체가 생성되며, 해당 함수 내에서 생성된 지역 변수를 저장하고 있습니다. 스코프 객체는 함수의 매개변수로 전달한 인자와 함께 초기화됩니다. 이것은 모든 전역 변수와 함수가 들어있는 전역 객체와 비슷하지만, 두 가지 중요한 차이점이 있습니다. 첫 번째로 함수 실행이 시작될 때마다 새로운 스코프 객체가 생성됩니다. 두 번째로 ( 따라서 스코프 객체는 JavaScript의 객체 시스템에서 사용하는 프로토타입 체인과 유사한 스코프 체인을 형성합니다. 클로저는 함수와 함수에 의해 생성되는 스코프 객체를 함께 지칭하는 용어입니다. 클로저는 상태를 저장할 수 있기 때문에 보통 객체를 대신하곤 합니다. 다음 글을 통해 클로저에 대한 여러 훌륭한 설명을 확인할 수 있습니다. |