it-swarm.dev

ECMAScript 6에서는 언제 Arrow 기능을 사용해야합니까?

질문은 다가오는 ECMAScript 6 (Harmony)의 컨텍스트에서 코드 스타일을 생각하고 이미 언어로 작업 한 사람들을 대상으로합니다.

() => {}function () {}을 사용하여 ES6에서 함수를 작성하는 두 가지 방법이 매우 유사합니다. 다른 언어에서는 람다 함수가 종종 익명으로 구별되지만 ECMAScript에서는 모든 함수가 익명 일 수 있습니다. 두 가지 유형 각각에는 고유 한 사용 도메인이 있습니다 (즉, this이 명시 적으로 또는 명시 적으로 바인딩되지 않아야 함). 이 두 도메인 사이에는 표기법을 사용하는 방대한 수의 사례가 있습니다.

ES6의 화살표 기능에는 최소한 두 가지 제한 사항이 있습니다.

  • new을 사용하지 마십시오.
  • 초기화시 범위에 고정 된 this 고정

이러한 두 가지 제한 사항을 제외하고 화살표 기능은 이론적으로 거의 모든 곳에서 정규 기능을 대체 할 수 있습니다. 실제로 그것을 사용하는 올바른 접근법은 무엇입니까? 예 : 화살표 기능을 사용해야합니다.

  • "어디에서나 작동합니다", 즉 어디에서나 함수는 this 변수에 대해 불가지론 할 필요가 없으며 개체를 만들지 않습니다.
  • 특정 범위에 바인딩되어야하는 이벤트 리스너, 시간 초과 등 '필요한 모든 곳'에서만
  • '짧은'기능은 있지만 '긴'기능은 아닙니다.
  • 다른 화살표 기능을 포함하지 않는 함수에서만

내가 찾고있는 것은 향후 버전의 ECMAScript에서 적절한 함수 표기법을 선택하는 지침이다. 지침은 명확해야하므로 팀의 개발자에게 가르쳐 질 수 있고 일관성을 유지하여 한 기능 표기법에서 다른 기능 표기법으로 앞뒤로 끊임없이 리팩토링 할 필요가 없습니다.

365
lyschoening

얼마 전에 우리 팀은 모든 코드 (중간 크기 AngularJS 앱)를 사용하여 컴파일 된 JavaScript로 마이그레이션했습니다. Traceur 바벨 . 이제 ES6 이상의 기능에 대해 다음과 같은 경험 법칙을 사용하고 있습니다.

  • 전역 범위와 Object.prototype 속성에 대해 function을 사용하십시오.
  • 개체 생성자에 class을 사용하십시오.
  • 다른 곳에서는 =>를 사용하십시오.

왜 화살표 기능을 거의 모든 곳에서 사용합니까?

  1. 범위 보안 : 화살표 함수가 일관되게 사용되면 루트와 동일한 thisObject을 사용하는 것이 모두 보장됩니다. 하나의 표준 함수 콜백이 여러개의 화살표 함수와 섞여 있다면 범위가 엉망이 될 가능성이 있습니다.
  2. 조밀도 : 화살표 기능은 읽고 쓰는 것이 더 쉽습니다. (이것은 약간의 예를 더 제공 할 것입니다.
  3. 선명도 : 거의 모든 것이 화살표 기능 일 때 일반 function은 즉시 범위 정의에 집중합니다. 개발자는 항상 다음 function 문을 검색하여 thisObject이 무엇인지 확인할 수 있습니다.

전역 범위 나 모듈 범위에서 항상 정규 함수를 사용하는 이유는 무엇입니까?

  1. thisObject에 액세스하면 안되는 함수를 나타냅니다.
  2. window 개체 (전역 범위)는 명시 적으로 가장 잘 처리됩니다.
  3. 많은 Object.prototype 정의는 전역 범위 (String.prototype.truncate 등)에 있으며, 일반적으로 function 유형이어야합니다. 전역 범위에서 function을 지속적으로 사용하면 오류를 방지하는 데 도움이됩니다.
  4. 전역 범위의 많은 함수는 구식 클래스 정의를위한 객체 생성자입니다.
  5. 함수의 이름을 지정할 수 있습니다.1. 이것은 두 가지 이점이 있습니다 : (1) function foo(){}보다 const foo = () => {}을 쓰는 것이 그리 어렵지 않습니다 - 특히 다른 함수 호출을 제외하고 말입니다. (2) 함수 이름이 스택 트레이스에 표시됩니다. 모든 내부 콜백을 지명하는 것은 지루할 수 있지만 모든 공용 함수의 이름을 지정하는 것은 좋은 생각 일 수 있습니다.
  6. 함수 선언은 정적 유틸리티 함수에서 유용한 속성 인 (커서가 선언되기 전에 액세스 될 수 있음을 의미 함)입니다.


객체 생성자

화살표 함수를 인스턴스화하려고하면 예외가 발생합니다.

var x = () => {};
new x(); // TypeError: x is not a constructor

따라서 화살표 함수보다 함수의 한 가지 주요 이점은 객체 생성자의 두 가지 기능입니다.

function Person(name) {
    this.name = name;
}

그러나, 기능적으로 동일한2 ES 하모니 초안 클래스 정의 는 거의 비슷합니다.

class Person {
    constructor(name) {
        this.name = name;
    }
}

전 표기법을 사용하면 결과적으로 낙심 할 것으로 예상됩니다. 객체 생성자 표기법은 객체가 프로그래밍 방식으로 생성되는 단순한 익명 객체 팩토리에 대해 일부에서는 여전히 사용될 수 있지만 다른 경우에는 그렇지 않습니다.

객체 생성자가 필요한 곳에서는 위에 표시된 것처럼 함수를 class으로 변환하는 것을 고려해야합니다. 구문은 익명 함수/클래스에서도 작동합니다.


화살표 기능의 가독성

범위 함수의 안전성을 저해하는 정규 함수를 고수하는 가장 좋은 방법은 화살표 함수가 일반 함수보다 읽기 쉽지 않다는 것입니다. 처음부터 코드가 작동하지 않는다면 화살표 함수가 필요하지 않은 것처럼 보일 수 있으며, 화살표 함수가 일관되게 사용되지 않으면 추한 것처럼 보입니다.

ECMAScript 5.1은 for-loops가 이전에 사용되었던 기능을 사용하는 기능적 프로그래밍 기능과 Array.forEach, Array.map 기능을 ECMAScript 5.1에서 제공하여 꽤 많이 변경되었습니다. 비동기 자바 스크립트는 꽤 많이 벗어났습니다. ES6는 또한 Promise 객체를 제공 할 것이며, 이는 더 많은 익명의 기능을 의미합니다. 함수형 프로그래밍에는 아무런 변화가 없습니다. 함수형 JavaScript에서는 화살표 함수가 일반 함수보다 바람직합니다.

예를 들어이 (특히 혼란스러운) 코드 조각을 가져 가라.:

function CommentController(articles) {
    this.comments = [];

    articles.getList()
        .then(articles => Promise.all(articles.map(article => article.comments.getList())))
        .then(commentLists => commentLists.reduce((a, b) => a.concat(b)));
        .then(comments => {
            this.comments = comments;
        })
}

규칙적인 기능을 가진 동일한 코드 조각 :

function CommentController(articles) {
    this.comments = [];

    articles.getList()
        .then(function (articles) {
            return Promise.all(articles.map(function (article) { 
                return article.comments.getList();
            }));
        })
        .then(function (commentLists) {
            return commentLists.reduce(function (a, b) {
                return a.concat(b); 
            });
        })
        .then(function (comments) {
            this.comments = comments;
        }.bind(this));
}

화살표 함수 중 하나는 표준 함수로 대체 될 수 있지만 그렇게하면 얻는 것이 거의 없을 것입니다. 어떤 버전이 더 읽기 쉽습니까? 나는 첫번째 것을 말할 것이다.

화살 함수 나 정규 함수 중 어느 것을 사용할 지에 대한 질문은 시간이 지남에 따라 덜 중요해 질 것이라고 생각합니다. 대부분의 함수 는 클래스 메소드가되어 function 키워드를 없애거나 클래스가됩니다. 함수는 Object.prototype를 통해 클래스를 패치하는 데 계속 사용됩니다. 그 동안 실제로 클래스 메서드 또는 클래스 여야하는 모든 것에 대해 function 키워드를 예약하는 것이 좋습니다.


메모

  1. 명명 된 화살표 함수는 ES6 spec 에서 지연되었습니다. 그들은 미래 버전을 추가 할 수 있습니다.
  2. 초안 명세서 에 따르면 "클래스 선언/표현식은 클래스가 extend 키워드를 사용하지 않는 한 함수 선언과 똑같은 생성자 함수/프로토 타입 쌍"을 만듭니다. 사소한 차이점은 클래스 선언은 상수이지만 함수 선언은 그렇지 않다는 것입니다.
  3. 단일 명령문 화살표 함수의 블록에 대한 참고 사항 : 화살표 함수 만 부작용 (예 : 할당)을 호출 할 때마다 블록을 사용하고 싶습니다. 그런 식으로 리턴 값을 버릴 수 있습니다.
295
lyschoening

proposal 에 따르면, 화살표는 "전통적인 Function Expression의 몇 가지 일반적인 문제점을 해결하고 해결하는 것"을 목표로했습니다. this을 어휘 적으로 바인딩하고 간결한 구문을 제공하여 문제를 개선하려고했습니다.

하나,

  • this을 어휘 적으로 일관되게 바인딩 할 수 없습니다
  • 화살표 함수 구문은 섬세하고 모호합니다

따라서 화살표 함수는 혼동과 오류의 기회를 제공하며 JavaScript 프로그래머의 어휘에서 제외되고 function로만 바뀝니다.

어휘에 대하여 this

this 문제가 있습니다 :

function Book(settings) {
    this.settings = settings;
    this.pages = this.createPages();
}
Book.prototype.render = function () {
    this.pages.forEach(function (page) {
        page.draw(this.settings);
    }, this);
};

화살표 함수는 콜백 내에서 this 속성에 액세스해야하는 문제를 해결하려고합니다. 이미 여러 가지 방법이 있습니다. this을 변수에 할당하거나 bind을 사용하거나 Array 집계 메소드에서 사용 가능한 세 번째 인수를 사용할 수 있습니다. 그러나 화살표가 가장 간단한 해결 방법 인 것처럼 보이므로 방법을 다음과 같이 리팩터링 할 수 있습니다.

this.pages.forEach(page => page.draw(this.settings));

그러나 코드가 jQuery와 같은 라이브러리를 사용했는지, 그 메소드가 this을 특별히 바인드하는지 고려하십시오. 이제 다루어야 할 두 가지 this 값이 있습니다.

Book.prototype.render = function () {
    var book = this;
    this.$pages.each(function (index) {
        var $page = $(this);
        book.draw(book.currentPage + index, $page);
    });
};

function을 (를) each을 (를) 동적으로 바인딩하려면 this을 사용해야합니다. 여기서는 화살표 기능을 사용할 수 없습니다.

여러 this 값을 다루는 것은 저자가 어떤 this을 (를) 말하고 있는지 알기가 어렵 기 때문에 혼동 될 수 있습니다.

function Reader() {
    this.book.on('change', function () {
        this.reformat();
    });
}

저자가 실제로 Book.prototype.reformat에게 전화하려고 했습니까? 아니면 this을 (를) 바인딩하지 않고 Reader.prototype.reformat (으)로 전화하려고합니까? 핸들러를 화살표 함수로 변경하면 저자가 동적 this을 원했지만 한 줄에 맞도록 화살표를 선택했는지 궁금해 할 것입니다.

function Reader() {
    this.book.on('change', () => this.reformat());
}

"화살표가 때때로 사용하기에 잘못된 기능 일 수 있다는 것은 예외적인가? 아마도 우리가 동적 this 값을 거의 필요로하지 않는다면, 대부분의 시간에 화살표를 사용하는 것이 좋다."

그러나 스스로에게 다음과 같이 질문하십시오. "코드를 디버깅하고 오류의 결과가 '에지 케이스 (Edge case)'에 의해 발생한다는 것을 발견하는 것이 '가치'일까요?"나는 대부분의 시간뿐만 아니라 문제를 피하는 것을 선호하지만 시간의 100 %.

더 좋은 방법이 있습니다. 항상 function (따라서 this은 항상 동적으로 바인딩 될 수 있음)를 사용하고 항상 변수를 통해 this을 참조하십시오. 변수는 어휘이며 많은 이름을 가정합니다. this을 변수에 할당하면 의도가 명확 해집니다.

function Reader() {
    var reader = this;
    reader.book.on('change', function () {
        var book = this;
        book.reformat();
        reader.reformat();
    });
}

또한alwaysthis을 변수에 지정하면 (단일 this이 있거나 다른 기능이없는 경우에도) 자신의 의도가 명확하게 유지됩니다. 코드가 변경되었습니다.

또한 동적 this도 예외가 아닙니다. jQuery는 5 천만 개가 넘는 웹 사이트에서 사용됩니다 (2016 년 2 월 현재 작성). this을 (를) 동적으로 바인딩하는 다른 API는 다음과 같습니다.

  • Mocha (어제 최대 120k 다운로드)는 this을 통해 테스트 방법을 노출합니다.
  • 그런트 (어제 ~ 63k 다운로드)는 this을 통해 빌드 작업을위한 메소드를 제공합니다.
  • 백본 (어제 최대 22k 다운로드)은 this에 액세스하는 메소드를 정의합니다.
  • DOM과 같은 이벤트 API는 EventTarget과 함께 this을 참조합니다.
  • 패치되거나 확장 된 프로토 타입 API는 this 인 인스턴스를 참조합니다.

( http://trends.builtwith.com/javascript/jQueryhttps://www.npmjs.com 을 통한 통계)

동적 this 바인딩이 이미 필요합니다.

어휘 this이 때로는 예상되지만 때로는 그렇지 않습니다. 동적 this이 예상되는 것처럼 때로는 그렇지 않습니다. 고맙게도 예상되는 바인딩을 항상 생성하고 전달하는 더 좋은 방법이 있습니다.

간결한 구문에 대하여

화살표 함수는 함수에 대한 "더 짧은 구문 형태"를 제공하는 데 성공했습니다. 그러나이 짧은 기능으로 더 성공할 수 있습니까?

function (x) { return x * x; }보다 x => x * x "읽기 쉬운"입니까? 아마도 한 줄의 짧은 코드를 생성 할 가능성이 높기 때문일 수 있습니다. 다이슨의 화면에서 읽는 효과에 대한 읽기 속도 및 줄 길이의 영향 ,

보통 및 빠른 속도에서 효과적인 판독을 지원하는 중간 줄 길이 (한 줄에 55 자)가 나타납니다. 이것은 최고 수준의 이해력을 만들어 냈습니다. . .

조건부 (삼항) 연산자와 단일 행 if 문에 대해서도 비슷한 근거가 있습니다.

그러나 당신은실제로 쓰는간단한 수학 함수 제안에 광고 됨 ? 내 도메인은 수학적이지 않으므로 서브 루틴이 그렇게 우아하지는 않습니다. 오히려 화살표 함수가 열 제한을 깨고 편집기 또는 스타일 가이드로 인해 다른 줄로 줄 바꿈되어 다이슨의 정의에 따라 "가독성"이 무효화되는 것을 볼 수 있습니다.

"가능한 경우 짧은 기능을 위해 짧은 버전을 사용하는 것은 어떻습니까?" 그러나 이제는 문체 규칙이 언어 제약 조건과 모순됩니다. "가장 짧은 표기법 만 예상 한대로 this을 바인딩한다는 점을 염두에두고 가능한 가장 짧은 함수 표기법을 사용하십시오." 이러한 혼란은 화살을 특히 오용하기 쉽다.

화살표 함수 구문에는 여러 가지 문제가 있습니다.

const a = x =>
    doSomething(x);

const b = x =>
    doSomething(x);
    doSomethingElse(x);

이 두 기능은 구문 상 유효합니다. 그러나 doSomethingElse(x);은 (는) b의 본문이 아니며 들여 쓰기가 잘 안된 최상위 문장 일뿐입니다.

블록 형식으로 확장하면 더 이상 암시적인 return이 없어 복원 할 수 없습니다. 그러나이 표현은only부작용을 일으키기위한 것일 수 있습니다. 그렇다면 명시적인 return이 앞으로 필요할지 누가 알 수 있습니까?

const create = () => User.create();

const create = () => {
    let user;
    User.create().then(result => {
        user = result;
        return sendEmail();
    }).then(() => user);
};

const create = () => {
    let user;
    return User.create().then(result => {
        user = result;
        return sendEmail();
    }).then(() => user);
};

Rest 매개 변수로 의도 된 것은 스프레드 연산자로 구문 분석 될 수 있습니다.

processData(data, ...results => {}) // Spread
processData(data, (...results) => {}) // Rest

할당은 기본 인수와 혼동 될 수 있습니다.

const a = 1;
let x;
const b = x => {}; // No default
const b = x = a => {}; // "Adding a default" instead creates a double assignment
const b = (x = a) => {}; // Remember to add parens

블록은 객체처럼 보입니다 :

(id) => id // Returns `id`
(id) => {name: id} // Returns `undefined` (it's a labeled statement)
(id) => ({name: id}) // Returns an object

이것은 무엇을 의미 하는가?

() => {}

작성자가 no-op 또는 빈 객체를 반환하는 함수를 만들려고 했습니까? (이 점을 염두에두고 { 뒤에 =>을 배치해야합니까? 표현식 구문으로 만 제한해야합니까? 그러면 화살표 빈도가 더 줄어 듭니다.)

=><=>=와 같습니다.

x => 1 ? 2 : 3
x <= 1 ? 2 : 3

if (x => 1) {}
if (x >= 1) {}

화살표 함수 표현식을 즉시 호출하려면 ()을 외부에 배치해야하지만 내부에 ()을 배치하는 것은 유효하며 의도적 일 수 있습니다.

(() => doSomething()()) // Creates function calling value of `doSomething()`
(() => doSomething())() // Calls the arrow function

그러나 즉시 호출 된 함수 표현식을 작성하려는 의도로 (() => doSomething()());을 작성하더라도 아무 일도 일어나지 않습니다.

위의 모든 경우를 고려하여 화살표 기능이 "더 이해하기 쉽다"고 주장하기는 어렵습니다. 하나의could이 구문을 사용하는 데 필요한 모든 특수 규칙을 익히십시오. 정말 가치가 있습니까?

function의 구문이 예외적으로 일반화되었습니다. function을 독점적으로 사용한다는 것은 언어 자체가 혼란스러운 코드를 작성하지 못하게한다는 것을 의미합니다. 모든 경우에 구문 적으로 이해해야하는 절차를 작성하려면 function을 선택하십시오.

가이드 라인에 대하여

"명확하고"일관성이 있어야하는 지침을 요청하십시오. 화살표 함수를 사용하면 구문 적으로 유효하고 논리적으로 유효하지 않은 코드가 생성되며 두 함수 형태가 의미 있고 임의로 서로 얽혀 있습니다. 따라서 다음을 제공합니다.

ES6의 기능 표기법에 대한 지침 :

  • 항상 function으로 프로 시저를 작성하십시오.
  • 항상 this을 변수에 지정하십시오. () => {}를 사용하지 마십시오.
79
Jackson

scope 함수를 단순화하고 this 키워드를 더 간단하게 만들어 화살표 함수 를 만들었습니다. 화살표처럼 보이는 => 구문을 사용합니다.

주 : 기존 기능을 대체하지는 않습니다. 화살표 함수로 모든 함수 구문을 바꾸면 모든 경우에 작동하지 않습니다.

기존 ES5 구문을 살펴 보겠습니다. this 키워드가 객체의 메서드 (객체에 속한 함수) 안에 있으면이 객체는 무엇을 참조할까요?

var Actor = {
  name: 'RajiniKanth',
  getName: function() {
     console.log(this.name);
  }
};
Actor.getName();

위의 스 니펫은 object을 참조하고 "RajiniKanth"라는 이름을 출력합니다. 아래의 스 니펫을 살펴보고 여기에서 어떤 점을 지적하는지 살펴 보겠습니다.

var Actor = {
  name: 'RajiniKanth',
  movies: ['Kabali', 'Sivaji', 'Baba'],
  showMovies: function() {
   this.movies.forEach(function(movie) {
     alert(this.name + " has acted in " + movie);
   });
  }
};

Actor.showMovies();

이제 this 키워드가 method’s function 안에 있다면 어떨까요?

여기서는 scope에서 벗어난 window object보다 inner function를 참조합니다. this이 있기 때문에 항상 현재 창에있는 함수의 소유자를 참조합니다.이 경우는 범위를 벗어 났으므로 창/전역 객체입니다.

object의 메소드 내부에있을 때 function의 소유자가 객체입니다. 따라서 this 키워드는 객체에 바인딩됩니다. 그러나 함수 내부에있을 때, 단독으로 또는 다른 메서드 내에서 항상 window/global 객체를 참조합니다.

var fn = function(){
  alert(this);
}

fn(); // [object Window]

ES5 자체에서이 문제를 해결할 수있는 방법이 있지만 해결 방법에 대한 ES6 화살표 함수로 들어가기 전에 살펴 보겠습니다.

일반적으로 메서드의 내부 함수 외부에서 변수를 만듭니다. 이제 ‘forEach’ 메소드는 thisobject’s 속성과 해당 값에 대한 액세스 권한을 얻습니다.

var Actor = {
  name: 'RajiniKanth',
  movies: ['Kabali', 'Sivaji', 'Baba'],
  showMovies: function() {
   var _this = this;
   this.movies.forEach(function(movie) {
     alert(_this.name + " has acted in " + movie);
   });
  }
};

Actor.showMovies();

bind을 사용하여 메서드를 참조하는 this 키워드를 method’s inner function에 연결합니다.

var Actor = {
  name: 'RajiniKanth',
  movies: ['Kabali', 'Sivaji', 'Baba'],
  showMovies: function() {
   this.movies.forEach(function(movie) {
     alert(_this.name + " has acted in " + movie);
   }).bind(this);
  }
};

Actor.showMovies();

이제 ES6 arrow 함수를 사용하여 간단한 방법으로 lexical scoping 문제를 처리 할 수 ​​있습니다.

var Actor = {
  name: 'RajiniKanth',
  movies: ['Kabali', 'Sivaji', 'Baba'],
  showMovies: function() {
   this.movies.forEach((movie) => {
     alert(this.name + " has acted in " + movie);
   });
  }
};

Actor.showMovies();

Arrow functionsparent scopebind한다는 점을 제외하고는 함수 구문과 훨씬 비슷합니다. arrow function is in top scope, this 인수가 window/global scope를 참조하면 일반 함수 내부의 화살표 함수는 외부 함수와 동일한이 인수를 갖습니다.

arrow 함수를 사용하면 this이 생성시 scope을 묶어 바꿀 수 없으며 변경할 수 없습니다. 새로운 연산자 인 bind, call 및 apply는 이에 영향을주지 않습니다.

var asyncFunction = (param, callback) => {
  window.setTimeout(() => {
  callback(param);
  }, 1);
};

// With a traditional function if we don't control
// the context then can we lose control of `this`.
var o = {
  doSomething: function () {
  // Here we pass `o` into the async function,
  // expecting it back as `param`
  asyncFunction(o, function (param) {
  // We made a mistake of thinking `this` is
  // the instance of `o`.
  console.log('param === this?', param === this);
  });
  }
};

o.doSomething(); // param === this? false

위의 예에서 우리는이 제어를 잃어 버렸습니다. 위의 예제는 this 또는 bind 변수 참조를 사용하여 해결할 수 있습니다. ES6을 사용하면 thislexical scoping에 바인딩하는 것이 더 쉬워집니다.

var asyncFunction = (param, callback) => {
  window.setTimeout(() => {
  callback(param);
  }, 1);
};

var o = {
  doSomething: function () {
  // Here we pass `o` into the async function,
  // expecting it back as `param`.
  //
  // Because this arrow function is created within
  // the scope of `doSomething` it is bound to this
  // lexical scope.
  asyncFunction(o, (param) => {
  console.log('param === this?', param === this);
  });
  }
};

o.doSomething(); // param === this? true

화살표 기능이 아닐 때

객체 리터럴 내부

var Actor = {
  name: 'RajiniKanth',
  movies: ['Kabali', 'Sivaji', 'Baba'],
  getName: () => {
     alert(this.name);
  }
};

Actor.getName();

Actor.getName는 화살표 함수로 정의되지만 호출시 컨텍스트가 undefined로 유지되기 때문에 this.namewindow이므로 경고가 정의되지 않았습니다.

그것은 화살표 함수가 문맥을 window object ... 즉 외부 범위와 어휘 적으로 바인딩하기 때문에 발생합니다. this.name를 실행하는 것은 정의되지 않은 window.name와 동일합니다.

객체 프로토 타입

prototype object에서 메소드를 정의 할 때도 동일한 규칙이 적용됩니다. sayCatName 메서드를 정의하기 위해 화살표 함수를 사용하는 대신 잘못된 context window :

function Actor(name) {
  this.name = name;
}
Actor.prototype.getName = () => {
  console.log(this === window); // => true
  return this.name;
};
var act = new Actor('RajiniKanth');
act.getName(); // => undefined

생성자 호출

건설 호출에서 this은 새로 생성 된 객체입니다. 새 Fn ()을 실행할 때 constructor Fn의 컨텍스트는 this instanceof Fn === true라는 새 개체입니다.

this은 새롭게 생성 된 객체에 할당되지 않도록하는 외부 범위와 같이 둘러싸는 컨텍스트에서 설정됩니다.

var Message = (text) => {
  this.text = text;
};
// Throws "TypeError: Message is not a constructor"
var helloMessage = new Message('Hello World!');

동적 컨텍스트가있는 콜백

화살표 함수는 context을 정적으로 선언에 바인딩하며 동적으로 만들 수는 없습니다. 이벤트 리스너를 DOM 요소에 연결하는 것은 클라이언트 측 프로그래밍에서 일반적인 작업입니다. 이벤트는 this를 대상 요소로하여 핸들러 함수를 트리거합니다.

var button = document.getElementById('myButton');
button.addEventListener('click', () => {
  console.log(this === window); // => true
  this.innerHTML = 'Clicked button';
});

this은 전역 컨텍스트에 정의 된 화살표 함수의 창입니다. 클릭 이벤트가 발생하면 브라우저는 버튼 컨텍스트를 사용하여 핸들러 함수를 호출하려고 시도하지만 화살표 함수는 사전 정의 된 컨텍스트를 변경하지 않습니다. this.innerHTMLwindow.innerHTML와 같으며 의미가 없습니다.

대상 요소에 따라 이것을 변경할 수있는 함수 표현식을 적용해야합니다.

var button = document.getElementById('myButton');
button.addEventListener('click', function() {
  console.log(this === button); // => true
  this.innerHTML = 'Clicked button';
});

사용자가 버튼을 클릭하면 핸들러 함수에서 this가 button입니다. 따라서 this.innerHTML = 'Clicked button'는 클릭 된 상태를 반영하도록 버튼 텍스트를 올바르게 수정합니다.

참고 문헌 : https://rainsoft.io/when-not-to-use-arrow-functions-in-javascript/

32
Thalaivar

화살표 기능 - 지금까지 가장 널리 사용되는 ES6 기능 ...

사용법 : 다음 시나리오를 제외한 모든 ES5 기능을 ES6 화살표 기능으로 교체해야합니다.

화살표 기능을 사용해서는 안됩니다 :

  1. 우리가 기능 호이스트를 원할 때
    • 화살표 기능은 익명으로 처리됩니다.
  2. 함수 [______.]에서 this/arguments을 사용하려는 경우
    • 화살표 함수는 자신의 this/arguments을 가지지 않기 때문에 외부 컨텍스트에 의존합니다.
  3. 명명 된 함수 를 사용하려면
    • 화살표 기능은 익명으로 처리됩니다.
  4. Function을 constructor 으로 사용하고자 할 때,
    • 화살표 함수에는 this이 없습니다.
  5. 객체 literal에 속성을 속성으로 추가하고 object를 사용하려는 경우
    • this (객체 자체 여야 함)에 액세스 할 수 없으므로.

더 잘 이해하기 위해 화살표 함수의 변형을 이해합시다.

Variant 1 : 함수에 하나 이상의 인수를 전달하고 그 중 일부 값을 반환하려는 경우.

ES5 버전 :

var multiply = function (a,b) {
    return a*b;
};
console.log(multiply(5,6)); //30

ES6 버전 :

var multiplyArrow = (a,b) => a*b;
console.log(multiplyArrow(5,6)); //30

참고 : function 키워드는 필요하지 않습니다. =>는 필수 항목입니다. {}는 선택 사항입니다. {}return은 JavaScript에 의해 암시 적으로 추가되며 {}를 제공 할 때는 필요하면 return을 추가해야합니다.

Variant 2 : 하나의 인수 만 함수에 전달하고 그 중 일부 값을 반환하고자 할 때.

ES5 버전 :

var double = function(a) {
    return a*2;
};
console.log(double(2)); //4

ES6 버전 :

var doubleArrow  = a => a*2;
console.log(doubleArrow(2)); //4

참고 : 하나의 인수 만 전달할 경우 괄호 ()를 생략 할 수 있습니다.

Variant 3 : 함수에 인수를 전달하지 않고 값을 반환하지 않으려는 경우.

ES5 버전 :

var sayHello = function() {
    console.log("Hello");
};
sayHello(); //Hello

ES6 버전 :

var sayHelloArrow = () => {console.log("sayHelloArrow");}
sayHelloArrow(); //sayHelloArrow

변형 4 : 화살표 함수에서 명시 적으로 반환하고 싶을 때.

ES6 버전 :

var increment = x => {
  return x + 1;
};
console.log(increment(1)); //2

Variant 5 : 화살표 함수에서 객체를 반환하고자 할 때.

ES6 버전 :

var returnObject = () => ({a:5});
console.log(returnObject());

참고 : 괄호 안에 객체를 s어야합니다 () 그렇지 않으면 자바 스크립트는 블록과 객체를 구별 할 수 없습니다.

변형 6 : 화살표 함수에는 arguments (객체와 같은 배열)이 없으며 arguments의 외부 컨텍스트에 종속됩니다.

ES6 버전 :

function foo() {
  var abc = i => arguments[0];
  console.log(abc(1));
};    
foo(2); // 2

참고 : foo은 ES5 함수이며 arguments 배열은 객체와 같이 전달되며 인수는 2이므로 foo에 대한 arguments[0]는 2입니다.

abc은 ES6 화살표 함수이므로 자신의 arguments을 가지지 않으므로 대신 fooarguments[0]를 외부 문맥으로 인쇄합니다.

변형 7 : 화살표 함수에는 this에 대한 외부 컨텍스트에 의존하는 자체 this이 없습니다

ES5 버전 :

var obj5 = {
  greet: "Hi, Welcome ",
  greetUser : function(user) {
        setTimeout(function(){
        console.log(this.greet + ": " +  user); // "this" here is undefined.
        });
     }
};

obj5.greetUser("Katty"); //undefined: Katty

참고 : setTimeout에 전달 된 콜백은 ES5 함수이며 use-strict 환경에서 정의되지 않은 자체 this을 가지므로 출력을 얻습니다.

undefined: Katty

ES6 버전 :

var obj6 = {
  greet: "Hi, Welcome ",
  greetUser : function(user) {
    setTimeout(() => console.log(this.greet + ": " +  user)); 
      // this here refers to outer context
   }
};

obj6.greetUser("Katty"); //Hi, Welcome: Katty

참고 : setTimeout에 전달 된 콜백은 ES6 화살표 함수이며 자체 this을 가지고 있지 않으므로 greetUserthis 인 외부 컨텍스트에서 가져옵니다. 따라서 obj6는 출력됩니다.

Hi, Welcome: Katty

기타 : arrow 함수와 함께 new을 사용할 수 없습니다. 화살표 함수에는 prototype 속성이 없습니다. arrow 함수가 this 또는 apply을 통해 호출 될 때 call을 바인딩하지 않습니다.

13
Manishz90

지금까지의 큰 해답 이외에도 화살표 함수가 "일반적인"JavaScript 함수보다 근본적으로 더 나은 이유에 대해 아주 다른 이유를 제시하고자합니다. 토론을 위해 TypeScript 나 Facebook의 "Flow"와 같은 유형 검사기를 사용한다고 가정 해 보겠습니다. 유효한 장난감 코드 ECMAScript 6 코드와 Flow 타입 주석을 생각해보십시오. (이 답변의 끝 부분에 Babel에서 결과적으로 나타나는 형식화되지 않은 코드가 포함되어 실제로 실행될 수 있습니다.)

export class C {
  n : number;
  f1: number => number; 
  f2: number => number;

  constructor(){
    this.n = 42;
    this.f1 = (x:number) => x + this.n;
    this.f2 = function (x:number) { return  x + this.n;};
  }
}

이제 우리는 다음과 같이 다른 모듈에서 C 클래스를 사용할 때 어떤 일이 일어나는지 봅니다.

let o = { f1: new C().f1, f2: new C().f2, n: "foo" };
let n1: number = o.f1(1); // n1 = 43
console.log(n1 === 43); // true
let n2: number = o.f2(1); // n2 = "1foo"
console.log(n2 === "1foo"); // true, not a string!

보시다시피, 여기서 형식 검사기가 실패했습니다 : f2가 숫자를 반환한다고 가정했지만 문자열을 반환했습니다!

더 나쁜 것은 f2의 "this"가 f2의 인수 목록에서 발생하지 않기 때문에 생각할 수없는 유형 검사기 가 일반 (비 화살표) JavaScript 함수를 처리 할 수 ​​없다는 것입니다. "this"의 필수 유형을 f2에 대한 주석으로 추가 할 수 없습니다.

이 문제는 유형 검사기를 사용하지 않는 사람들에게도 영향을 줍니까? 정적 유형이없는 경우에도 우리는 마치 정적 유형이있는 것처럼 생각하기 때문에 그렇게 생각합니다. ( "첫 번째 매개 변수는 숫자 여야하며, 두 번째 매개 변수는 문자열이어야합니다."등) 함수의 본문에서 사용되거나 사용되지 않을 수있는 숨겨진 "this"인수는 우리의 정신 부기를 어렵게 만듭니다.

다음은 Babel에 의해 만들어 질 runnable untyped 버전입니다 :

class C {
    constructor() {
        this.n = 42;
        this.f1 = x => x + this.n;
        this.f2 = function (x) { return x + this.n; };
    }
}

let o = { f1: new C().f1, f2: new C().f2, n: "foo" };
let n1 = o.f1(1); // n1 = 43
console.log(n1 === 43); // true
let n2 = o.f2(1); // n2 = "1foo"
console.log(n2 === "1foo"); // true, not a string!
6
Carsten Führmann

Arrow function 이 this, arguments, super 또는 new.target 을 바인드하지 않기 때문에 항상 지역 this에 대한 액세스가 필요하지 않은 화살표 함수를 사용하는 것을 선호합니다.

3
zowers

간단한 방법으로,

var a =20; function a(){this.a=10; console.log(a);} 
//20, since the context here is window.

다른 예 :

var a = 20;
function ex(){
this.a = 10;
function inner(){
console.log(this.a); //can you guess the output of this line.
}
inner();
}
var test = new ex();

Ans : 콘솔에서 20을 인쇄합니다.

이 예제에서 ex 함수는 new 연산자와 함께 실행되어 컨텍스트가 만들어지며 inner이 실행되면 JS는 새 스택을 만들고 inner 함수를 실행합니다. global context에는 로컬 컨텍스트가 있습니다.

따라서 inner 함수가 ex 인 로컬 컨텍스트를 갖기를 원한다면 컨텍스트를 내부 함수에 바인딩해야합니다.

화살표는 Global context를 취하는 대신 local context를 취하는 대신이 문제를 해결합니다. given example,에서 this으로 new ex()을 사용합니다.

따라서 바인딩이 명시적인 모든 경우에 Arrows는 기본적으로 문제를 해결합니다.

화살표 함수 또는 Lambdas는 ES 6에서 도입되었습니다. 최소한의 구문에서 그 우아함을 제외하고는 가장 주목할만한 기능입니다 차이의 범위는this화살표 함수 내부

regular function 표현식에서 this 키워드는 호출 된 context 에 따라 다른 값에 바인딩됩니다.

화살표 함수에서 this lexically 바운드이므로 화살표 함수가 정의 된 범위 (parent-scope)에서 this을 닫습니다. 어디서 어떻게 호출/호출되는지에 관계없이 변경되지 않습니다.

객체에 대한 메소드로서의 화살표 기능

// this = global Window
let objA = {
 id: 10,
 name: "Simar",
 print () { // same as print: function() 
  console.log(`[${this.id} -> ${this.name}]`);
 }
}
objA.print(); // logs: [10 -> Simar]
objA = {
 id: 10,
 name: "Simar",
 print: () => {
  // closes over this lexically (global Window)
  console.log(`[${this.id} -> ${this.name}]`);
 }
};
objA.print(); // logs: [undefined -> undefined]

objA.print()의 경우 print() 메소드가 일반 function을 (를) 사용하여 정의 된 경우 메소드 호출을 위해 thisobjA (으)로 올바르게 해석하여 작동했지만 arrow=> 함수로 정의 된 경우 실패했습니다. 객체 (this)에서 메소드로 호출 될 때 일반 함수에서 objA이 객체 자체이기 때문입니다. 그러나 화살표 함수의 경우 this은 정의 된 엔 클로징 범위 (이 경우 전역/창)의 this에 사 전적으로 바인딩되며 objA의 메서드로 호출하는 동안 그대로 유지됩니다.

this이 시간 정의에서 고정 및 바인딩 될 것으로 예상되는 경우에만 객체의 메소드에서 정규 함수에 비해 화살표 함수의 장점.

/* this = global | Window (enclosing scope) */

let objB = {
 id: 20,
 name: "Paul",
 print () { // same as print: function() 
  setTimeout( function() {
    // invoked async, not bound to objB
    console.log(`[${this.id} -> ${this.name}]`);
  }, 1)
 }
};
objB.print(); // logs: [undefined -> undefined]'
objB = {
 id: 20,
 name: "Paul",
 print () { // same as print: function() 
  setTimeout( () => {
    // closes over bind to this from objB.print()
    console.log(`[${this.id} -> ${this.name}]`);
  }, 1)
 }
};
objB.print(); // logs: [20 -> Paul]

objB.print()의 경우 print() 메소드는 console.log( [$ {this.id}-> {this.name}] )을 (를) 비동기식으로 호출하는 함수로 정의됩니다. setTimeout의 콜백, 화살표 함수가 콜백으로 사용되었지만 콜백이 일반 함수로 정의되면 실패한 thisobjB (으)로 올바르게 해결되었습니다. setTimeout(()=>..)에 전달 된 화살표 => 함수가 this 위로 어휘 적으로 부모에서 닫 혔기 때문입니다. 그것을 정의한 objB.print()의 호출. 다른 말로하면, setTimeout(()==>...objB의 호출은 this 자체이기 때문에 화살표 => 함수는 thisobjB에 바인딩 된 objB.print()에 전달되었습니다.

Function.prototype.bind()을 사용하면 콜백을 올바른 this에 바인딩하여 일반 함수로 정의 된 콜백을 쉽게 만들 수 있습니다.

const objB = {
 id: 20,
 name: "Singh",
 print () { // same as print: function() 
  setTimeout( (function() {
    console.log(`[${this.id} -> ${this.name}]`);
  }).bind(this), 1)
 }
}
objB.print() // logs: [20 -> Singh]

그러나 화살표 함수는 함수 정의 시점에 this을 알고 바인딩되어야하는 비동기 콜백의 경우 오류가 발생하기 쉽고 편리합니다.

호출마다 변경해야하는 화살표 기능의 제한

언제든지 호출 할 때 this을 (를) 변경할 수있는 기능이 필요하며 화살표 기능을 사용할 수 없습니다.

/* this = global | Window (enclosing scope) */

function print() { 
   console.log(`[${this.id} -> {this.name}]`);
}
const obj1 = {
 id: 10,
 name: "Simar",
 print // same as print: print
};
obj.print(); // logs: [10 -> Simar]
const obj2 = {
 id: 20,
 name: "Paul",
};
printObj2 = obj2.bind(obj2);
printObj2(); // logs: [20 -> Paul]
print.call(obj2); // logs: [20 -> Paul]

위의 어느 것도 화살표 기능 const print = () => { console.log( [$ {this.id}-> {this.name}] );}에서 작동하지 않습니다. this은 (는) 변경할 수 없으며 this에 바인딩됩니다. 정의 된 범위 (전역/창). 이 모든 예에서 우리는 서로 다른 객체 (obj1obj2)를 사용하여 동일한 함수를 차례대로 호출했습니다. 둘 다 print() 함수가 선언 된 후에 만들어졌습니다.

이것들은 예를 들어 설명되었지만 실제 사례에 대해 더 생각해 봅시다. arrays에서 작동하는 것과 유사한 reduce() 메소드를 작성해야하는 경우 호출 컨텍스트에서 this을 (를) 추론해야하기 때문에 다시 람다로 정의 할 수 없습니다. 그것이 호출 된 배열

이러한 이유로 constructor 함수는 화살표 함수로 정의 할 수 없습니다. 선언시 생성자 함수에 대한 this을 설정할 수 없기 때문입니다. new 키워드를 사용하여 생성자 함수를 호출 할 때마다 새 객체가 생성되어 특정 호출에 바인딩됩니다.

또한 프레임 워크 또는 시스템이 동적 컨텍스트 this으로 나중에 호출 할 콜백 함수를 수락하면 화살표 함수를 다시 사용할 수 없으므로 this은 매 호출마다 변경해야 할 수 있습니다. 이 상황은 일반적으로 DOM 이벤트 핸들러로 발생합니다.

'use strict'
var button = document.getElementById('button');
button.addEventListener('click', function {
  // web-api invokes with this bound to current-target in DOM
  this.classList.toggle('on');
});
var button = document.getElementById('button');
button.addEventListener('click', () => {
  // TypeError; 'use strict' -> no global this
  this.classList.toggle('on');
});

이것은 Angular 2 +Vue.js와 같은 프레임 워크에서 템플릿 컴포넌트 바인딩 메소드가 호출에 대한 this과 같은 정규 함수/메소드가 될 것으로 예상하는 이유이기도합니다. 바인딩 함수의 프레임 워크에 의해 Angular는 Zone.js를 사용하여 뷰 템플릿 바인딩 함수를 호출하기 위해 비동기 컨텍스트를 관리합니다.

반면, React에서 컴포넌트의 메소드를 <input onChange={this.handleOnchange} />와 같은 이벤트 핸들러로 전달하려면 모든 호출에 대해 handleOnchanage = (event)=> {this.props.onInputChange(event.target.value);}을 화살표 함수로 정의해야합니다. 렌더링 된 DOM 요소에 대해 JSX를 생성 한 구성 요소의 동일한 인스턴스가 되길 원합니다.


이 기사는 내 Medium 발행물에서도 볼 수 있습니다. 당신이 artile을 좋아하거나 의견이나 제안이 있으면 clap 남겨주세요 comments 중간 .

1
Simar Singh

나는이 쓰레드에서 내가 처음 쓴 나의 모든 대답 에 여전히 맞서있다. 그러나 코드 스타일에 대한 제 의견은 그때 이후로 발전 했으므로 마지막 질문에 대한이 새로운 질문에 대한 답변을 얻었습니다.

어휘 this

나의 마지막 대답에서, 나는 의도적으로이 언어에 관한 기본적인 믿음을 피했다. 왜냐하면 그것이 내가 만든 논증과 직접적으로 관련이 없기 때문이다. 그럼에도 불구하고, 이것이 명시 적으로 언급되지 않고서, 왜 많은 사람들이 화살표를 사용하지 말라는 제 권고를 왜 그렇게 유용하게 생각하는지 이해할 수 있습니다.

내 믿음은 이것입니다 : 우리는 처음에는 this을 사용해서는 안됩니다. 따라서 누군가가 의도적으로 코드에서 this을 사용하는 것을 피하면 화살표의 "어휘 this"기능은 가치가 거의 없습니다. 또한 this이 나쁜 것이라는 전제하에, this에 대한 화살표 처리는 "좋은 일"이 아니라 다른 나쁜 언어 기능에 대한 손상 제어의 형태입니다.

나는이 문제가 어떤 사람들에게는 일어나지 않는다는 것을 알지만, 누구에게도 마찬가지입니다. 그들은 this이 파일 당 백 번 나타나는 코드베이스에서 변함없이 스스로를 발견해야합니다. 그리고 약간의 (또는 많은) 손상 통제가 있습니다. 모든 합리적인 사람이 희망 할 수 있습니다. 화살표는 좋은 상황이 될 수 있습니다.

화살표가있는 것보다 this을 사용하는 코드를 작성하는 것이 더 쉽지만 화살표를 사용하는 규칙은 매우 복잡합니다 (현재 스레드 참조). 따라서, 당신이 요청한 바와 같이, 지침은 "명확한"것이 아니며 "일관된 것"도 아닙니다. 프로그래머가 화살표의 모호성에 대해 알고 있다고해도 어쨌든 그들은 어리 석다 고 생각합니다. 어휘 this의 가치가 그들을 우회하기 때문입니다.

이 모든 것은 다음과 같은 실현에 대한 머리말입니다. this을 사용하지 않으면 화살표가 일반적으로 발생시키는 this에 대한 모호성은 부적합합니다. 이 상황에서 화살표는 중립이됩니다.

간결한 구문

첫 번째 답변을 작성했을 때 모범 사례에 대한 철저한 준수가 더 완벽한 코드를 생성 할 수 있다는 것을 의미한다면 지불해야 할 가치있는 가격이라고 생각했습니다. 그러나 결국에는 간결함이 코드 품질을 향상시킬 수있는 추상화의 한 형태로도 작용할 수 있음을 깨닫게되었습니다. 때로는 모범 사례에서 벗어나는 것을 정당화하기에 충분할 때가 있습니다.

다른 말로하면 : 빌어 먹을, 원 - 라이너 기능도 필요합니다!

가이드 라인

this- 중립 화살표 함수의 가능성, 그리고 추구가 가치가있는, 나는 다음과 같은보다 관대 한 지침을 제공합니다.

ES6의 함수 표기법 가이드 라인 :

  • this을 사용하지 마십시오.
  • 이름으로 호출 할 함수에 함수 선언을 사용하십시오 (호이스트로 인해).
  • 콜백에는 화살표 함수를 사용하십시오 (간결 해지기 때문에).
0
Jackson