티스토리 뷰
자바스크립트의 서브셋은 대부분 보안을 목적으로 하고 있고, 광고 서버와 같은 신뢰할 수 없는 곳에서 온 스크립트라도, 보안 서브셋을 사용하여 작성되었다면 그 스크립트는 안전하게 실행될 수 있다. 이 장에서는 자브스크립트 확장을 다룬다.
자바스크립트 서브셋
좋은 부분들
더글라스 크락포드는 책에서 자스 서브셋을 다루는데, 이는 생각하기에 사용할 가치가 있는 부분만으로 구성했다. 이 서브셋의 목적은 언어를 단순화하고, 애매한 부분과 결점을 숨기며, 궁극적으로는 프로그래밍을 좀더 쉽게 하고 더 나은 프로그램을 만들 수 있도록 하는 것이다. 크락포드의 서브셋에는 with, continue, eval()이 없고 함수 정의 표현식만을 사용한다. 쉼표 연산자, 비트 연산자, ++, --, ==, !=도 없다.
보안을 위한 서브셋
좋은 부분은 프로그래머의 생산성을 향상시키려는 열망과 미학적 이유를 위해 설계된 자스의 서브셋이다. 이보다 큰 부류의 서브셋이 있는데, 이 서브셋은 보안 컨테이너 혹은 '샌드박스'안에서 신뢰할 수 없는 자스 코드를 안전하게 실행하려는 목적으로 설계되었다.
현재 보안 서브셋은 꽤 다양하게 구현되어 있다.
ADsafe, dojox.scure, Caja, FBJS, 마이크로소프트 Web Sandbox
상수와 범위 변수
var, let, const
해체 할당
해체 할당에서 등호의 오른쪽에 있는 값은 배열 또는 객체이고, 왼쪽에는 배열 문법이나 객체 리터럴 문법과 비슷한 문법으로 하나 이상의 변수 이름을 지정한다.
let [x,y]]=[1,2];
[x,y]=[x+1,y+1];
[x,y]=[y,x];
console.log([x,y]);
해체 할당을 사용하면 배열을 반환하는 함수의 결과를 다루기가 수월해진다.
function polar(x,y){
return [Math.sqrt(x*x+y*y), Math.atan2(y,x)];
}
let [r,theta]=polar(1.0,1.0);
해체 할당의 왼쪽에 있는 변수의 개수가 오른쪽에 있는 배열 요소의 개수와 일치할 필요는 없다. 왼쪽에 있는 변수 중 할당되지 못한 변수는 undefined로 설정되고, 오른쪽에 있는 추가 값들은 무시된다. 왼쪽에 있는 변수의 목록에서 오른쪽의 특정 값을 건너뛰려면 쉼표를 추가하면 된다.
let [x,y]=[1]; //x=1, y=undefined
[x,y=[1,2,3]; //x=1,y=2
[,x,,y]=[1,2,3,4]; //x=2, y=4
해체 할당은 오른쪽에서 개별적으로 추출한 값이 아니라 전체 데이터 구조를 값으로 사용한다. 따라서 다음과 같은 체인할당을 할 수 있다.
let first,second,all;
all=[first,second]=[1,2,3,4]; // first=1, second=2, all=[1,2,3,4]
심지어 해체 할당은 중첩 배열에도 사용할 수 있다. 다음처럼 오른쪽이 중첩 배열일 때 해체 할당을 적용하려면 ,왼쪽 또한 중첩된 배열처럼 보이는 리터럴로 구성해야 한다.
let [one, [towA, towB]]=[1,[2,2.5],3]; //one=1, twoA=2, twoB=2.5
또한, 객체 할당은 오른쪽 값이 객체일 때도 수행될 수 있다.
아래에서 r,g,b는 프로퍼티 이름이고 red,green,blue는 변수이름이다.
let transparent={r:0.0,g:0.0,b:0.0,a:1.0};
let {r:red, g:green, b:blue}=transparent; //red=0.0, green=0.0, blue=0.0
//let sin=Math.sin,cos=Math.cos,tan=Math.tan과 같다.
let {sin:sin, cos:cos, tan:tan}=Math;
중첩된 객체에도 해체 할당을 사용할 수 있다. 사실, 임의의 데이터 구조를 나타내기 위해 중첩 배열과 중첩 객체 두 문법을 결합할 수도 있다.
//중첩된 데이터 구조:객체 배열을 포함한 객체
let data={
name: "destructuring assignment",
type: "extension",
impl: [{engine: "spidermonkey", version: 1.7},
{engine: "rhino", version: 1.7}]
};
//앞의 데이터에서 값 네 개를 추출하기 위해 해체 할당을 사용한다. 이거안돼
let ({name:feature, impl:[{engine:impl1, version:v1},{engine:impl2}]}=data){
console.log(feature);
console.log(impl1);
console.log(v1);
console.log(impl2);
}
아니 안돼잖아
let {name:feature, impl:[{engine:impl1, version:v1},{engine:impl2}]}=data;
console.log(feature);
console.log(impl1);
console.log(v1);
console.log(impl2);
순회
for/of 루프
for...of 명령문은 반복가능한 객체 (Array, Map, Set, String, TypedArray, arguments 객체 등을 포함)에 대해서 반복하고 각 개별 속성값에 대해 실행되는 문이 있는 사용자 정의 반복 후크를 호출하는 루프를 생성합니다.
for/each 루프를 배열에 사용하면 배열 요소(인덱스가 아니라)를 순회한다.
a=['one','two','three'];
for(let p in a) console.log(p); //0,1,2
for(let v of a) console.log(v); //one two three
for...in 루프는 객체의 모든 열거가능한 속성에 대해 반복합니다.
for...of 구문은 컬렉션 전용입니다. 모든 객체보다는, [Symbol.iterator] 속성이 있는 모든 컬렉션 요소에 대해 이 방식으로 반복합니다.
다음 예는 for...of 루프와 for...in 루프의 차이를 보입니다.
Object.prototype.objCustom = function () {};
Array.prototype.arrCustom = function () {};
let iterable = [3, 5, 7];
iterable.foo = "hello";
for (let i in iterable) {
console.log(i); // 0, 1, 2, "foo", "arrCustom", "objCustom"
}
for (let i of iterable) {
console.log(i); // 3, 5, 7
}
이터레이터(Iterator)
자바스크립트에서 반복자(Iterator)는 시퀀스를 정의하고 종료시의 반환값을 잠재적으로 정의하는 객체입니다. 더 구체적으로 말하자면, 반복자는 두 개의 속성( value, done)을 반환하는 next() 메소드 사용하여 객체의 Iterator protocol을 구현합니다. 시퀀스의 마지막 값이 이미 산출되었다면 done 값은 true 가 됩니다. 만약 value값이 done 과 함께 존재한다면, 그것은 반복자의 반환값이 됩니다.
반복자를 생성하면 next() 메소드를 반복적으로 호출하여 명시적으로 반복시킬 수 있습니다. 반복자를 반복시키는 것은 일반적으로 한 번씩만 할 수 있기 때문에, 반복자를 소모시키는 것이라고 할 수 있습니다. 마지막 값을 산출하고나서 next()를 추가적으로 호출하면 {done: true}. 가 반환됩니다.
자바스크립트에서 가장 일반적인 반복자는 배열 반복자로, 배열의 각 값을 순서대로 반환합니다. 모든 반복자가 배열로 표현될수 있다고 상상할 수 있지만 , 이것은 사실은 아닙니다. 배열은 완전히 할당되어야 하지만, 반복자는 필요한만큼만 소모되므로 무제한 시퀀스로 표현할 수 있습니다. 이를 테면 0부터 무한대사이의 정수범위처럼 말이죠.
여기에 실습할 수 있는 예제가 있습니다. start에서 end까지 step 수 만큼 띄어진 정수 시퀀스를 정의하는 간단한 범위 반복자를 만들 수 있습니다. 최종적으로 시퀀스의 크기가 반환됩니다.
function makeRangeIterator(start = 0, end = Infinity, step = 1) {
var nextIndex = start;
var n = 0;
var rangeIterator = {
next: function() {
var result;
if (nextIndex < end) {
result = { value: nextIndex, done: false }
} else if (nextIndex == end) {
result = { value: n, done: true }
} else {
result = { done: true };
}
nextIndex += step;
n++;
return result;
}
};
return rangeIterator;
}
var it = makeRangeIterator(1, 4);
var result = it.next();
while (!result.done) {
console.log(result.value); // 1 2 3
result = it.next();
}
console.log("Iterated over sequence of size: ", result.value);
제너레이터
잘 만들어진 반복자(Iterator)는 유용한 도구인 반면, 이것을 생성할 때는 주의해서 프로그래밍을 해야 하는데, 반복자 내부에 명시적으로 상태를 유지할 필요가 있기 때문입니다. 생성자(Generator) 함수는 이에 대한 강력한 대안을 제공합니다: 실행이 연속적이지 않은 하나의 함수를 작성함으로서 개발자가 iterative algorithm을 정의할 수 있게 해줍니다. 생성자 함수는 function* 문법을 사용하여 작성됩니다. 생성자 함수가 최초로 호출될 때, 함수 내부의 어떠한 코드도 실행되지 않고, 대신 생성자라고 불리는 반복자 타입을 반환합니다. 생성자의 next 메소드를 호출함으로서 어떤 값이 소비되면, 생성자 함수는 yield 키워드를 만날 때까지 실행됩니다.
생성자 함수는 원하는 만큼 호출될 수 있고, 매번 새로운 생성자를 반환합니다다. 하지만, 각 생성자는 단 한 번만 순회될 수 있을 것입니다.
위의 예제 코드에 생성자를 적용한 것입니다. 두 코드의 행위는 동일하지만, 생성자를 사용한 쪽이 쓰거나 읽기가 훨씬 쉽습니다.
Generator.prototype.next() yield 표현을 통해 yield된 값을 반환합니다.
Generator.prototype.return()주어진 값을 반환하고 생성기를 종료합니다.
Generator.prototype.throw()생성기로 에러를 throw합니다.
function* makeRangeIterator(start = 0, end = Infinity, step = 1) {
let n = 0;
for (let i = start; i < end; i += step) {
n++;
yield i;
}
return n;
}
Iterables
객체는 값이 for..of 구조 내에서 반복되는 것 같은 그 반복 동작을 정의하는 경우 반복이 가능(iterable)합니다. Array 또는 Map과 같은 일부 내장 형은 기본 반복 동작이 있지만 다른 형(가령 Object)은 없습니다.
반복가능하기 위해서, 객체는 @@iterator 메서드를 구현해야 합니다. 즉, 객체( 혹은 그 프로토타입 체인에 등장하는 객체 중 하나)가 Symbol.iterator 키를 갖는 속성이 있어야 함을 뜻합니다.
하나의 iterable은 단 한 번, 혹은 여러번 반복가능합니다. 어떤 순간에 어떻게 사용할 지는 프로그래머에게 달려있습니다. 단 한 번 반복가능한 iterable(e.g. Generator)은 관습적으로 자신의 @@iterator 메소드로부터 this를 반환합니다. 반면, 여러 번 반복 가능한 iterables은 @@iterator 메소드가 호출되는 매 회 새로운 iterator를 반드시 반환해야합니다.
String, Array, TypedArray, Map 및 Set은 모두 내장 반복가능 객체입니다, 그들의 프로토타입 객체가 모두 Symbol.iterator 메서드가 있기 때문입니다.
var myIterable = {
*[Symbol.iterator]() {
yield 1;
yield 2;
yield 3;
}
}
for (let value of myIterable) {
console.log(value);
}
// 1
// 2
// 3
or
[...myIterable]; // [1, 2, 3]
iterator를 기대하는 구문
for(let value of ['a', 'b', 'c']){
console.log(value)
}
// "a"
// "b"
// "c"
[...'abc'] // ["a", "b", "c"]
function* gen(){
yield* ['a', 'b', 'c']
}
gen().next() // { value:"a", done:false }
[a, b, c] = new Set(['a', 'b', 'c'])
a // "a"
Generator 심화
function* fibonacci(){
var fn1 = 0;
var fn2 = 1;
while (true){
var current = fn1;
fn1 = fn2;
fn2 = current + fn1;
var reset = yield current;
if (reset){
fn1 = 0;
fn2 = 1;
}
}
}
var sequence = fibonacci();
console.log(sequence.next().value); // 0
console.log(sequence.next().value); // 1
console.log(sequence.next().value); // 1
console.log(sequence.next().value); // 2
console.log(sequence.next().value); // 3
console.log(sequence.next().value); // 5
console.log(sequence.next().value); // 8
console.log(sequence.next(true).value); // 0
console.log(sequence.next().value); // 1
console.log(sequence.next().value); // 1
console.log(sequence.next().value); // 2
배열 함축
자스1.7의 기능. 안돼
일반적으로 배열 함축은
[표현식 for (변수 in 객체) if (조건)]
let evensquares=[x*x for (x in range(0,10)) if(x%2===0)];
let evensquares=[];
for(x in range(0,10)){
if(x%2===0)
evensquares.push(x*x);
}
약칭 함수
자스 1.8에는 함수 작성을 간단하게 하기 위한 약칭(표현식 클로저라고 부른다)이 포함되었다. 만약 함수가 단일 평가식을 평가하고 그 값을 반환한다면, return 키워드와 함수 몸체를 둘러싼 중괄호를 생략하고 단순히 평가될 표현식을 인자 목록 뒤에 두기만 하면 된다.
let succ=function(x) x+1, yes=function() true, no=function() false;
다중 catch절
try/catch 구문은 여러 catch 구문을 사용할 수 있도록 확장되었다. 이 기능을 사용하려면 catch절의 매개변수 이름 다음에 if 키워드와 조건문 표현식을 두어야 한다. 만약 조건문이 true라면 다른 모든 catch절은 건너뛴다.
try{
throw 1;
}
catch(e if e instanceof ReferenceError){
//참조 에러를 처리한다
}
catch(e if e==="quit"){
//문자열이 quit인 경우를 처리한다
}
catch(e){
//다른 모든 경우를 처리한다
}
finally{
//finally절은 별 다를 바 없이 작동
}
E4X: XML을 위한 ECMAScript
'책 > 자바스크립트 완벽 가이드' 카테고리의 다른 글
13장 웹브라우저의 자바스크립트 (0) | 2020.11.19 |
---|---|
12장 서버 측 자바스크립트 (0) | 2020.11.15 |
10장 정규 표현식을 사용한 패턴 매칭 (0) | 2020.11.14 |
9장 클래스와 모듈 (0) | 2020.11.12 |
8장 함수 (0) | 2020.11.09 |