diff --git a/1-js/09-classes/02-class-inheritance/1-class-constructor-error/solution.md b/1-js/09-classes/02-class-inheritance/1-class-constructor-error/solution.md index 4711e48271..612c61aed1 100644 --- a/1-js/09-classes/02-class-inheritance/1-class-constructor-error/solution.md +++ b/1-js/09-classes/02-class-inheritance/1-class-constructor-error/solution.md @@ -1,6 +1,6 @@ -That's because the child constructor must call `super()`. +인스턴스를 생성할 수 없었던 이유는 자식의 생성자가 super()를 호출해야 하기 때문입니다. -Here's the corrected code: +아래에 올바른 코드가 있습니다. ```js run class Animal { @@ -21,7 +21,7 @@ class Rabbit extends Animal { } *!* -let rabbit = new Rabbit("White Rabbit"); // ok now +let rabbit = new Rabbit("White Rabbit"); // 이제 생성됩니다 */!* alert(rabbit.name); // White Rabbit ``` diff --git a/1-js/09-classes/02-class-inheritance/1-class-constructor-error/task.md b/1-js/09-classes/02-class-inheritance/1-class-constructor-error/task.md index 380a4720b2..42adfd864b 100644 --- a/1-js/09-classes/02-class-inheritance/1-class-constructor-error/task.md +++ b/1-js/09-classes/02-class-inheritance/1-class-constructor-error/task.md @@ -4,9 +4,9 @@ importance: 5 # Error creating an instance -Here's the code with `Rabbit` extending `Animal`. +아래에 Animal 클래스를 상속하는 Rabbit 코드가 있습니다. -Unfortunately, `Rabbit` objects can't be created. What's wrong? Fix it. +아직은 Rabbit 객체를 생성할 수 없습니다. 무엇이 잘못된 것일까요? 코드를 수정해보세요. ```js run class Animal { @@ -24,7 +24,7 @@ class Rabbit extends Animal { } *!* -let rabbit = new Rabbit("White Rabbit"); // Error: this is not defined +let rabbit = new Rabbit("White Rabbit"); // 에러: 정의되지 않음 */!* alert(rabbit.name); ``` diff --git a/1-js/09-classes/02-class-inheritance/2-clock-class-extended/task.md b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/task.md index bbc2c6a43c..6992f57944 100644 --- a/1-js/09-classes/02-class-inheritance/2-clock-class-extended/task.md +++ b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/task.md @@ -2,14 +2,14 @@ importance: 5 --- -# Extended clock +# 시계 확장하기 We've got a `Clock` class. As of now, it prints the time every second. - +`Clock`이라는 매초 시간을 출력하는 클래스가 있습니다. [js src="source.view/clock.js"] -Create a new class `ExtendedClock` that inherits from `Clock` and adds the parameter `precision` -- the number of `ms` between "ticks". Should be `1000` (1 second) by default. +`Clock`을 상속하는 `ExtendedClock`을 생성해서 `precision`이라는 매개변수를 추가해보세요 -- "ticks" 사이에 ms의 수 만큼. 1000 (1 second)를 기본값으로 하는것이 좋습니다. -- Your code should be in the file `extended-clock.js` -- Don't modify the original `clock.js`. Extend it. +– 코드는 `extended-clock.js`라는 독립적인 파일인 것이 좋습니다. +– 오리지널 `clock.js`를 수정하지 마세요. 확장하세요. diff --git a/1-js/09-classes/02-class-inheritance/3-class-extend-object/solution.md b/1-js/09-classes/02-class-inheritance/3-class-extend-object/solution.md index ca9e80601b..6fa87690c9 100644 --- a/1-js/09-classes/02-class-inheritance/3-class-extend-object/solution.md +++ b/1-js/09-classes/02-class-inheritance/3-class-extend-object/solution.md @@ -1,14 +1,14 @@ -First, let's see why the latter code doesn't work. +먼저, 해당 코드가 왜 작동하지 않는지 살펴봐야 합니다. -The reason becomes obvious if we try to run it. An inheriting class constructor must call `super()`. Otherwise `"this"` won't be "defined". +이유는 코드를 실행한다면 명백해 보입니다. 상속받는 클래스는 `super()`를 반드시 호출해야 합니다. 그렇지 않으면 `"this"` 는 "정의"되지 않기 때문이죠. -So here's the fix: +아래 수정된 코드가 있습니다. ```js run class Rabbit extends Object { constructor(name) { *!* - super(); // need to call the parent constructor when inheriting + super(); // 상속할 때 부모의 생성자를 호출해야 합니다 */!* this.name = name; } @@ -19,16 +19,16 @@ let rabbit = new Rabbit("Rab"); alert( rabbit.hasOwnProperty('name') ); // true ``` -But that's not all yet. +그런데 그게 전부는 아닙니다. -Even after the fix, there's still important difference in `"class Rabbit extends Object"` versus `class Rabbit`. +위와 같이 수정한다고 해도, 여전히 `"class Rabbit extends Object"` 와 `class Rabbit` 사이에는 다른 점이 존재합니다. -As we know, the "extends" syntax sets up two prototypes: +이미 배웠듯이, "extends" 문법은 두개의 프로토타입을 설정합니다. -1. Between `"prototype"` of the constructor functions (for methods). -2. Between the constructor functions themselves (for static methods). +1. 생성자 함수들의 `"prototype"` 사이에 (메서드를 위한것). +2. 생성자 함수 자신의 사이에 (정적인 메서드를 위한것). -In our case, for `class Rabbit extends Object` it means: +예제의 경우에는, `class Rabbit extends Object` 가 뜻하는 것은 아래와 같습니다. ```js run class Rabbit extends Object {} @@ -37,45 +37,45 @@ alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) true alert( Rabbit.__proto__ === Object ); // (2) true ``` -So `Rabbit` now provides access to static methods of `Object` via `Rabbit`, like this: +그래서 `Rabbit` 이 `Object` 의 정적인 메서드에 `Rabbit` 을 통해서 접근할 수 있게 해야 합니다. 아래와 같이 말이죠. ```js run class Rabbit extends Object {} *!* -// normally we call Object.getOwnPropertyNames +// 보통은 Object.getOwnPropertyNames 로 호출합니다 alert ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // a,b */!* ``` -But if we don't have `extends Object`, then `Rabbit.__proto__` is not set to `Object`. +그러나 `extends Object` 가 없다면, `Rabbit.__proto__` 은 `Object` 로 설정되지 않습니다. -Here's the demo: +아래에 예시가 있습니다. ```js run class Rabbit {} -alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) true -alert( Rabbit.__proto__ === Object ); // (2) false (!) -alert( Rabbit.__proto__ === Function.prototype ); // as any function by default +alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) true Rabbit의 프로토타입은 객체의 프로토타입입니다. +alert( Rabbit.__proto__ === Object ); // (2) false (!) Rabbit의 프로토타입은 객체가 아닙니다. +alert( Rabbit.__proto__ === Function.prototype ); // 어떠한 함수든 기본으로 __proto__는 함수의 프로토타입입니다. *!* // error, no such function in Rabbit -alert ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // Error +alert ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // 에러 */!* ``` -So `Rabbit` doesn't provide access to static methods of `Object` in that case. +그래서 `Rabbit` 은 `Object` 의 정적인 메서드에 접근을 할 수 없습니다. -By the way, `Function.prototype` has "generic" function methods, like `call`, `bind` etc. They are ultimately available in both cases, because for the built-in `Object` constructor, `Object.__proto__ === Function.prototype`. +어쨋든 `Function.prototype` 은 `call`, `bind` 기타 등등의 "제네릭" 함수의 메서드를 가집니다. 제네릭 함수들은 궁극적으로 두 가지 경우에 모두 사용 가능 합니다. 왜냐하면, `Object`의 내장된 생성자는 `Object.__proto__ === Function.prototype` 이기 때문이죠. -Here's the picture: +이해를 돕기 위한 그림이 있습니다. ![](rabbit-extends-object.svg) -So, to put it short, there are two differences: +그래서, 짧게 요약하면 두 가지 다른 점이 있습니다. | class Rabbit | class Rabbit extends Object | |--------------|------------------------------| -| -- | needs to call `super()` in constructor | +| -- | `super()`를 생성자에서 호출해야 한다 | | `Rabbit.__proto__ === Function.prototype` | `Rabbit.__proto__ === Object` | diff --git a/1-js/09-classes/02-class-inheritance/3-class-extend-object/task.md b/1-js/09-classes/02-class-inheritance/3-class-extend-object/task.md index b82a4255e9..e842bfaf0c 100644 --- a/1-js/09-classes/02-class-inheritance/3-class-extend-object/task.md +++ b/1-js/09-classes/02-class-inheritance/3-class-extend-object/task.md @@ -2,11 +2,11 @@ importance: 5 --- -# Class extends Object? +# 객체를 확장하는 클래스? -As we know, all objects normally inherit from `Object.prototype` and get access to "generic" object methods like `hasOwnProperty` etc. +이미 살펴보았듯이, 보통 모든 객체들은 `Object.prototype` 으로 부터 상속되고 `hasOwnProperty` 같은 "제네릭" 객체의 메서드에 접근이 가능합니다 -For instance: +아래 예시가 있습니다. ```js run class Rabbit { @@ -18,16 +18,16 @@ class Rabbit { let rabbit = new Rabbit("Rab"); *!* -// hasOwnProperty method is from Object.prototype +// hasOwnProperty 메서드는 Object.prototype 으로 부터 왔습니다 alert( rabbit.hasOwnProperty('name') ); // true */!* ``` -But if we spell it out explicitly like `"class Rabbit extends Object"`, then the result would be different from a simple `"class Rabbit"`? +그런데 만약에 명시적으로 `"class Rabbit extends Object"` 라고 사용한다면, 결과는 간단히 `"class Rabbit"`로 사용하는 것과 다를까요? -What's the difference? +그렇다면 무엇이 다를까요? -Here's an example of such code (it doesn't work -- why? fix it?): +아래에 예시가 있습니다. (이것은 작동하지 않습니다 -- 왜 그럴까요? 작동하게 수정할 수 있을까요?) ```js class Rabbit extends Object { diff --git a/1-js/09-classes/02-class-inheritance/article.md b/1-js/09-classes/02-class-inheritance/article.md index 58245e6696..86d52d8014 100644 --- a/1-js/09-classes/02-class-inheritance/article.md +++ b/1-js/09-classes/02-class-inheritance/article.md @@ -1,7 +1,7 @@ -# Class inheritance +# 클래스 상속 -Let's say we have two classes. +두 개의 클래스가 있다고 가정해 보겠습니다. `Animal`: @@ -27,7 +27,7 @@ let animal = new Animal("My animal"); ![](rabbit-animal-independent-animal.svg) -...And `Rabbit`: +...그리고 `Rabbit`: ```js class Rabbit { @@ -45,13 +45,13 @@ let rabbit = new Rabbit("My rabbit"); ![](rabbit-animal-independent-rabbit.svg) -Right now they are fully independent. +현재 위의 예시 클래스들은 완전히 독립적입니다. -But we'd want `Rabbit` to extend `Animal`. In other words, rabbits should be based on animals, have access to methods of `Animal` and extend them with its own methods. +그런데 `Rabbit`을 `Animal`의 연장선에 놓고 싶다고 한다면. 자세히 설명하자면, rabbits는 Animal 클래스를 기반으로 두고 있고, `Animal` 클래스의 메서드들과 독립적인 메서드들로 구성되게 하고 싶다고 해보죠. -To inherit from another class, we should specify `"extends"` and the parent class before the braces `{..}`. +다른 클래스로부터 상속받으려면, `"extends"`를 사용해서 braces `{..}`전에 부모 클래스를 명시해야 합니다. -Here `Rabbit` inherits from `Animal`: +아래 코드는 `Rabbit`이 `Animal`을 상속하는 방법을 보여주고 있습니다. ```js run class Animal { @@ -69,7 +69,7 @@ class Animal { } } -// Inherit from Animal by specifying "extends Animal" +// "extends Animal"를 사용해서 Animal로 부터 상속 받습니다 *!* class Rabbit extends Animal { */!* @@ -80,24 +80,24 @@ class Rabbit extends Animal { let rabbit = new Rabbit("White Rabbit"); -rabbit.run(5); // White Rabbit runs with speed 5. -rabbit.hide(); // White Rabbit hides! +rabbit.run(5); // 하얀토끼는 스피드 5로 달립니다. +rabbit.hide(); // 하얀토끼는 숨었습니다! ``` -Now the `Rabbit` code became a bit shorter, as it uses `Animal` constructor by default, and it also can `run`, as animals do. +이제 `Rabbit` 코드는 `Animal`의 생성자를 사용함으로써 더 짧아졌고 Animal 클래스에서 정의된 `run` 메서드를 사용할 수 있습니다. -Internally, `extends` keyword adds `[[Prototype]]` reference from `Rabbit.prototype` to `Animal.prototype`: +내부적으로, `extends` 키워드는 `[[Prototype]]` 참고하는 것을 `Rabbit.prototype`에서 `Animal.prototype`으로 추가합니다. -![](animal-rabbit-extends.svg) +![](animal-rabbit-extends.png) -So, if a method is not found in `Rabbit.prototype`, JavaScript takes it from `Animal.prototype`. +그래서 만약에 `Rabbit.prototype` 안에서 메서드를 찾을 수 없으면, 자바스크립트는 `Animal.prototype`에서 가져옵니다. -As we can recall from the chapter , JavaScript uses prototypal inheritance for build-in objects. E.g. `Date.prototype.[[Prototype]]` is `Object.prototype`, so dates have generic object methods. + 챕터에서 살펴봤듯이, 자바스크립트는 내장 객체를 위해 같은 프로토타입을 상속합니다. 예를 들면, `Date.prototype.[[Prototype]]` 은 `Object.prototype` 이기 때문에 dates는 일반적인 객체의 메서드인 것이죠. -````smart header="Any expression is allowed after `extends`" -Class syntax allows to specify not just a class, but any expression after `extends`. +````smart header="`extends` 이후에 어떠한 표현형식이든 가능합니다" +클래스 문법은 오직 클래스만 정의할 수 있게 하는 것이 아니라 `extends` 후에 어떠한 표현 식이든 추가할 수 있습니다. -For instance, a function call that generates the parent class: +예를 들면, 아래의 함수처럼 부모 클래스를 생성한다고 해보겠습니다. ```js run function f(phrase) { @@ -112,33 +112,33 @@ class User extends f("Hello") {} new User().sayHi(); // Hello ``` -Here `class User` inherits from the result of `f("Hello")`. +위의 예제에서 `class User` 는 `f("Hello")`의 결과를 상속합니다. -That may be useful for advanced programming patterns when we use functions to generate classes depending on many conditions and can inherit from them. +이런 방법은 함수를 사용하여 다양한 조건에 따라 클래스를 생성하고 상속이 요구되는 고급 프로그래밍 패턴을 사용할 때 유용합니다. ```` -## Overriding a method +## 메서드 오버라이딩 -Now let's move forward and override a method. As of now, `Rabbit` inherits the `stop` method that sets `this.speed = 0` from `Animal`. +이제 더 자세히 배워보겠습니다. 지금까지는 `Rabbit`이 `stop`메서드를 상속하고 `Animal`에서 온 `stop`메서드는 `this.speed = 0`으로 설정했었습니다. -If we specify our own `stop` in `Rabbit`, then it will be used instead: +만약에 독립적인 `stop`메서드를 `Rabbit` 안에 정의할 수 있다면 다음과 같을 것입니다. ```js class Rabbit extends Animal { stop() { - // ...this will be used for rabbit.stop() + // ...여기에 정의될 메서드가 rabbit.stop() 으로 호출될 것입니다. } } ``` -...But usually we don't want to totally replace a parent method, but rather to build on top of it to tweak or extend its functionality. We do something in our method, but call the parent method before/after it or in the process. +...그러나 일반적으로 부모 메서드를 완전히 대체하기보다는 그 위에 기능을 추가하거나 수정하거나 확장하는 것이 좋습니다. 메서드에서 무언가를 수행하지만 처리 전후 또는 처리 중에 부모 메서드를 호출하기 때문이죠. -Classes provide `"super"` keyword for that. +이것을 위해 클래스들은 `"super"`라는 키워드를 제공합니다. -- `super.method(...)` to call a parent method. -- `super(...)` to call a parent constructor (inside our constructor only). +- `super.method(...)` 는 부모 클래스의 메서드를 호출합니다. +- `super(...)` 는 부모의 생성자를 호출합니다. (상속받는 생성자의 안에서만 사용해야 합니다). -For instance, let our rabbit autohide when stopped: +예를 들어 아래의 rabbit에서 stop 되었을 때 어떻게 자동으로 hide를 호출하는지 알아보겠습니다. ```js run class Animal { @@ -167,52 +167,52 @@ class Rabbit extends Animal { *!* stop() { - super.stop(); // call parent stop - this.hide(); // and then hide + super.stop(); // 부모의 stop을 호출합니다. + this.hide(); // 그리고 hide 메서드를 실행합니다. } */!* } let rabbit = new Rabbit("White Rabbit"); -rabbit.run(5); // White Rabbit runs with speed 5. -rabbit.stop(); // White Rabbit stands still. White rabbit hides! +rabbit.run(5); // 하얀 토끼는 스피드 5로 달립니다. +rabbit.stop(); // 하얀 토끼는 그대로 서 있습니다. 그리고 숨었습니다! ``` -Now `Rabbit` has the `stop` method that calls the parent `super.stop()` in the process. +여기서 `Rabbit`은 부모 클래스로부터 `super.stop()`메서드를 호출하는 `stop` 메서드를 가지고 있습니다. -````smart header="Arrow functions have no `super`" -As was mentioned in the chapter , arrow functions do not have `super`. +````smart header="화살표 함수는 `super`가 없습니다" + 챕터에서 언급했듯이, 화살표 함수는 `super`를 가지고 있지 않습니다. -If accessed, it's taken from the outer function. For instance: +만약에 부모 함수에 접근 가능하다면 외부의 함수에서 가져온 것일 것입니다. 아래 예제를 보면 알 수 있습니다. ```js class Rabbit extends Animal { stop() { - setTimeout(() => super.stop(), 1000); // call parent stop after 1sec + setTimeout(() => super.stop(), 1000); // 부모의 stop을 1초 후에 호출합니다 } } ``` -The `super` in the arrow function is the same as in `stop()`, so it works as intended. If we specified a "regular" function here, there would be an error: +화살표 함수 안의 `super`는 `stop()` 안에서 쓰인 것과 같아서 의도한 대로 동작할 것입니다. 그런데 만약에 "정규" 함수를 여기에 정의한다면, 에러가 발생할 것입니다. ```js -// Unexpected super +// 예상하지 못한 super setTimeout(function() { super.stop() }, 1000); ``` ```` -## Overriding constructor +## 생성자 오버라이딩 -With constructors it gets a little bit tricky. +생성자를 오버라이딩 하는 데에는 기술이 조금 필요합니다. -Till now, `Rabbit` did not have its own `constructor`. +아직은 `Rabbit` 은 자신만의 `생성자(contructor)`를 가지고 있지 않았습니다. -According to the [specification](https://tc39.github.io/ecma262/#sec-runtime-semantics-classdefinitionevaluation), if a class extends another class and has no `constructor`, then the following "empty" `constructor` is generated: +[자바스크립트 명세서](https://tc39.github.io/ecma262/#sec-runtime-semantics-classdefinitionevaluation)에 따르면 만약 클래스가 다른 클래스를 확장하고 생성된 클래스가 `생성자`를 가지고 있지 않다면, "비어있는" `생성자`를 생성합니다. ```js class Rabbit extends Animal { - // generated for extending classes without own constructors + // 자신의 생성자가 없는 상속받는 클래스를 생성합니다. *!* constructor(...args) { super(...args); @@ -221,9 +221,9 @@ class Rabbit extends Animal { } ``` -As we can see, it basically calls the parent `constructor` passing it all the arguments. That happens if we don't write a constructor of our own. +위에 예시에서처럼 해당 클래스의 생성자를 명시하지 않을 때는 기본적으로 부모의 `생성자`를 모든 인수와 함께 전달합니다. -Now let's add a custom constructor to `Rabbit`. It will specify the `earLength` in addition to `name`: +이제 `Rabbit`에 특수한 생성자를 추가해 보겠습니다. 생성자에서 `earLength`를 정의하고 `name`을 추가할 것입니다. ```js run class Animal { @@ -248,29 +248,29 @@ class Rabbit extends Animal { } *!* -// Doesn't work! -let rabbit = new Rabbit("White Rabbit", 10); // Error: this is not defined. +// 아래의 코드는 동작하지 않습니다! +let rabbit = new Rabbit("White Rabbit", 10); // 에러: 정의되지 않음.. */!* ``` -Whoops! We've got an error. Now we can't create rabbits. What went wrong? +이런! 에러가 발생합니다. 이제 rabbits를 생성할 수 없네요. 무엇이 잘못된 것일까요? -The short answer is: constructors in inheriting classes must call `super(...)`, and (!) do it before using `this`. +간단히 설명하자면 상속받은 클래스들 안의 생성자는 `this`를 사용하기 전에 `super(...)`를 반드시 호출해야 합니다. -...But why? What's going on here? Indeed, the requirement seems strange. +...그런데 왜 그래야 할까요? 무슨 일이 일어나고 있는 걸까요? 결국, 이런 요구사항은 뭔가 이상해 보입니다. -Of course, there's an explanation. Let's get into details, so you'll really understand what's going on. +당연히 super를 반드시 호출해야 하는 상황을 설명할 수 있습니다. 자세히 보면 무슨 일이 일어나는지 이해할 수 있을 것입니다. -In JavaScript, there's a distinction between a "constructor function of an inheriting class" and all others. In an inheriting class, the corresponding constructor function is labeled with a special internal property `[[ConstructorKind]]:"derived"`. +자바스크립트에서는 "상속하는 클래스의 생성자 함수"와 다른 모든 클래스 사이에는 구분이 있습니다. 상속 클래스에서 상응하는 생성자 함수는 특별한 내부 속성 `[[ConstructorKind]]:"derived"` 로 레이블이 지정되어 있습니다. 하지만 일반적으로 상위 메소드를 완전히 대체하기보다는 그 위에 기능을 추가하거나 수정하거나 확장하는 것이 좋습니다. 메서드에서 무언가를 수행하는 것처럼 보이지만, 처리 전후 또는 처리 중에 부모 메서드를 호출하기 때문이죠. -The difference is: +다른 점은 다음과 같습니다. -- When a normal constructor runs, it creates an empty object and assigns it to `this`. -- But when a derived constructor runs, it doesn't do this. It expects the parent constructor to do this job. +- 일반 생성자가 실행되면 빈 객체를 `this`로 만들고 계속 진행합니다. +- 그러나 파생된 생성자가 실행될 때는 파생되지 않습니다. 부모 생성자가 이러한 작업을 수행할 것으로 기대합니다. -So if we're making a constructor of our own, then we must call `super`, because otherwise the object for `this` won't be created. And we'll get an error. +그래서 만약 생성자를 생성한다면, `super`를 호출해야 합니다. 그렇지 않으면 `this`를 빈 객체로 생성할 것이기 때문입니다. 그래서 오류가 발생한 것입니다. -For `Rabbit` constructor to work, it needs to call `super()` before using `this`, like here: +`Rabbit`이 작동하려면, 다음과 같이 `this`를 사용하기 전에 `super()`를 호출해야 합니다. ```js run class Animal { @@ -306,25 +306,25 @@ alert(rabbit.earLength); // 10 ## Super: internals, [[HomeObject]] -```warn header="Advanced information" -If you're reading the tutorial for the first time - this section may be skipped. +```warn header="추가 정보" +만약에 이 강좌를 처음 보는 것이라면 이번 파트는 넘어가도 됩니다. -It's about the internal mechanisms behind inheritance and `super`. +다음 내용은 상속과 `super`의 내부 매커니즘에 대한 것입니다. ``` -Let's get a little deeper under the hood of `super`. We'll see some interesting things along the way. +super의 숨겨진 부분을 조금 더 깊숙이 들어가 보겠습니다. 흥미로운 것들을 보게 될 것입니다. -First to say, from all that we've learned till now, it's impossible for `super` to work at all! +먼저 지금까지 배운 모든 것에서, `super`가 전혀 작동하지 않는다는 것을 명심하세요! -Yeah, indeed, let's ask ourselves, how it should technically work? When an object method runs, it gets the current object as `this`. If we call `super.method()` then, the engine needs to get the `method` from the prototype of the current object. But how? +실제 기술적으로 어떻게 super가 작동 할 수 있는지 스스로 답해보세요. 객체 메서드가 실행되면 현재 객체를 `this` 로 가져옵니다. `super.method()` 를 호출하면 현재 객체의 프로토타입에서 `method` 를 가져와야합니다. 그런데 어떻게 하면 그렇게 할까요? -The task may seem simple, but it isn't. The engine knows the current object `this`, so it could get the parent `method` as `this.__proto__.method`. Unfortunately, such a "naive" solution won't work. +작업은 단순해 보일 수도 있지만 그렇지 않습니다. 엔진은 현재 객체 `this`를 알고 있음으로, 부모 `method` 를 `this .__ proto __. method`로 얻을 수 있을것 같지만 불행히도 이러한 "간단한" 해결책은 효과가 없습니다. -Let's demonstrate the problem. Without classes, using plain objects for the sake of simplicity. +여기에 어떠한 문제가 있는지 설명해 보겠습니다. 클래스가 없으면 단순성을 위해 일반 오브젝트를 사용합니다. -You may skip this part and go below to the `[[HomeObject]]` subsection if you don't want to know the details. That won't harm. Or read on if you're interested in understanding things in-depth. +만약에 더 자세히 알고 싶지 않다면 아래의 `[[HomeObject]]`부분은 넘어가도 됩니다. 모른다고 문제 될 것은 없으니 더 자세히 알고 싶은 분만 읽어보시길 바랍니다. -In the example below, `rabbit.__proto__ = animal`. Now let's try: in `rabbit.eat()` we'll call `animal.eat()`, using `this.__proto__`: +아래 `rabbit .__ proto__ = animal` 예시를 보면, `rabbit.eat ()`에서`this .__ proto__`를 사용하여`animal.eat ()`를 호출하는 것을 시도합니다. ```js run let animal = { @@ -339,7 +339,7 @@ let rabbit = { name: "Rabbit", eat() { *!* - // that's how super.eat() could presumably work + // super.eat() 이 작동할 방법일 것입니다 this.__proto__.eat.call(this); // (*) */!* } @@ -348,11 +348,11 @@ let rabbit = { rabbit.eat(); // Rabbit eats. ``` -At the line `(*)` we take `eat` from the prototype (`animal`) and call it in the context of the current object. Please note that `.call(this)` is important here, because a simple `this.__proto__.eat()` would execute parent `eat` in the context of the prototype, not the current object. +`(*)`줄에서는 prototype (`animal`)에서`eat`을 가져와서 현재 객체의 컨텍스트에서 호출합니다. 간단한`this .__ proto __. eat ()`은 현재 객체가 아니라 프로토타입의 맥락에서 부모의 `eat` 을 실행할 것이기 때문에 `.call (this)`이 중요하게 사용되었습니다. -And in the code above it actually works as intended: we have the correct `alert`. +그래서 위의 코드는 실제 의도 한 대로 작동합니다. `경고` 문구가 올바르게 출력됩니다. -Now let's add one more object to the chain. We'll see how things break: +이제 하나의 객체를 체인에 추가해 봅시다. 상황이 어떻게 잘못되는지 볼 수 있습니다. ```js run let animal = { @@ -365,7 +365,7 @@ let animal = { let rabbit = { __proto__: animal, eat() { - // ...bounce around rabbit-style and call parent (animal) method + // ...rabbit-style 로 바운싱해서 부모(animal)의 메서드를 호출 this.__proto__.eat.call(this); // (*) } }; @@ -373,59 +373,59 @@ let rabbit = { let longEar = { __proto__: rabbit, eat() { - // ...do something with long ears and call parent (rabbit) method + // ...long ears 에서 부모(rabbit)의 메서드를 호출 this.__proto__.eat.call(this); // (**) } }; *!* -longEar.eat(); // Error: Maximum call stack size exceeded +longEar.eat(); // 에러: 최대 호출 스택 크기를 초과했습니다. */!* ``` -The code doesn't work anymore! We can see the error trying to call `longEar.eat()`. +코드가 이제는 작동하지 않습니다! `longEar.eat ()`를 호출하는 동안 오류를 볼 수 있습니다. -It may be not that obvious, but if we trace `longEar.eat()` call, then we can see why. In both lines `(*)` and `(**)` the value of `this` is the current object (`longEar`). That's essential: all object methods get the current object as `this`, not a prototype or something. +정확하게 보이지 않을 수도 있습니다만 `longEar.eat ()` 호출을 추적하면 그 이유를 알 수 있습니다. `(*)` 와 `(**)` 두 줄에서 `this`의 값은 현재 객체 (`longEar`)입니다. 이것은 필수입니다. 그래서 모든 객체 메서드는 현재의 객체를 프로토타입이나 무언가가 아닌 `this`로 얻습니다. -So, in both lines `(*)` and `(**)` the value of `this.__proto__` is exactly the same: `rabbit`. They both call `rabbit.eat` without going up the chain in the endless loop. +그래서`(*)`와`(**)`두 줄에서 `this .__ proto__` 의 값은 정확히 `rabbit`으로 동일합니다. 그래서 둘 다 무한 루프속에서 `rabbit.eat`을 호출하려고 합니다. -Here's the picture of what happens: +아래 그림에서 무슨 일이 일어나고 있는지 이해를 도와줄 것입니다. ![](this-super-loop.svg) -1. Inside `longEar.eat()`, the line `(**)` calls `rabbit.eat` providing it with `this=longEar`. +1. `longEar.eat ()` 내부에서 `(**)` 는 `this = longEar` 를 가지고 있는 `rabbit.eat` 을 호출합니다. ```js - // inside longEar.eat() we have this = longEar + // longEar.eat() 내부에서는 this = longEar 입니다 this.__proto__.eat.call(this) // (**) - // becomes + // 위의 코드는 다음과 같고 longEar.__proto__.eat.call(this) - // that is + // longEar의 prototype은 rabbit이므로 이렇게 되는 것을 의미합니다 rabbit.eat.call(this); ``` -2. Then in the line `(*)` of `rabbit.eat`, we'd like to pass the call even higher in the chain, but `this=longEar`, so `this.__proto__.eat` is again `rabbit.eat`! +2. 그런 다음`rabbit.eat`의`(*)`줄에서 더 위 단계에 호출을 전달하려고 합니다. 그러나 `this = longEar` 이므로 `this .__ proto __. eat` 은 다시 `rabbit.eat` 입니다! ```js - // inside rabbit.eat() we also have this = longEar + // rabbit.eat() 내부에서 다시 this = longEar 입니다 this.__proto__.eat.call(this) // (*) - // becomes + // 다음과 같고 longEar.__proto__.eat.call(this) - // or (again) + // 또는 (또다시) 이렇게 됩니다 rabbit.eat.call(this); ``` -3. ...So `rabbit.eat` calls itself in the endless loop, because it can't ascend any further. +3. ...그래서 `rabbit.eat` 은 더는 올라가서 호출할 수가 없어 스스로들 부르기 때문에 무한 루프가 됩니다. -The problem can't be solved by using `this` alone. +이런 문제는 `this`를 독립적으로 사용해서는 해결할 수 없습니다. ### `[[HomeObject]]` -To provide the solution, JavaScript adds one more special internal property for functions: `[[HomeObject]]`. +해결책을 제공하기 위해 자바스크립트는 함수를 위한 `[[HomeObject]]`라는 하나의 특별한 내부 속성을 추가합니다. -When a function is specified as a class or object method, its `[[HomeObject]]` property becomes that object. +함수가 클래스 나 객체 메서드로 지정되면, 그것의 [[HomeObject]] 속성은 그 객체가 됩니다. -Then `super` uses it to resolve the parent prototype and its methods. +그런 다음 `super` 는 [[HomeObject]] 를 사용하여 부모 프로토타입과 그 메소드를 결정합니다. -Let's see how it works, first with plain objects: +먼저 일반 객체로 어떻게 작동하는지 살펴보겠습니다. ```js run let animal = { @@ -452,22 +452,22 @@ let longEar = { }; *!* -// works correctly +// 올바르게 작동합니다 longEar.eat(); // Long Ear eats. */!* ``` -It works as intended, due to `[[HomeObject]]` mechanics. A method, such as `longEar.eat`, knows its `[[HomeObject]]` and takes the parent method from its prototype. Without any use of `this`. +`[[HomeObject]]`의 메커니즘 때문에 의도한 대로 작동합니다. `longEar.eat` 과 같은 메서드는 그것의 [[HomeObject]] 를 알고 프로토타입으로부터 부모 메서드를 취합니다. `this`를 사용하지 않고도 말이죠. -### Methods are not "free" +### "자유"롭지 않은 메서드 -As we've known before, generally functions are "free", not bound to objects in JavaScript. So they can be copied between objects and called with another `this`. +이전에 알려진 것처럼 일반적으로 함수는 "자유"이며 자바스크립트의 객체에 바인딩 되지 않습니다. 그래서 함수들은 객체 간에 복사될 수 있고 또 다른 `this` 로 호출될 수 있습니다. -The very existance of `[[HomeObject]]` violates that principle, because methods remember their objects. `[[HomeObject]]` can't be changed, so this bond is forever. +`[[HomeObject]]`의 존재는 메서드가 객체를 기억하기 때문에 위와 같은 원리를 위반합니다. `[[HomeObject]]`는 변경할 수 없음으로 함수와 객체 간의 유대감은 영원합니다. -The only place in the language where `[[HomeObject]]` is used -- is `super`. So, if a method does not use `super`, then we can still consider it free and copy between objects. But with `super` things may go wrong. +`[[HomeObject]]`가 사용되는 언어의 유일한 장소는 `super` 입니다. 따라서 메서드가 `super` 를 사용하지 않는다면, 여전히 함수들을 자유롭게 다룰 수 있고 객체들 사이에서 복사 할 수 있습니다. 하지만 `super`로도 상황이 잘못될 수 있습니다. -Here's the demo of a wrong `super` result after copying: +다음은 잘못된 `super`의 사용예시입니다. ```js run let animal = { @@ -503,28 +503,28 @@ tree.sayHi(); // I'm an animal (?!?) */!* ``` -A call to `tree.sayHi()` shows "I'm an animal". Definitevely wrong. +`tree.sayHi()`를 호출하면 "I'm an animal"을 출력합니다. 이것은 확실히 잘못된 결과입니다. -The reason is simple: -- In the line `(*)`, the method `tree.sayHi` was copied from `rabbit`. Maybe we just wanted to avoid code duplication? -- Its `[[HomeObject]]` is `rabbit`, as it was created in `rabbit`. There's no way to change `[[HomeObject]]`. -- The code of `tree.sayHi()` has `super.sayHi()` inside. It goes up from `rabbit` and takes the method from `animal`. +잘못된 결과가 나온 이유는 간단합니다. +- `(*)` 줄에서 `tree.sayHi` 메소드는`rabbit` 에서 복사되었습니다. 어쩌면 단지 코드 중복을 피하고 싶었던 걸까요? +- 그래서 tree 객체의 `[[HomeObject]]`는 rabbit 안에 만들어졌기 때문에 `rabbit`입니다. `[[HomeObject]]`를 ​​변경할 방법은 없습니다. +- `tree.sayHi()` 의 코드는 `super.sayHi()` 를 내부에 가지고 있습니다. `rabbit`에서 부모객체인 `animal` 에서 가져온 것이죠. -Here's the diagram of what happens: +아래의 다이어그램은 어떻게 진행되는지 보여주고 있습니다. ![](super-homeobject-wrong.svg) -### Methods, not function properties +### 메서드는 함수의 프로퍼티가 아닙니다 -`[[HomeObject]]` is defined for methods both in classes and in plain objects. But for objects, methods must be specified exactly as `method()`, not as `"method: function()"`. +`[[HomeObject]]`는 클래스와 일반 객체 모두에서 메서드에 대해 정의됩니다. 그러나 객체의 경우, 메서드는 `method ()` 처럼 정확히 지정되어야 합니다. 예를 들면 `"메서드: function () "`같이 말이죠. -The difference may be non-essential for us, but it's important for JavaScript. +이러한 차이는 필수는 아니지만, 자바스크립트에서는 중요합니다. -In the example below a non-method syntax is used for comparison. `[[HomeObject]]` property is not set and the inheritance doesn't work: +아래 예제에서는 비 메서드 구문을 사용하여 비교해 보겠습니다. `[[HomeObject]]`프로퍼티로 설정되지 않고 상속되지 않습니다. ```js run let animal = { - eat: function() { // intentially writing like this instead of eat() {... + eat: function() { // 짧은 문법으로는 eat() {...} 이 되어야 합니다 // ... } }; @@ -537,21 +537,21 @@ let rabbit = { }; *!* -rabbit.eat(); // Error calling super (because there's no [[HomeObject]]) +rabbit.eat(); // super 호출 에러 (왜냐하면 [[HomeObject]]가 없기 때문입니다) */!* ``` -## Summary +## 요약 -1. To extend a class: `class Child extends Parent`: - - That means `Child.prototype.__proto__` will be `Parent.prototype`, so methods are inherited. -2. When overriding a constructor: - - We must call parent constructor as `super()` in `Child` constructor before using `this`. -3. When overriding another method: - - We can use `super.method()` in a `Child` method to call `Parent` method. -4. Internals: - - Methods remember their class/object in the internal `[[HomeObject]]` property. That's how `super` resolves parent methods. - - So it's not safe to copy a method with `super` from one object to another. +1. 클래스를 확장하는 법: `class Child extends Parent` + - 이 문법은 `Child.prototype.__proto__`이 `Parent.prototype`되는 것을 의미하기 때문에 매서드들이 상속된다. +2. 생성자를 오버라이딩 할 때는 + - 부모의 생성자를 `super()`로 `this`가 사용되기 전에 `자식`의 생성자 안에서 호출해야 한다. +3. 다른 메서드를 오버라이딩 할 때는 + - `부모`의 메서드를 호출할 때 `super.method()`로 `자식` 안에서 사용할 수 있다. +4. Internals + - 메서드는 내부 `[[HomeObject]]` 프로퍼티에 그것의 클래스와 오브젝트를 기억한다. `[[HomeObject]]`는 어떻게 `super`가 부모 메서드들을 호출할 수 있게 하는가에 대한 문제를 해결하는 방법이다. + - 그래서 메서드를 `super`와 함께 한 객체에서 다른 객체로 복사하는 것은 안전하지 않다. -Also: -- Arrow functions don't have own `this` or `super`, so they transparently fit into the surrounding context. +또한 +- 화살표 함수들은 자신의 `this`나 `super`를 가지고 있지 않다. 그래서 주변 컨텍스트에 영향 없이 투명하게 사용할 수 있다. diff --git a/1-js/99-js-misc/03-currying-partials/article.md b/1-js/99-js-misc/03-currying-partials/article.md index e11aebeb66..1ae2a12a07 100644 --- a/1-js/99-js-misc/03-currying-partials/article.md +++ b/1-js/99-js-misc/03-currying-partials/article.md @@ -3,21 +3,21 @@ libs: --- -# 커링 +# Currying -[커링 (Currying)](https://en.wikipedia.org/wiki/Currying) 은 함수와 함께 사용할 수 있는 고급기술입니다. 오직 자바스크립트에만 존재하는 것은 아니고 다른 언어에도 존재하죠. +[Currying](https://en.wikipedia.org/wiki/Currying) is an advanced technique of working with functions. It's used not only in JavaScript, but in other languages as well. -커링은 `f(a, b, c)`처럼 단일 호출로 처리하는 함수를 `f(a)(b)(c)`와 같이 각각의 인수가 호출 가능한 프로세스로 호출된 후 병합되도록 변환하는 것입니다. +Currying is a transformation of functions that translates a function from callable as `f(a, b, c)` into callable as `f(a)(b)(c)`. -커링은 함수를 호출하지 않습니다. 단지 변환할 뿐이죠. +Currying doesn't call a function. It just transforms it. -먼저 예제를 통해서 커링이 무엇인지 이해하고 그다음에 실용적인 적용법을 알아보겠습니다. +Let's see an example first, to better understand what we're talking about, and then practical applications. -`f`의 두 개의 인수를 커링하는 헬퍼 함수 `curry(f)`를 생성해 보겠습니다. 다른 말로 하면, `f(a, b)`처럼 두 개의 인수를 요구하는 함수를 `f(a)(b)` 형식으로 변환하는 `curry(f)`라는 함수를 만드는 것입니다. +We'll create a helper function `curry(f)` that performs currying for a two-argument `f`. In other words, `curry(f)` for two-argument `f(a, b)` translates it into a function that runs as `f(a)(b)`: ```js run *!* -function curry(f) { // 커링 변환을 하는 curry(f) 함수 +function curry(f) { // curry(f) does the currying transform return function(a) { return function(b) { return f(a, b); @@ -36,30 +36,30 @@ let curriedSum = curry(sum); alert( curriedSum(1)(2) ); // 3 ``` -위의 예시에서 보듯이, 실제 구현은 그저 두 개의 래퍼를 사용한 것과 같이 간단합니다. +As you can see, the implementation is straightforward: it's just two wrappers. -- `curry(func)`의 반환값은 `function(a)`형태의 래퍼입니다. -- `carriedSum`이 `sum(1)`과 같은 함수를 호출했을 때처럼, 그 인수는 렉시컬 환경에 저장이 되고 새로운 래퍼 `function(b)`이 반환됩니다. -- 그리고 반환된 `function(b)`래퍼 함수가 `2`를 인수로 호출됩니다. 그리고 반환값이 원래의 `sum`으로 넘겨져서 호출됩니다. +- The result of `curry(func)` is a wrapper `function(a)`. +- When it is called like `sum(1)`, the argument is saved in the Lexical Environment, and a new wrapper is returned `function(b)`. +- Then this wrapper is called with `2` as an argument, and it passes the call to the original `sum`. -lodash 라이브러리의 [_.curry](https://lodash.com/docs#curry) 같이 래퍼를 반환할 때 함수가 보통 때처럼 또는 partial 적으로 호출하는 것을 허용하는 더 진보적으로 구현된 커링도 있습니다. +More advanced implementations of currying, such as [_.curry](https://lodash.com/docs#curry) from lodash library, return a wrapper that allows a function to be called both normally and partially: ```js run function sum(a, b) { return a + b; } -let carriedSum = _.curry(sum); // lodash 라이브러리의 _.carry 사용 +let curriedSum = _.curry(sum); // using _.curry from lodash library -alert( carriedSum(1, 2) ); // 3, 보통 때 처럼 호출가능 -alert( carriedSum(1)(2) ); // 3, partially 호출되었음 +alert( curriedSum(1, 2) ); // 3, still callable normally +alert( curriedSum(1)(2) ); // 3, called partially ``` -## 커링은 어디에 써야할까요? +## Currying? What for? -커링의 이점을 이해하려면 가치가 있을 만한 실제 사례가 필요합니다. +To understand the benefits we need a worthy real-life example. -예를 들어, 정보를 형식화하고 출력하는 로그 함수 `log (date, importance, message)`가 있다고 가정해 보겠습니다. 실제 프로젝트에서 이러한 함수는 네트워크를 통해 로그를 보내는 것과 같은 많은 유용한 기능을 제공합니다. 여기서는 `alert 창` 을 띄우는 것으로 해보겠습니다. +For instance, we have the logging function `log(date, importance, message)` that formats and outputs the information. In real projects such functions have many useful features like sending logs over the network, here we'll just use `alert`: ```js function log(date, importance, message) { @@ -67,53 +67,53 @@ function log(date, importance, message) { } ``` -커링을 적용해보겠습니다! +Let's curry it! ```js log = _.curry(log); ``` -위와 같이 커링을 적용한 후에도 기존 함수 `log` 는 정상적으로 작동합니다. +After that `log` work normally: ```js log(new Date(), "DEBUG", "some debug"); // log(a, b, c) ``` -...그러나 커링을 적용한 함수를 호출해도 정상적으로 동작합니다. +...But also works in the curried form: ```js log(new Date())("DEBUG")("some debug"); // log(a)(b)(c) ``` -아래처럼 현재 시간으로 로그를 출력하는데 편리하도록 log 함수를 작성해서 사용할 수 있습니다. +Now we can easily make a convenience function for current logs: ```js -// logNow 는 log 의 첫 번째 인수가 고정된 partial이 될 것입니다. +// logNow will be the partial of log with fixed first argument let logNow = log(new Date()); // use it logNow("INFO", "message"); // [HH:mm] INFO message ``` -이제 `logNow` 는 `log`의 첫 번째 인수를 고정한 것입니다. 다른 말로 하면 "partially 적용된 함수" 또는 짧게 하면 "partial" 입니다. +Now `logNow` is `log` with fixed first argument, in other words "partially applied function" or "partial" for short. -이제 더 나아가서 디버깅 로그를 편리하게 하는 함수를 만들어 보겠습니다. +We can go further and make a convenience function for current debug logs: ```js let debugNow = logNow("DEBUG"); -debugNow("message"); // [HH:mm] DEBUG 메세지 +debugNow("message"); // [HH:mm] DEBUG message ``` -결과적으로 -1. 커링한 후에 잃은 것은 없습니다. `log`는 아직 보통 때처럼 호출할 수 있습니다. -2. 오늘의 로그 같은 partial 함수를 쉽게 작성할 수 있습니다. +So: +1. We didn't lose anything after currying: `log` is still callable normally. +2. We can easily generate partial functions such as for today's logs. -## 고급 커리 구현 +## Advanced curry implementation -만약 좀 더 깊이 공부하고 싶다면 (필수는 아닙니다!), 예제를 통해서 다중-인수를 허용하는 "고급"커리를 구현하는 방법을 알아보겠습니다. +In case you'd like to get in details, here's the "advanced" curry implementation for multi-argument functions that we could use above. -사실 꽤 짧습니다. +It's pretty short: ```js function curry(func) { @@ -131,7 +131,7 @@ function curry(func) { } ``` -사용 예시입니다. +Usage examples: ```js function sum(a, b, c) { @@ -140,17 +140,17 @@ function sum(a, b, c) { let curriedSum = curry(sum); -alert( curriedSum(1, 2, 3) ); // 6, 보통때 처럼 단일 callable 형식으로 호출하기 -alert( curriedSum(1)(2,3) ); // 6, 첫 번째 인수를 커링하기 -alert( curriedSum(1)(2)(3) ); // 6, 모두 커링하기 +alert( curriedSum(1, 2, 3) ); // 6, still callable normally +alert( curriedSum(1)(2,3) ); // 6, currying of 1st arg +alert( curriedSum(1)(2)(3) ); // 6, full currying ``` -새로운 `curry`는 복잡해 보일 수도 있지만 사실 이해하기는 쉽습니다. +The new `curry` may look complicated, but it's actually easy to understand. -`curry(func)`의 반환값은 `curried`라는 아래의 래퍼와 같습니다. +The result of `curry(func)` call is the wrapper `curried` that looks like this: ```js -// func 이 변환되어야 하는 함수입니다 +// func is the function to transform function curried(...args) { if (args.length >= func.length) { // (1) return func.apply(this, args); @@ -162,35 +162,35 @@ function curried(...args) { }; ``` -위의 예시를 실행시키면, 두 개의 `if` 분기점이 있습니다. +When we run it, there are two `if` execution branches: -(1)에 해당하는 경우(함수가 호출되었을때): `args` 를 카운트한 갯수가 전달된 원래 함수 func (`func.length`)와 같거나 길다면, 그대로 `func` 호출에 전달함. -(2)에 해당하는 경우(partial이 적용될때): 아직 `func`이 호출되지 않습니다. `pass`라는 래퍼가 대신 반환되고, `pass` 래퍼함수가 `curried`를 이전함수와 새로운 인수와 함께 다시 적용합니다. 그 다음 새로운 `curried` 호출에, 다시 새로운 partial (만약에 인수가 충분하지 않으면)을 반환하거나 최종적으로 `func` 결과를 반환합니다. +1. Call now: if passed `args` count is the same as the original function has in its definition (`func.length`) or longer, then just pass the call to it. +2. Get a partial: otherwise, `func` is not called yet. Instead, another wrapper `pass` is returned, that will re-apply `curried` providing previous arguments together with the new ones. Then on a new call, again, we'll get either a new partial (if not enough arguments) or, finally, the result. -예를 들면, `sum(a, b, c)` 예시에서 어떻게 진행되었는지 살펴보세요. 인수가 세 개이므로 `sum.length = 3` 입니다. +For instance, let's see what happens in the case of `sum(a, b, c)`. Three arguments, so `sum.length = 3`. -`curried(1)(2)(3)`이 호출되는 과정은 다음과 같습니다. +For the call `curried(1)(2)(3)`: -1. 첫 번째 `curried(1)` 을 호출할때 `1`을 렉시컬 환경에 기억하고 `curried(1)` 이 `pass` 래퍼를 반환합니다. -2. `pass`래퍼가 `(2)`와 함께 호출됩니다. 이전의 인수인 (`1`)을 가져서 `(2)`와 연결하고`curried (1, 2)`를 함께 호출합니다. 인수의 개수는 아직 3보다 작기때문에 `curry`는 `pass`를 반환합니다. -3. `pass` 래퍼가 다시 `(3)`과 함께 호출됩니다. 다음 호출인 `pass(3)`가 이전의 인수들인 (`1`, `2`)를 가져오고 `3`을 추가하고 `curried(1, 2, 3)` 호출을 합니다 -- 여기에 `3`인수는 마지막으로, 원래의 함수에 전달됩니다. +1. The first call `curried(1)` remembers `1` in its Lexical Environment, and returns a wrapper `pass`. +2. The wrapper `pass` is called with `(2)`: it takes previous args (`1`), concatenates them with what it got `(2)` and calls `curried(1, 2)` with them together. As the argument count is still less than 3, `curry` returns `pass`. +3. The wrapper `pass` is called again with `(3)`, for the next call `pass(3)` takes previous args (`1`, `2`) and adds `3` to them, making the call `curried(1, 2, 3)` -- there are `3` arguments at last, they are given to the original function. -아직 확실하게 이해되지 않았다면, 호출되는 순서를 마음속이나 종이에 그려보세요. +If that's still not obvious, just trace the calls sequence in your mind or on the paper. -```smart header="오직 고정된 길이의 함수들만 사용 가능합니다" -커링은 해당 함수가 고정된 개수의 인수를 가지도록 요구합니다. +```smart header="Fixed-length functions only" +The currying requires the function to have a fixed number of arguments. -`f(...args)`같은 나머지 매개변수를 사용하는 함수는 이러한 방법으로 커리할 수 없습니다. +A function that uses rest parameters, such as `f(...args)`, can't be curried this way. ``` -```smart header="커링보다는 조금 더" -커링의 정의에 따르면, 커링은 `sum(a, b, c)`을 `sum(a)(b)(c)`으로 변환해야 합니다. +```smart header="A little more than currying" +By definition, currying should convert `sum(a, b, c)` into `sum(a)(b)(c)`. -그러나 커링의 구현은 자바스크립트에서 고급단계입니다. 이번 챕터에서 알아보았듯이 커링은 다중-인수를 단일 프로세스로 callable 한 함수를 다중 프로세스 형태로 변형할 수 있도록 하는 것입니다. +But most implementations of currying in JavaScript are advanced, as described: they also keep the function callable in the multi-argument variant. ``` -## 요약 +## Summary -*커링*은 `f(a,b,c)`를 `f(a)(b)(c)` 와 같이 다중 callable 프로세스 형태로 변환하는 기술입니다. 보통 자바스크립트에서의 커링되어진 함수는 평소처럼 호출도 하고 만약에 인수들이 충분하지 않을 때에는 partial을 반환합니다. +*Currying* is a transform that makes `f(a,b,c)` callable as `f(a)(b)(c)`. JavaScript implementations usually both keep the function callable normally and return the partial if arguments count is not enough. -커링은 partials를 쉽게 적용할 수 있도록 합니다. 로그 예시에서 보았듯이 다목적으로 쓰였던 `log(date, importance, message)` 함수는 커링후에 `log(date)`같이 하나의 인수를 가진 형태나 `log(date, importance)`처럼 두 개의 인수를 가진 형태로 호출할 수 있었습니다. +Currying allows to easily get partials. As we've seen in the logging example: the universal function `log(date, importance, message)` after currying gives us partials when called with one argument like `log(date)` or two arguments `log(date, importance)`.