diff --git a/1-js/04-object-basics/01-object/article.md b/1-js/04-object-basics/01-object/article.md index 9ea60fc04..190a6f678 100644 --- a/1-js/04-object-basics/01-object/article.md +++ b/1-js/04-object-basics/01-object/article.md @@ -5,11 +5,7 @@ 그런데 객체형은 원시형과 달리 다양한 데이터를 담을 수 있습니다. 키로 구분된 데이터 집합이나 복잡한 개체(entity)를 저장할 수 있죠. 객체는 자바스크립트 거의 모든 면에 녹아있는 개념이므로 자바스크립트를 잘 다루려면 객체를 잘 이해하고 있어야 합니다. -<<<<<<< HEAD 객체는 중괄호 `{…}`를 이용해 만들 수 있습니다. 중괄호 안에는 '키(key): 값(value)' 쌍으로 구성된 *프로퍼티(property)* 를 여러 개 넣을 수 있는데, `키`엔 문자형, `값`엔 모든 자료형이 허용됩니다. 프로퍼티 키는 '프로퍼티 이름' 이라고도 부릅니다. -======= -An object can be created with curly braces `{…}` with an optional list of *properties*. A property is a "key: value" pair, where `key` is a string (also called a "property name"), and `value` can be anything. ->>>>>>> upstream/master 서랍장을 상상하면 객체를 이해하기 쉽습니다. 서랍장 안 파일은 프로퍼티, 파일 각각에 붙어있는 이름표는 객체의 키라고 생각하시면 됩니다. 복잡한 서랍장 안에서 이름표를 보고 원하는 파일을 쉽게 찾을 수 있듯이, 객체에선 키를 이용해 프로퍼티를 쉽게 찾을 수 있습니다. 추가나 삭제도 마찬가지입니다. @@ -24,11 +20,7 @@ let user = {}; // '객체 리터럴' 문법 ![](object-user-empty.svg) -<<<<<<< HEAD 중괄호 `{...}`를 이용해 객체를 선언하는 것을 *객체 리터럴(object literal)* 이라고 부릅니다. 객체를 선언할 땐 주로 이 방법을 사용합니다. -======= -Usually, the curly braces `{...}` are used. That declaration is called an *object literal*. ->>>>>>> upstream/master ## 리터럴과 프로퍼티 @@ -52,11 +44,7 @@ let user = { // 객체 ![user object](object-user.svg) -<<<<<<< HEAD -서랍장에 파일을 추가하고 뺄 수 있듯이 개발자는 프로퍼티를 추가, 삭제할 수 있습니다. -======= -We can add, remove and read files from it at any time. ->>>>>>> upstream/master +서랍장에 파일을 추가하고, 꺼내 읽고, 뺄 수 있듯이 개발자는 프로퍼티를 추가, 조회, 삭제할 수 있습니다. 점 표기법(dot notation)을 이용하면 프로퍼티 값을 읽는 것도 가능합니다. @@ -74,11 +62,7 @@ user.isAdmin = true; ![user object 2](object-user-isadmin.svg) -<<<<<<< HEAD `delete` 연산자를 사용하면 프로퍼티를 삭제할 수 있습니다. -======= -To remove a property, we can use the `delete` operator: ->>>>>>> upstream/master ```js delete user.age; @@ -108,35 +92,8 @@ let user = { ``` 이런 쉼표를 'trailing(길게 늘어지는)' 혹은 'hanging(매달리는)' 쉼표라고 부릅니다. 이렇게 끝에 쉼표를 붙이면 모든 프로퍼티가 유사한 형태를 보이기 때문에 프로퍼티를 추가, 삭제, 이동하는 게 쉬워집니다. -<<<<<<< HEAD -````smart header="상수 객체는 수정될 수 있습니다." -주의하세요. `const`로 선언된 객체는 수정될 수 있습니다. - -예시: - -```js run -const user = { - name: "John" -}; - -*!* -user.name = "Pete"; // (*) -*/!* - -alert(user.name); // Pete -``` - -`(*)`로 표시한 줄에서 오류를 일으키는 것처럼 보일 수 있지만 그렇지 않습니다. `const`는 `user`의 값을 고정하지만, 그 내용은 고정하지 않습니다. - -`const`는 `user=...`를 전체적으로 설정하려고 할 때만 오류가 발생합니다. - -상수 객체 프로퍼티를 만드는 또 다른 방법이 있습니다. 이후에 챕터에서 다루겠습니다. -```` ## 대괄호 표기법 -======= -## Square brackets ->>>>>>> upstream/master 여러 단어를 조합해 프로퍼티 키를 만든 경우엔, 점 표기법을 사용해 프로퍼티 값을 읽을 수 없습니다. @@ -245,21 +202,13 @@ let bag = { }; ``` -<<<<<<< HEAD 대괄호 표기법은 프로퍼티 이름과 값의 제약을 없애주기 때문에 점 표기법보다 훨씬 강력합니다. 그런데 작성하기 번거롭다는 단점이 있습니다. -======= -Square brackets are much more powerful than dot notation. They allow any property names and variables. But they are also more cumbersome to write. ->>>>>>> upstream/master 이런 이유로 프로퍼티 이름이 확정된 상황이고, 단순한 이름이라면 처음엔 점 표기법을 사용하다가 뭔가 복잡한 상황이 발생했을 때 대괄호 표기법으로 바꾸는 경우가 많습니다. ## 단축 프로퍼티 -<<<<<<< HEAD 실무에선 프로퍼티 값을 기존 변수에서 받아와 사용하는 경우가 종종 있습니다. -======= -In real code, we often use existing variables as values for property names. ->>>>>>> upstream/master 예시: @@ -304,11 +253,7 @@ let user = { ## 프로퍼티 이름의 제약사항 -<<<<<<< HEAD -아시다시피 변수 이름(키)엔 'for', 'let', 'return' 같은 예약어를 사용하면 안됩니다. -======= -As we already know, a variable cannot have a name equal to one of the language-reserved words like "for", "let", "return" etc. ->>>>>>> upstream/master +아시다시피 변수 이름엔 'for', 'let', 'return' 같은 예약어를 사용하면 안됩니다. 그런데 객체 프로퍼티엔 이런 제약이 없습니다. @@ -381,11 +326,7 @@ alert( "blabla" in user ); // user.blabla는 존재하지 않기 때문에 false `in` 왼쪽엔 반드시 *프로퍼티 이름*이 와야 합니다. 프로퍼티 이름은 보통 따옴표로 감싼 문자열입니다. -<<<<<<< HEAD 따옴표를 생략하면 아래 예시와 같이 엉뚱한 변수가 조사 대상이 됩니다. -======= -If we omit quotes, that means a variable should contain the actual name to be tested. For instance: ->>>>>>> upstream/master ```js run let user = { age: 30 }; @@ -415,11 +356,7 @@ alert( "test" in obj ); // `in`을 사용하면 프로퍼티 유무를 제대로 `undefined`는 변수는 정의되어 있으나 값이 할당되지 않은 경우에 쓰기 때문에 프로퍼티 값이 `undefined`인 경우는 흔치 않습니다. 값을 '알 수 없거나(unknown)' 값이 '비어 있다는(empty)' 것을 나타낼 때는 주로 `null`을 사용합니다. 위 예시에서 `in` 연산자는 자리에 어울리지 않는 초대손님처럼 보이네요. -<<<<<<< HEAD -## 'for..in' 반복문 -======= -## The "for..in" loop [#forin] ->>>>>>> upstream/master +## 'for..in' 반복문 [#forin] `for..in` 반복문을 사용하면 객체의 모든 키를 순회할 수 있습니다. `for..in`은 앞서 학습했던 `for(;;)` 반복문과는 완전히 다릅니다. @@ -476,11 +413,7 @@ for (let code in codes) { */!* ``` -<<<<<<< HEAD 현재 개발 중인 애플리케이션의 주 사용자가 독일인이라고 가정해 봅시다. 나라 번호를 선택하는 화면에서 `49`가 맨 앞에 오도록 하는 게 좋을 겁니다. -======= -The object may be used to suggest a list of options to the user. If we're making a site mainly for a German audience then we probably want `49` to be the first. ->>>>>>> upstream/master 그런데 코드를 실행해 보면 예상과는 전혀 다른 결과가 출력됩니다. @@ -492,24 +425,14 @@ The object may be used to suggest a list of options to the user. If we're making ````smart header="정수 프로퍼티? 그게 뭔가요?" '정수 프로퍼티'라는 용어는 변형 없이 정수에서 왔다 갔다 할 수 있는 문자열을 의미합니다. -<<<<<<< HEAD 문자열 "49"는 정수로 변환하거나 변환한 정수를 다시 문자열로 바꿔도 변형이 없기 때문에 정수 프로퍼티입니다. 하지만 '+49'와 '1.2'는 정수 프로퍼티가 아닙니다. ```js run +// Number(...)는 명시적으로 값을 숫자형으로 변환합니다. // 함수 Math.trunc는 소수점 아래를 버리고 숫자의 정수부만 반환합니다. alert( String(Math.trunc(Number("49"))) ); // '49'가 출력됩니다. 기존에 입력한 값과 같으므로 정수 프로퍼티입니다. alert( String(Math.trunc(Number("+49"))) ); // '49'가 출력됩니다. 기존에 입력한 값(+49)과 다르므로 정수 프로퍼티가 아닙니다. alert( String(Math.trunc(Number("1.2"))) ); // '1'이 출력됩니다. 기존에 입력한 값(1.2)과 다르므로 정수 프로퍼티가 아닙니다. -======= -So, `"49"` is an integer property name, because when it's transformed to an integer number and back, it's still the same. But `"+49"` and `"1.2"` are not: - -```js run -// Number(...) explicitly converts to a number -// Math.trunc is a built-in function that removes the decimal part -alert( String(Math.trunc(Number("49"))) ); // "49", same, integer property -alert( String(Math.trunc(Number("+49"))) ); // "49", not same "+49" ⇒ not integer property -alert( String(Math.trunc(Number("1.2"))) ); // "1", not same "1.2" ⇒ not integer property ->>>>>>> upstream/master ``` ```` @@ -558,15 +481,9 @@ for (let code in codes) { - 프로퍼티 키는 문자열이나 심볼이어야 합니다. 보통은 문자열입니다. - 값은 어떤 자료형도 가능합니다. -<<<<<<< HEAD 아래와 같은 방법을 사용하면 프로퍼티에 접근할 수 있습니다. - 점 표기법: `obj.property` - 대괄호 표기법 `obj["property"]`. 대괄호 표기법을 사용하면 `obj[varWithKey]`같이 변수에서 키를 가져올 수 있습니다. -======= -To access a property, we can use: -- The dot notation: `obj.property`. -- Square brackets notation `obj["property"]`. Square brackets allow taking the key from a variable, like `obj[varWithKey]`. ->>>>>>> upstream/master 객체엔 다음과 같은 추가 연산자를 사용할 수 있습니다. - 프로퍼티를 삭제하고 싶을 때: `delete obj.prop` diff --git a/1-js/04-object-basics/02-object-copy/article.md b/1-js/04-object-basics/02-object-copy/article.md index f614c8e47..ae27cd902 100644 --- a/1-js/04-object-basics/02-object-copy/article.md +++ b/1-js/04-object-basics/02-object-copy/article.md @@ -1,51 +1,31 @@ -<<<<<<< HEAD # 참조에 의한 객체 복사 객체와 원시 타입의 근본적인 차이 중 하나는 객체는 '참조에 의해(by reference)' 저장되고 복사된다는 것입니다. 원시값(문자열, 숫자, 불린 값)은 '값 그대로' 저장·할당되고 복사되는 반면에 말이죠. -예시: -======= -# Object references and copying - -One of the fundamental differences of objects versus primitives is that objects are stored and copied "by reference", whereas primitive values: strings, numbers, booleans, etc -- are always copied "as a whole value". +값을 복사할 때 내부에서 어떤 일이 일어나는지 살펴보면 이해하기 쉽습니다. -That's easy to understand if we look a bit under the hood of what happens when we copy a value. +먼저 문자열 같은 원시값부터 살펴봅시다. -Let's start with a primitive, such as a string. - -Here we put a copy of `message` into `phrase`: ->>>>>>> upstream/master +아래에선 `message`의 복사본을 `phrase`에 넣습니다. ```js let message = "Hello!"; let phrase = message; ``` -<<<<<<< HEAD 예시를 실행하면 두 개의 독립된 변수에 각각 문자열 `"Hello!"`가 저장됩니다. ![](variable-copy-value.svg) +당연한 결과처럼 보이죠? + 그런데 객체의 동작 방식은 이와 다릅니다. **변수엔 객체가 그대로 저장되는 것이 아니라, 객체가 저장되어있는 '메모리 주소'인 객체에 대한 '참조 값'이 저장됩니다.** 그림을 통해 변수 user에 객체를 할당할 때 무슨 일이 일어나는지 알아봅시다. -======= -As a result we have two independent variables, each one storing the string `"Hello!"`. - -![](variable-copy-value.svg) - -Quite an obvious result, right? - -Objects are not like that. - -**A variable assigned to an object stores not the object itself, but its "address in memory" -- in other words "a reference" to it.** - -Let's look at an example of such a variable: ->>>>>>> upstream/master ```js let user = { @@ -53,25 +33,19 @@ let user = { }; ``` -And here's how it's actually stored in memory: +실제로는 메모리에 다음과 같이 저장됩니다. ![](variable-contains-reference.svg) -<<<<<<< HEAD -객체는 메모리 내 어딘가에 저장되고, 변수 `user`엔 객체를 '참조'할 수 있는 값이 저장됩니다. - -따라서 **객체가 할당된 변수를 복사할 땐 객체의 참조 값이 복사되고 객체는 복사되지 않습니다.** -======= -The object is stored somewhere in memory (at the right of the picture), while the `user` variable (at the left) has a "reference" to it. +객체는 메모리 어딘가(그림 오른쪽)에 저장되고, 변수 `user`(그림 왼쪽)엔 객체를 '참조'할 수 있는 값이 저장됩니다. -We may think of an object variable, such as `user`, like a sheet of paper with the address of the object on it. +객체 변수 `user`는 객체 주소가 적힌 종이 한 장이라고 생각할 수 있습니다. -When we perform actions with the object, e.g. take a property `user.name`, the JavaScript engine looks at what's at that address and performs the operation on the actual object. +`user.name`처럼 객체를 대상으로 작업을 수행하면 자바스크립트 엔진은 그 주소로 이동해 실제 객체에 작업을 수행합니다. -Now here's why it's important. +이 점이 중요한 이유를 살펴봅시다. -**When an object variable is copied, the reference is copied, but the object itself is not duplicated.** ->>>>>>> upstream/master +**객체가 할당된 변수를 복사할 땐 객체의 참조 값이 복사되고 객체는 복사되지 않습니다.** 예시: @@ -81,21 +55,11 @@ let user = { name: "John" }; let admin = user; // 참조값을 복사함 ``` -<<<<<<< HEAD 변수는 두 개이지만 각 변수엔 동일 객체에 대한 참조 값이 저장되죠. ![](variable-copy-reference.svg) 따라서 객체에 접근하거나 객체를 조작할 땐 여러 변수를 사용할 수 있습니다. -======= -Now we have two variables, each storing a reference to the same object: - -![](variable-copy-reference.svg) - -As you can see, there's still one object, but now with two variables that reference it. - -We can use either variable to access the object and modify its contents: ->>>>>>> upstream/master ```js run let user = { name: 'John' }; @@ -109,25 +73,17 @@ admin.name = 'Pete'; // 'admin' 참조 값에 의해 변경됨 alert(*!*user.name*/!*); // 'Pete'가 출력됨. 'user' 참조 값을 이용해 변경사항을 확인함 ``` -<<<<<<< HEAD -객체를 서랍장에 비유하면 변수는 서랍장을 열 수 있는 열쇠라고 할 수 있습니다. 서랍장은 하나, 서랍장을 열 수 있는 열쇠는 두 개인데, 그중 하나(`admin`)를 사용해 서랍장을 열어 정돈한 후, 또 다른 열쇠로 서랍장을 열면 정돈된 내용을 볼 수 있습니다. -======= -It's as if we had a cabinet with two keys and used one of them (`admin`) to get into it and make changes. Then, if we later use another key (`user`), we are still opening the same cabinet and can access the changed contents. ->>>>>>> upstream/master +객체를 서랍장에 비유하면, 변수는 서랍장을 열 수 있는 열쇠라고 할 수 있습니다. 서랍장은 하나뿐이지만, 이를 여는 열쇠는 두 개 있는 셈입니다. 그중 하나인 `admin`으로 서랍장을 열어 내용을 변경하면, 나중에 다른 열쇠인 `user`로 같은 서랍장을 열었을 때도 변경된 내용을 확인할 수 있습니다. ### 참조에 의한 비교 -<<<<<<< HEAD 객체 비교 시 동등 연산자 `==`와 일치 연산자 `===`는 동일하게 동작합니다. **비교 시 피연산자인 두 객체가 동일한 객체인 경우에 참을 반환하죠.** 두 변수가 같은 객체를 참조하는 예시를 살펴봅시다. 일치·동등 비교 모두에서 참이 반환됩니다. -======= -Two objects are equal only if they are the same object. -For instance, here `a` and `b` reference the same object, thus they are equal: ->>>>>>> upstream/master +예를 들어 아래 코드에서 `a`와 `b`는 같은 객체를 참조하므로 동등하다고 평가됩니다. ```js run let a = {}; @@ -137,11 +93,7 @@ alert( a == b ); // true, 두 변수는 같은 객체를 참조합니다. alert( a === b ); // true ``` -<<<<<<< HEAD 다른 예시를 살펴봅시다. 두 객체 모두 비어있다는 점에서 같아 보이지만, 독립된 객체이기 때문에 일치·동등 비교하면 거짓이 반환됩니다. -======= -And here two independent objects are not equal, even though they look alike (both are empty): ->>>>>>> upstream/master ```js run let a = {}; @@ -150,17 +102,12 @@ let b = {}; // 독립된 두 객체 alert( a == b ); // false ``` -<<<<<<< HEAD `obj1 > obj2` 같은 대소 비교나 `obj == 5` 같은 원시값과의 비교에선 객체가 원시형으로 변환됩니다. 객체가 어떻게 원시형으로 변하는지에 대해선 곧 학습할 예정인데, 이러한 비교(객체끼리의 대소 비교나 원시값과 객체를 비교하는 것)가 필요한 경우는 매우 드물긴 합니다. 대개 코딩 실수 때문에 이런 비교가 발생합니다. -## 객체 복사, 병합과 Object.assign -======= -For comparisons like `obj1 > obj2` or for a comparison against a primitive `obj == 5`, objects are converted to primitives. We'll study how object conversions work very soon, but to tell the truth, such comparisons are needed very rarely -- usually they appear as a result of a programming mistake. - -````smart header="Const objects can be modified" -An important side effect of storing objects as references is that an object declared as `const` *can* be modified. +````smart header="상수 객체는 수정될 수 있습니다." +객체가 참조로 저장된다는 사실 때문에 생기는 중요한 부수 효과가 있습니다. `const`로 선언된 객체도 *수정할 수 있습니다*. -For instance: +예시: ```js run const user = { @@ -174,29 +121,22 @@ user.name = "Pete"; // (*) alert(user.name); // Pete ``` -It might seem that the line `(*)` would cause an error, but it does not. The value of `user` is constant, it must always reference the same object, but properties of that object are free to change. +`(*)`로 표시한 줄에서 오류가 발생할 것처럼 보일 수 있지만 그렇지 않습니다. `user`의 값은 상수이므로 항상 같은 객체를 참조해야 하지만, 그 객체의 프로퍼티는 자유롭게 변경할 수 있습니다. -In other words, the `const user` gives an error only if we try to set `user=...` as a whole. +다시 말해, `const user`는 `user=...`처럼 `user` 전체에 새 값을 대입하려고 할 때만 오류를 일으킵니다. -That said, if we really need to make constant object properties, it's also possible, but using totally different methods. We'll mention that in the chapter . +그렇지만 객체 프로퍼티까지 상수로 만들어야 한다면 완전히 다른 방법을 써야 합니다. 이 방법은 챕터에서 다룹니다. ```` -## Cloning and merging, Object.assign [#cloning-and-merging-object-assign] ->>>>>>> upstream/master +## 객체 복사, 병합과 Object.assign [#cloning-and-merging-object-assign] 객체가 할당된 변수를 복사하면 동일한 객체에 대한 참조 값이 하나 더 만들어진다는 걸 배웠습니다. -<<<<<<< HEAD 그런데 객체를 복제하고 싶다면 어떻게 해야 할까요? 기존에 있던 객체와 똑같으면서 독립적인 객체를 만들고 싶다면 말이죠. 방법은 있는데 자바스크립트는 객체 복제 내장 메서드를 지원하지 않기 때문에 조금 어렵습니다. 사실 객체를 복제해야 할 일은 거의 없습니다. 참조에 의한 복사로 해결 가능한 일이 대다수이죠. 정말 복제가 필요한 상황이라면 새로운 객체를 만든 다음 기존 객체의 프로퍼티들을 순회해 원시 수준까지 프로퍼티를 복사하면 됩니다. -======= -But what if we need to duplicate an object? - -We can create a new object and replicate the structure of the existing one, by iterating over its properties and copying them on the primitive level. ->>>>>>> upstream/master 아래와 같이 말이죠. @@ -221,11 +161,7 @@ clone.name = "Pete"; // clone의 데이터를 변경합니다. alert( user.name ); // 기존 객체에는 여전히 John이 있습니다. ``` -<<<<<<< HEAD [Object.assign](mdn:js/Object/assign)를 사용하는 방법도 있습니다. -======= -We can also use the method [Object.assign](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign). ->>>>>>> upstream/master 문법과 동작 방식은 다음과 같습니다. @@ -233,24 +169,13 @@ We can also use the method [Object.assign](https://developer.mozilla.org/en-US/d Object.assign(dest, ...sources) ``` -<<<<<<< HEAD - 첫 번째 인수 `dest`는 목표로 하는 객체입니다. - 이어지는 인수 `src1, ..., srcN`는 복사하고자 하는 객체입니다. `...`은 필요에 따라 얼마든지 많은 객체를 인수로 사용할 수 있다는 것을 나타냅니다. - 객체 `src1, ..., srcN`의 프로퍼티를 `dest`에 복사합니다. `dest`를 제외한 인수(객체)의 프로퍼티 전부가 첫 번째 인수(객체)로 복사됩니다. - 마지막으로 `dest`를 반환합니다. `assign` 메서드를 사용해 여러 객체를 하나로 병합하는 예시를 살펴봅시다. -```js -======= -- The first argument `dest` is a target object. -- Further arguments is a list of source objects. - -It copies the properties of all source objects into the target `dest`, and then returns it as the result. - -For example, we have `user` object, let's add a couple of permissions to it: - ```js run ->>>>>>> upstream/master let user = { name: "John" }; let permissions1 = { canView: true }; @@ -261,7 +186,7 @@ let permissions2 = { canEdit: true }; Object.assign(user, permissions1, permissions2); */!* -// now user = { name: "John", canView: true, canEdit: true } +// 이제 user = { name: "John", canView: true, canEdit: true } alert(user.name); // John alert(user.canView); // true alert(user.canEdit); // true @@ -277,11 +202,7 @@ Object.assign(user, { name: "Pete" }); alert(user.name); // user = { name: "Pete" } ``` -<<<<<<< HEAD `Object.assign`을 사용하면 반복문 없이도 간단하게 객체를 복사할 수 있습니다. -======= -We also can use `Object.assign` to perform a simple object cloning: ->>>>>>> upstream/master ```js run let user = { @@ -297,21 +218,13 @@ alert(clone.name); // John alert(clone.age); // 30 ``` -<<<<<<< HEAD -예시를 실행하면 `user`에 있는 모든 프로퍼티가 빈 배열에 복사되고 변수에 할당됩니다. -======= -Here it copies all properties of `user` into the empty object and returns it. +예시를 실행하면 `user`에 있는 모든 프로퍼티가 빈 객체에 복사되고, 그 객체가 반환됩니다. -There are also other methods of cloning an object, e.g. using the [spread syntax](info:rest-parameters-spread) `clone = {...user}`, covered later in the tutorial. ->>>>>>> upstream/master +객체를 복제하는 다른 방법도 있습니다. 예를 들어 튜토리얼 뒷부분에서 다룰 [스프레드 문법](info:rest-parameters-spread)을 사용하면 `clone = {...user}`처럼 객체를 복제할 수 있습니다. ## 중첩 객체 복사 -<<<<<<< HEAD 지금까진 `user`의 모든 프로퍼티가 원시값인 경우만 가정했습니다. 그런데 프로퍼티는 다른 객체에 대한 참조 값일 수도 있습니다. 이 경우는 어떻게 해야 할까요? -======= -Until now we assumed that all properties of `user` are primitive. But properties can be references to other objects. ->>>>>>> upstream/master 아래와 같이 말이죠. ```js run @@ -326,13 +239,9 @@ let user = { alert( user.sizes.height ); // 182 ``` -<<<<<<< HEAD `clone.sizes = user.sizes`로 프로퍼티를 복사하는 것만으론 객체를 복제할 수 없습니다. `user.sizes`는 객체이기 때문에 참조 값이 복사되기 때문입니다. `clone.sizes = user.sizes`로 프로퍼티를 복사하면 `clone`과 `user`는 같은 sizes를 공유하게 됩니다. 아래와 같이 말이죠. -======= -Now it's not enough to copy `clone.sizes = user.sizes`, because `user.sizes` is an object, and will be copied by reference, so `clone` and `user` will share the same sizes: ->>>>>>> upstream/master ```js run let user = { @@ -347,31 +256,18 @@ let clone = Object.assign({}, user); alert( user.sizes === clone.sizes ); // true, 같은 객체입니다. -<<<<<<< HEAD -// user와 clone는 sizes를 공유합니다. -user.sizes.width++; // 한 객체에서 프로퍼티를 변경합니다. -alert(clone.sizes.width); // 51, 다른 객체에서 변경 사항을 확인할 수 있습니다. +// user와 clone은 sizes를 공유합니다. +user.sizes.width = 60; // 한 객체에서 프로퍼티를 변경합니다. +alert(clone.sizes.width); // 60, 다른 객체에서 변경 사항을 확인할 수 있습니다. ``` -이 문제를 해결하려면 `user[key]`의 각 값을 검사하면서, 그 값이 객체인 경우 객체의 구조도 복사해주는 반복문을 사용해야 합니다. 이런 방식을 '깊은 복사(deep cloning)'라고 합니다. - -깊은 복사 시 사용되는 표준 알고리즘인 [Structured cloning algorithm](https://html.spec.whatwg.org/multipage/structured-data.html#safe-passing-of-structured-data)을 사용하면 위 사례를 비롯한 다양한 상황에서 객체를 복제할 수 있습니다. - -자바스크립트 라이브러리 [lodash](https://lodash.com)의 메서드인 [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep)을 사용하면 이 알고리즘을 직접 구현하지 않고도 깊은 복사를 처리할 수 있으므로 참고하시기 바랍니다. -======= -// user and clone share sizes -user.sizes.width = 60; // change a property from one place -alert(clone.sizes.width); // 60, get the result from the other one -``` - -To fix that and make `user` and `clone` truly separate objects, we should use a cloning loop that examines each value of `user[key]` and, if it's an object, then replicate its structure as well. That is called a "deep cloning" or "structured cloning". There's [structuredClone](https://developer.mozilla.org/en-US/docs/Web/API/structuredClone) method that implements deep cloning. - +이 문제를 해결해 `user`와 `clone`을 진짜로 독립된 객체로 만들려면 `user[key]`의 각 값을 검사하면서, 그 값이 객체인 경우 객체의 구조도 복제하는 반복문을 사용해야 합니다. 이런 방식을 '깊은 복사(deep cloning)' 또는 '구조화 복사(structured cloning)'라고 합니다. 깊은 복사를 구현한 [structuredClone](https://developer.mozilla.org/en-US/docs/Web/API/structuredClone) 메서드가 있습니다. ### structuredClone -The call `structuredClone(object)` clones the `object` with all nested properties. +`structuredClone(object)`을 호출하면 중첩 프로퍼티까지 모두 포함해 `object`가 복제됩니다. -Here's how we can use it in our example: +앞선 예시에 이 메서드를 적용해 봅시다. ```js run let user = { @@ -386,59 +282,50 @@ let user = { let clone = structuredClone(user); */!* -alert( user.sizes === clone.sizes ); // false, different objects +alert( user.sizes === clone.sizes ); // false, 서로 다른 객체입니다. -// user and clone are totally unrelated now -user.sizes.width = 60; // change a property from one place -alert(clone.sizes.width); // 50, not related +// 이제 user와 clone은 완전히 독립적입니다. +user.sizes.width = 60; // 한 객체에서 프로퍼티를 변경합니다. +alert(clone.sizes.width); // 50, 다른 객체에는 영향을 주지 않습니다. ``` -The `structuredClone` method can clone most data types, such as objects, arrays, primitive values. +`structuredClone` 메서드는 객체, 배열, 원시값 등 대부분의 자료형을 복제할 수 있습니다. -It also supports circular references, when an object property references the object itself (directly or via a chain or references). +객체 프로퍼티가 객체 자신을 직접 또는 여러 참조를 거쳐 참조하는 순환 참조도 지원합니다. -For instance: +예시: ```js run let user = {}; -// let's create a circular reference: -// user.me references the user itself +// 순환 참조를 만들어 봅시다. +// user.me는 user 자신을 참조합니다. user.me = user; let clone = structuredClone(user); alert(clone.me === clone); // true ``` -As you can see, `clone.me` references the `clone`, not the `user`! So the circular reference was cloned correctly as well. +보시다시피 `clone.me`는 `user`가 아니라 `clone`을 참조합니다. 순환 참조도 올바르게 복제된 것입니다. -Although, there are cases when `structuredClone` fails. +다만 `structuredClone`이 실패하는 경우도 있습니다. -For instance, when an object has a function property: +예를 들어 객체에 함수 프로퍼티가 있으면 실패합니다. ```js run -// error +// 에러가 발생합니다. structuredClone({ f: function() {} }); ``` -Function properties aren't supported. +함수 프로퍼티는 지원되지 않습니다. -To handle such complex cases we may need to use a combination of cloning methods, write custom code or, to not reinvent the wheel, take an existing implementation, for instance [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep) from the JavaScript library [lodash](https://lodash.com). ->>>>>>> upstream/master +이처럼 복잡한 경우를 처리하려면 여러 복제 방법을 조합하거나 직접 코드를 작성해야 할 수 있습니다. 바퀴를 다시 발명하지 않으려면 자바스크립트 라이브러리 [lodash](https://lodash.com)의 [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep) 같은 기존 구현을 사용할 수도 있습니다. ## 요약 -<<<<<<< HEAD 객체는 참조에 의해 할당되고 복사됩니다. 변수엔 '객체' 자체가 아닌 메모리상의 주소인 '참조'가 저장됩니다. 따라서 객체가 할당된 변수를 복사하거나 함수의 인자로 넘길 땐 객체가 아닌 객체의 참조가 복사됩니다. -======= -Objects are assigned and copied by reference. In other words, a variable stores not the "object value", but a "reference" (address in memory) for the value. So copying such a variable or passing it as a function argument copies that reference, not the object itself. ->>>>>>> upstream/master 그리고 복사된 참조를 이용한 모든 작업(프로퍼티 추가·삭제 등)은 동일한 객체를 대상으로 이뤄집니다. -<<<<<<< HEAD -객체의 '진짜 복사본'을 만들려면 '얕은 복사(shallow copy)'를 가능하게 해주는 `Object.assign`이나 '깊은 복사'를 가능하게 해주는 [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep)를 사용하면 됩니다. 이때 얕은 복사본은 중첩 객체를 처리하지 못한다는 점을 기억해 두시기 바랍니다. -======= -To make a "real copy" (a clone) we can use `Object.assign` for the so-called "shallow copy" (nested objects are copied by reference) or a "deep cloning" function `structuredClone` or use a custom cloning implementation, such as [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep). ->>>>>>> upstream/master +객체의 '진짜 복사본'(클론)을 만들려면 '얕은 복사(shallow copy)'를 가능하게 해주는 `Object.assign`을 사용하거나(중첩 객체는 참조로 복사됩니다), '깊은 복사' 함수인 `structuredClone` 혹은 [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep) 같은 기존 구현을 사용할 수 있습니다. diff --git a/1-js/04-object-basics/03-garbage-collection/article.md b/1-js/04-object-basics/03-garbage-collection/article.md index 997971bae..8ef2a634f 100644 --- a/1-js/04-object-basics/03-garbage-collection/article.md +++ b/1-js/04-object-basics/03-garbage-collection/article.md @@ -14,27 +14,16 @@ 예시: -<<<<<<< HEAD - 현재 함수의 지역 변수와 매개변수 - 중첩 함수의 체인에 있는 함수에서 사용되는 변수와 매개변수 - 전역 변수 - 기타 등등 -======= - - The currently executing function, its local variables and parameters. - - Other functions on the current chain of nested calls, their local variables and parameters. - - Global variables. - - (there are some other, internal ones as well) ->>>>>>> upstream/master 이런 값은 *루트(root)* 라고 부릅니다. 2. 루트가 참조하는 값이나 체이닝으로 루트에서 참조할 수 있는 값은 도달 가능한 값이 됩니다. -<<<<<<< HEAD 전역 변수에 객체가 저장되어있다고 가정해 봅시다. 이 객체의 프로퍼티가 또 다른 객체를 참조하고 있다면, 프로퍼티가 참조하는 객체는 도달 가능한 값이 됩니다. 이 객체가 참조하는 다른 모든 것들도 도달 가능하다고 여겨집니다. 자세한 예시는 아래에서 살펴보겠습니다. -======= - For instance, if there's an object in a global variable, and that object has a property referencing another object, *that* object is considered reachable. And those that it references are also reachable. Detailed examples to follow. ->>>>>>> upstream/master 자바스크립트 엔진 내에선 [가비지 컬렉터(garbage collector)](https://en.wikipedia.org/wiki/Garbage_collection_(computer_science))가 끊임없이 동작합니다. 가비지 컬렉터는 모든 객체를 모니터링하고, 도달할 수 없는 객체는 삭제합니다. @@ -85,11 +74,7 @@ let admin = user; user = null; ``` -<<<<<<< HEAD 전역 변수 `admin`을 통하면 여전히 객체 John에 접근할 수 있기 때문에 John은 메모리에서 삭제되지 않습니다. 이 상태에서 `admin`을 다른 값(null 등)으로 덮어쓰면 John은 메모리에서 삭제될 수 있습니다. -======= -...Then the object is still reachable via `admin` global variable, so it must stay in memory. If we overwrite `admin` too, then it can be removed. ->>>>>>> upstream/master ## 연결된 객체 @@ -184,19 +169,11 @@ John과 Ann은 여전히 서로를 참조하고 있고, 두 객체 모두 외부 ![](garbage-collection-2.svg) -<<<<<<< HEAD 이후 루트가 참조하고 있는 것들을 mark 합니다. ![](garbage-collection-3.svg) 도달 가능한 모든 객체를 방문할 때까지, mark 한 객체가 참조하는 객체를 계속해서 mark 합니다. -======= -Then we follow their references and mark referenced objects: - -![](garbage-collection-3.svg) - -...And continue to follow further references, while possible: ->>>>>>> upstream/master ![](garbage-collection-4.svg) @@ -206,23 +183,13 @@ Then we follow their references and mark referenced objects: 루트에서 페인트를 들이붓는다고 상상하면 이 과정을 이해하기 쉽습니다. 루트를 시작으로 참조를 따라가면서 도달가능한 객체 모두에 페인트가 칠해진다고 생각하면 됩니다. 이때 페인트가 묻지 않은 객체는 메모리에서 삭제됩니다. -<<<<<<< HEAD 지금까지 가비지 컬렉션이 어떻게 동작하는지에 대한 개념을 알아보았습니다. 자바스크립트 엔진은 실행에 영향을 미치지 않으면서 가비지 컬렉션을 더 빠르게 하는 다양한 최적화 기법을 적용합니다. -======= -That's the concept of how garbage collection works. JavaScript engines apply many optimizations to make it run faster and not introduce any delays into the code execution. ->>>>>>> upstream/master 최적화 기법: -<<<<<<< HEAD - **generational collection(세대별 수집)** -- 객체를 '새로운 객체'와 '오래된 객체'로 나눕니다. 객체 상당수는 생성 이후 제 역할을 빠르게 수행해 금방 쓸모가 없어지는데, 이런 객체를 '새로운 객체'로 구분합니다. 가비지 컬렉터는 이런 객체를 공격적으로 메모리에서 제거합니다. 일정 시간 이상 동안 살아남은 객체는 '오래된 객체'로 분류하고, 가비지 컬렉터가 덜 감시합니다. - **incremental collection(점진적 수집)** -- 방문해야 할 객체가 많다면 모든 객체를 한 번에 방문하고 mark 하는데 상당한 시간이 소모됩니다. 가비지 컬렉션에 많은 리소스가 사용되어 실행 속도도 눈에 띄게 느려지겠죠. 자바스크립트 엔진은 이런 현상을 개선하기 위해 가비지 컬렉션을 여러 부분으로 분리한 다음, 각 부분을 별도로 수행합니다. 작업을 분리하고, 변경 사항을 추적하는 데 추가 작업이 필요하긴 하지만, 긴 지연을 짧은 지연 여러 개로 분산시킬 수 있다는 장점이 있습니다. - **idle-time collection(유휴 시간 수집)** -- 가비지 컬렉터는 실행에 주는 영향을 최소화하기 위해 CPU가 유휴 상태일 때에만 가비지 컬렉션을 실행합니다. -======= -- **Generational collection** -- objects are split into two sets: "new ones" and "old ones". In typical code, many objects have a short life span: they appear, do their job and die fast, so it makes sense to track new objects and clear the memory from them if that's the case. Those that survive for long enough, become "old" and are examined less often. -- **Incremental collection** -- if there are many objects, and we try to walk and mark the whole object set at once, it may take some time and introduce visible delays in the execution. So the engine splits the whole set of existing objects into multiple parts. And then clear these parts one after another. There are many small garbage collections instead of a total one. That requires some extra bookkeeping between them to track changes, but we get many tiny delays instead of a big one. -- **Idle-time collection** -- the garbage collector tries to run only while the CPU is idle, to reduce the possible effect on the execution. ->>>>>>> upstream/master 이 외에도 다양한 최적화 기법과 가비지 컬렉션 알고리즘이 있습니다. 다양한 기법과 알고리즘을 소개해 드리고 싶지만, 엔진마다 세부 사항이나 기법이 다르기 때문에 여기서 멈추도록 하겠습니다. 엔진이 발전하면 기법도 달라지기 때문에 학습해야 할 이유가 진짜 없다면 '심화' 학습은 그리 가치 있지 않다고 생각합니다. 순수한 호기심 때문이라면 물론 괜찮습니다. 이런 분들을 위해 아래에 링크를 몇 개를 소개해놓았습니다. @@ -230,30 +197,16 @@ That's the concept of how garbage collection works. JavaScript engines apply man 지금까지 알아본 내용을 요약해 봅시다. -<<<<<<< HEAD - 가비지 컬렉션은 엔진이 자동으로 수행하므로 개발자는 이를 억지로 실행하거나 막을 수 없습니다. - 객체는 도달 가능한 상태일 때 메모리에 남습니다. - 참조된다고 해서 도달 가능한 것은 아닙니다. 서로 연결된 객체들도 도달 불가능할 수 있습니다. -======= -- Garbage collection is performed automatically. We cannot force or prevent it. -- Objects are retained in memory while they are reachable. -- Being referenced is not the same as being reachable (from a root): a pack of interlinked objects can become unreachable as a whole, as we've seen in the example above. ->>>>>>> upstream/master 모던 자바스크립트 엔진은 좀 더 발전된 가비지 컬렉션 알고리즘을 사용합니다. 어떤 알고리즘을 사용하는지 궁금하다면 'The Garbage Collection Handbook: The Art of Automatic Memory Management'(저자 - R. Jones et al)를 참고하시기 바랍니다. -<<<<<<< HEAD 저수준(low-level) 프로그래밍에 익숙하다면, [A tour of V8: Garbage Collection](http://jayconrod.com/posts/55/a-tour-of-v8-garbage-collection)을 읽어보세요. V8 가비지 컬렉터에 대한 자세한 내용을 확인해 볼 수 있습니다. [V8 공식 블로그](https://v8.dev/)에도 메모리 관리 방법 변화에 대한 내용이 올라옵니다. 가비지 컬렉션을 심도 있게 학습하려면 V8 내부구조를 공부하거나 V8 엔지니어로 일했던 [Vyacheslav Egorov](http://mrale.ph)의 블로그를 읽는 것도 좋습니다. 여러 엔진 중 'V8' 엔진을 언급하는 이유는 인터넷에서 관련 글을 쉽게 찾을 수 있기 때문입니다. V8과 타 엔진들은 동작 방법이 비슷한데, 가비지 컬렉션 동작 방식에는 많은 차이가 있습니다. 저수준 최적화가 필요한 상황이라면, 엔진에 대한 조예가 깊어야 합니다. 먼저 자바스크립트에 익숙해진 후에 엔진에 대해 학습하는 것을 추천해 드립니다. -======= -If you are familiar with low-level programming, more detailed information about V8's garbage collector is in the article [A tour of V8: Garbage Collection](https://jayconrod.com/posts/55/a-tour-of-v8-garbage-collection). - -The [V8 blog](https://v8.dev/) also publishes articles about changes in memory management from time to time. Naturally, to learn more about garbage collection, you'd better prepare by learning about V8 internals in general and read the blog of [Vyacheslav Egorov](https://mrale.ph) who worked as one of the V8 engineers. I'm saying: "V8", because it is best covered by articles on the internet. For other engines, many approaches are similar, but garbage collection differs in many aspects. - -In-depth knowledge of engines is good when you need low-level optimizations. It would be wise to plan that as the next step after you're familiar with the language. ->>>>>>> upstream/master diff --git a/1-js/04-object-basics/04-object-methods/7-calculator/task.md b/1-js/04-object-basics/04-object-methods/7-calculator/task.md index a588bba12..efafae38a 100644 --- a/1-js/04-object-basics/04-object-methods/7-calculator/task.md +++ b/1-js/04-object-basics/04-object-methods/7-calculator/task.md @@ -6,19 +6,13 @@ importance: 5 `calculator`라는 객체를 만들고 세 메서드를 구현해 봅시다. -<<<<<<< HEAD -- `read()`에선 프롬프트 창을 띄우고 더할 값 두 개를 입력받습니다. 입력받은 값은 객체의 프로퍼티에 저장합니다. +- `read()`에선 프롬프트 창을 띄우고 더할 값 두 개를 입력받습니다. 입력받은 값은 각각 `a`와 `b`라는 이름의 객체 프로퍼티에 저장합니다. - `sum()`은 저장된 두 값의 합을 반환합니다. - `mul()`은 저장된 두 값의 곱을 반환합니다. -======= -- `read()` prompts for two values and saves them as object properties with names `a` and `b` respectively. -- `sum()` returns the sum of saved values. -- `mul()` multiplies saved values and returns the result. ->>>>>>> upstream/master ```js let calculator = { - // ... 여기에 답안 작성 ... + // ... 여기에 코드 작성 ... }; calculator.read(); diff --git a/1-js/04-object-basics/04-object-methods/8-chain-calls/task.md b/1-js/04-object-basics/04-object-methods/8-chain-calls/task.md index da207d22d..7c89c1902 100644 --- a/1-js/04-object-basics/04-object-methods/8-chain-calls/task.md +++ b/1-js/04-object-basics/04-object-methods/8-chain-calls/task.md @@ -4,11 +4,7 @@ importance: 2 # 체이닝 -<<<<<<< HEAD 올라가기(`up`)와 내려가기(`down`) 메서드를 제공하는 객체 `ladder`가 있습니다. -======= -There's a `ladder` object that allows you to go up and down: ->>>>>>> upstream/master ```js let ladder = { @@ -25,11 +21,7 @@ let ladder = { }; ``` -<<<<<<< HEAD 메서드를 연이어 호출하고자 한다면 아래와 같이 코드를 작성할 수 있습니다. -======= -Now, if we need to make several calls in sequence, we can do it like this: ->>>>>>> upstream/master ```js ladder.up(); @@ -40,18 +32,10 @@ ladder.down(); ladder.showStep(); // 0 ``` -<<<<<<< HEAD `up`, `down`, `showStep`을 수정해 아래처럼 메서드 호출 체이닝이 가능하도록 해봅시다. -======= -Modify the code of `up`, `down`, and `showStep` to make the calls chainable, like this: ->>>>>>> upstream/master ```js -ladder.up().up().down().showStep().down().showStep(); // shows 1 then 0 +ladder.up().up().down().showStep().down().showStep(); // 1과 0을 차례대로 보여줌 ``` -<<<<<<< HEAD 참고로 이런 방식은 자바스크립트 라이브러리에서 널리 사용됩니다. -======= -Such an approach is widely used across JavaScript libraries. ->>>>>>> upstream/master