수색…


비고

범위는 변수가 존재하고 동일한 범위의 다른 코드에서 액세스 할 수있는 컨텍스트입니다. JavaScript는 함수형 프로그래밍 언어로 주로 사용될 수 있기 때문에 런타임에 버그 및 예기치 않은 동작을 방지하는 데 도움이되므로 변수 및 함수의 범위를 아는 것이 중요합니다.

var와 let의 차이점

(참고 : let 을 사용하는 모든 예제는 const 에도 유효합니다)

var 는 모든 버전의 JavaScript에서 사용할 수 있으며 letconst 는 ECMAScript 6의 일부이며 일부 최신 브라우저에서만 사용할 수 있습니다 .

var 는 선언 된 시점에 따라 포함 함수 또는 전역 공간으로 범위가 지정됩니다.

var x = 4; // global scope

function DoThings() {
    var x = 7; // function scope
    console.log(x);
}

console.log(x); // >> 4
DoThings();     // >> 7
console.log(x); // >> 4

if 명령문 및 모든 유사한 블록 구문을 "이스케이프 (escape)" if 것을 의미합니다.

var x = 4;
if (true) {
    var x = 7;
}
console.log(x); // >> 7

for (var i = 0; i < 4; i++) {
    var j = 10;
}
console.log(i); // >> 4
console.log(j); // >> 10

비교해 let .

let x = 4;

if (true) {
    let x = 7;
    console.log(x); // >> 7
}

console.log(x); // >> 4

for (let i = 0; i < 4; i++) {
    let j = 10;
}
console.log(i); // >> "ReferenceError: i is not defined"
console.log(j); // >> "ReferenceError: j is not defined"

ijfor 루프에서만 선언되므로 외부에서 선언되지 않습니다.

다른 몇 가지 중요한 차이점이 있습니다.

전역 변수 선언

(함수 및 블록 외부의) 최상위 범위에서 var 선언은 요소를 전역 개체에 넣습니다. let 하지 않습니다 :

var x = 4;
let y = 7;

console.log(this.x); // >> 4
console.log(this.y); // >> undefined

재 선언

var 사용하여 변수를 두 번 선언해도 오류는 발생하지 않습니다 (단 한번 선언하는 것과 동일 함).

var x = 4;
var x = 7;

let 사용하면 오류가 발생합니다.

let x = 4;
let x = 7;

TypeError : 식별자 x 가 이미 선언되었습니다.

yvar 로 선언 될 때도 마찬가지입니다.

var y = 4;
let y = 7;

TypeError : 식별자 y 가 이미 선언되었습니다.

그러나 let으로 선언 된 변수는 중첩 된 블록에서 재사용 (재 선언되지 않음) 될 수 있습니다.

let i = 5;    
{
   let i = 6;
   console.log(i); // >> 6
}
console.log(i); // >> 5

블록 내에서 외부 i 액세스 할 수 있지만 내부 블록에 i 에 대한 let 선언이있는 경우 외부 i 액세스 할 수 없으며 두 번째 선언 전에 사용하면 ReferenceError 가 throw됩니다.

let i = 5;
{
    i = 6;  // outer i is unavailable within the Temporal Dead Zone
    let i;
}

ReferenceError : i가 정의되지 않았습니다.

호이 스팅

varlet 선언 된 변수는 게양 됩니다. 차이는 선언 변수이다 var 가 자동 (할당됩니다 이후, 자신의 할당하기 전에 참조 할 수있는 undefined 값 등), 그러나 let -그렇지 특별히 변수를 필요로 호출되기 전에 선언 할 수 있습니다 :

console.log(x); // >> undefined
console.log(y); // >> "ReferenceError: `y` is not defined"
//OR >> "ReferenceError: can't access lexical declaration `y` before initialization"
var x = 4;
let y = 7;

블록의 시작과 let 또는 const 선언 사이의 영역을 Temporal Dead Zone 이라고하며,이 영역의 변수에 대한 ReferenceErrorReferenceError 시킵니다. 이것은 변수가 선언되기 전에 할당 된 경우에도 발생합니다.

y=7; // >> "ReferenceError: `y` is not defined"
let y;

비 엄격 모드에서는 선언없이 변수에 값을 할당하면 자동으로 변수가 전역 범위에 선언됩니다 . 이 경우 전역 범위에서 y 가 자동으로 선언되는 대신 let 은 변수 이름 ( y )을 예약하고 선언 / 초기화 된 행 앞에 변수에 대한 액세스 나 할당을 허용하지 않습니다.

마감

함수가 선언되면, 선언의 맥락에서 변수의 범위에 포획된다. 예를 들어, 아래의 코드에서, 변수 x 외 범위 값으로 결합되고, 다음의 참조 x 컨텍스트에서 포착 bar :

var x = 4; // declaration in outer scope

function bar() {
    console.log(x); // outer scope is captured on declaration
}

bar(); // prints 4 to console

샘플 출력 : 4

"캡처"범위의이 개념은 외부 범위가 종료 된 후에도 외부 범위의 변수를 사용하고 수정할 수 있기 때문에 흥미 롭습니다. 예를 들어, 다음을 고려하십시오.

function foo() {
    var x = 4; // declaration in outer scope

    function bar() {
        console.log(x); // outer scope is captured on declaration
    }

    return bar;
    
    // x goes out of scope after foo returns
}

var barWithX = foo();
barWithX(); // we can still access x

샘플 출력 : 4

위의 예제에서 foo 가 호출되면 함수 bar 에 컨텍스트가 캡처됩니다. 따라서 반환 된 후에도 bar 는 여전히 변수 x 액세스하고 수정할 수 있습니다. 다른 함수에서 컨텍스트를 캡처 한 함수 foo클로저 라고합니다.

개인 데이터

이렇게하면 특정 함수 나 함수 집합에만 표시되는 "개인"변수 정의와 같은 흥미로운 작업을 수행 할 수 있습니다. 고안된 (그러나 인기있는) 예 :

function makeCounter() {
    var counter = 0;

    return {
        value: function () {
            return counter;
        },
        increment: function () {
            counter++;
        }
    };
}

var a = makeCounter();
var b = makeCounter();

a.increment();

console.log(a.value());
console.log(b.value());

샘플 출력 :

1
0

makeCounter() 가 호출되면 해당 함수의 컨텍스트 스냅 샷이 저장됩니다. makeCounter() 내의 모든 코드는 실행시 해당 스냅 샷을 사용합니다. makeCounter() 두 번 호출하면 counter 의 사본이있는 두 개의 서로 다른 스냅 샷을 만들 수 있습니다.

즉시 호출되는 함수 식 (IIFE)

클로저는 또한 즉시 호출 된 함수 표현식을 사용하여 전역 네임 스페이스 오염을 방지하는 데 사용됩니다.

즉시 호출 된 함수 표현식 (또는 더 직관적으로 자체 실행 익명 함수 )은 본질적으로 선언 직후에 호출되는 클로저입니다. IIFE의 일반적인 아이디어는 IIFE 내의 코드에서만 액세스 할 수있는 별도의 컨텍스트를 만드는 부작용을 호출하는 것입니다.

우리가 $ jQuery 를 참조 할 수 있기를 원한다고 가정 해보자. IIFE를 사용하지 않고 간단한 방법을 고려하십시오.

var $ = jQuery;
// we've just polluted the global namespace by assigning window.$ to jQuery

다음 예제에서는 IIFE를 사용하여 $ 가 클로저에 의해 생성 된 컨텍스트에서만 jQuery 연결되도록합니다.

(function ($) {
    // $ is assigned to jQuery here
})(jQuery);
// but window.$ binding doesn't exist, so no pollution

클로저에 대한 자세한 내용은 Stackoverflow에 대한 정식 답변을 참조하십시오.

호이 스팅

호이 스팅 무엇입니까?

호이 스팅 은 모든 변수 및 함수 선언을 해당 범위의 맨 위로 이동시키는 메커니즘입니다. 그러나 변수 할당은 원래 있던 위치에서 계속 발생합니다.

예를 들어 다음 코드를 생각해보십시오.

console.log(foo);  // → undefined
var foo = 42;
console.log(foo);  // → 42

위의 코드는 다음과 같습니다.

var foo;             // → Hoisted variable declaration
console.log(foo);    // → undefined
foo = 42;            // → variable assignment remains in the same place
console.log(foo);    // → 42

호이 스팅으로 인해 위의 undefined 것은 실행 결과로 not defined 것과 동일 not defined .

console.log(foo);    // → foo is not defined 

유사한 원칙이 기능에도 적용됩니다. 함수가 변수 (예 : 함수 표현식 )에 할당되면 대입이 동일한 위치에있는 동안 변수 선언이 게양됩니다. 다음 두 코드 스 니펫은 동일합니다.

console.log(foo(2, 3));     // → foo is not a function

var foo = function(a, b) {
    return a * b;
}
var foo;
console.log(foo(2, 3));     // → foo is not a function
foo = function(a, b) {
    return a * b;
}

함수 문을 선언 할 때 다른 시나리오가 발생합니다. 함수 선언문과는 달리 함수 선언은 해당 범위의 맨 위에 올립니다. 다음 코드를 살펴보십시오.

console.log(foo(2, 3));  // → 6
function foo(a, b) {
    return a * b;
}

위의 코드는 권상으로 인해 다음 코드 스 니펫과 동일합니다.

function foo(a, b) {
    return a * b;
}

console.log(foo(2, 3));  // → 6

여기에는 무엇이 무엇인지 그리고 어떤 것이 게양되지 않는지에 대한 몇 가지 예가 나와 있습니다.

// Valid code:
foo();

function foo() {}

// Invalid code:
bar();                     // → TypeError: bar is not a function
var bar = function () {};


// Valid code:
foo();
function foo() {
    bar();
}
function bar() {}


// Invalid code:
foo();
function foo() {
    bar();                // → TypeError: bar is not a function
}
var bar = function () {};


// (E) valid:
function foo() {
    bar();
}
var bar = function(){};
foo();

호이 스팅의 한계

변수 초기화는 호이스트가 될 수 없으며 간단한 JavaScript 호이스트 선언은 초기화가 아닙니다.

예 : 아래 스크립트는 서로 다른 출력을 제공합니다.

var x = 2; 
var y = 4; 
alert(x + y);

이것은 6의 출력을 줄 것입니다.하지만이 ...

var x = 2; 
alert(x + y);
var y = 4; 

그러면 NaN 출력이 나옵니다. y의 값을 초기화하기 때문에 JavaScript Hoisting이 발생하지 않으므로 y 값은 정의되지 않습니다. JavaScript는 y가 아직 선언되지 않았다고 간주합니다.

두 번째 예는 아래와 같습니다.

var x = 2; 
var y;
alert(x + y);
y = 4; 

그러면 NaN 출력이 나옵니다.

여기에 이미지 설명을 입력하십시오.

var 대신 let in 루프 사용 (클릭 핸들러 예제)

로드 된 각 loadedData 배열에 대해 버튼을 추가해야한다고 가정 해 loadedData (예 : 각 버튼은 데이터를 표시하는 슬라이더 여야합니다. 간단히하기 위해 메시지를 경고합니다). 다음과 같이 시도 할 수 있습니다.

for(var i = 0; i < loadedData.length; i++)
    jQuery("#container").append("<a class='button'>"+loadedData[i].label+"</a>")
        .children().last() // now let's attach a handler to the button which is a child
        .on("click",function() { alert(loadedData[i].content); });

하지만 경고 대신 각 버튼이

TypeError : loadedData [i]가 정의되지 않았습니다.

오류. 이것은 i 의 범위가 전역 범위 (또는 함수 범위)이고 루프 이후에 i == 3 이기 때문입니다. 우리가 필요로하는 것은 " i 의 상태를 기억하지 않는 것"입니다. 이 작업은 let 사용하여 수행 할 수 있습니다.

for(let i = 0; i < loadedData.length; i++)
    jQuery("#container").append("<a class='button'>"+loadedData[i].label+"</a>")
        .children().last() // now let's attach a handler to the button which is a child
        .on("click",function() { alert(loadedData[i].content); });

이 코드로 테스트 할 loadedData 의 예는 loadedData 과 같습니다.

    var loadedData = [
        { label:"apple",      content:"green and round" },
        { label:"blackberry", content:"small black or blue" },
        { label:"pineapple",  content:"weird stuff.. difficult to explain the shape" }
    ];

이것을 설명하기위한 바이올린

메소드 호출

함수를 객체의 메소드로 호출하면이 객체의 값 this 해당 객체가됩니다.

var obj = {
    name: "Foo",
    print: function () {
        console.log(this.name)
    }
}

obj의 메소드로서 print를 호출 할 수 있습니다. this obj가 될 것이다.

obj.print();

그러면 다음과 같이 기록됩니다.

익명 호출

익명 함수로 함수를 호출, this (전역 객체가 될 것 self 브라우저를).

function func() {
    return this;
}

func() === window; // true
5

에서 인 ECMAScript 5의 strict 모드 , this 될 것입니다 undefined 함수가 익명으로 불려 갔을 경우.

(function () {
    "use strict";
    func();
}())

그러면 출력됩니다.

undefined

생성자 호출

이 함수와 생성자로 호출 될 때 new 키워드 this 생성되는 객체의 값을 취

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

var obj = new Obj("Foo");

console.log(obj);

이것은 기록 할 것이다.

{name : "Foo"}

화살표 함수 호출

6

화살표 함수를 사용하는 경우 this 있어 둘러싸 실행 콘텍스트의 값을 얻어 this (즉 this 화살표의 기능은 어휘 범위보다 보통 동적 범위를 갖는다). 전역 코드 (함수에 속하지 않는 코드)에서는 전역 객체가됩니다. 그리고 여기에 설명 된 다른 방법 중 하나에서 화살표 표기법으로 선언 된 함수를 호출하더라도이 방법은 그대로 유지됩니다.

var globalThis = this; //"window" in a browser, or "global" in Node.js

var foo = (() => this);           

console.log(foo() === globalThis);          //true

var obj = { name: "Foo" };
console.log(foo.call(obj) === globalThis);  //true

방법을 참조하십시오 this 아니라 메소드가 호출 된 객체를 참조보다는 컨텍스트를 상속합니다.

var globalThis = this;

var obj = {
    withoutArrow: function() {
        return this;
    },
    withArrow: () => this
};

console.log(obj.withoutArrow() === obj);      //true
console.log(obj.withArrow() === globalThis);  //true

var fn = obj.withoutArrow; //no longer calling withoutArrow as a method
var fn2 = obj.withArrow;
console.log(fn() === globalThis);             //true
console.log(fn2() === globalThis);            //true

적용 및 호출 구문 및 호출.

applycall 모든 기능의 방법은 대한 사용자 정의 값을 제공 할 수 있도록 this .

function print() {
    console.log(this.toPrint);
}

print.apply({ toPrint: "Foo" }); // >> "Foo"
print.call({ toPrint: "Foo" }); // >> "Foo"

위에서 사용 된 두 호출의 구문이 동일하다는 것을 알 수 있습니다. 즉, 서명이 비슷합니다.

하지만 우리는 함수를 다루고 범위를 변경하기 때문에 사용법에 약간의 차이가 있습니다. 함수에 전달 된 원래 인수를 유지해야합니다. 모두 apply 하고 call 다음과 같이 대상 기능 지원 전달 인수를 :

function speak() {
    var sentences = Array.prototype.slice.call(arguments);
    console.log(this.name+": "+sentences);
}
var person = { name: "Sunny" };
speak.apply(person, ["I", "Code", "Startups"]); // >> "Sunny: I Code Startups"
speak.call(person, "I", "<3", "Javascript"); // >> "Sunny: I <3 Javascript"

apply 사용하면 Array 또는 arguments 객체 (배열과 같은)를 인수 목록으로 전달할 수 있지만 call 에서는 각 인수를 개별적으로 전달해야합니다.

이 두 가지 방법을 사용하면 ECMAScript의 기본 bind 버전을 구현하지 않아도 원래 기능에서 개체의 메서드로 항상 호출되는 함수를 만드는 등 원하는대로 멋지게 들어갈 수 있습니다.

function bind (func, obj) { 
    return function () {
        return func.apply(obj, Array.prototype.slice.call(arguments, 1));
    }
}

var obj = { name: "Foo" };

function print() {
    console.log(this.name);
}

printObj = bind(print, obj);

printObj();

이것은 기록 할 것이다.

"Foo"


bind 함수가 많이 있습니다.

  1. obj this 값으로 사용됩니다.
  2. 인수를 함수에 전달한다.
  3. 값을 돌려 준다.

바인드 된 호출

모든 함수의 bind 메소드를 사용하면 특정 객체에 엄격하게 바인드 된 컨텍스트로 해당 함수의 새 버전을 생성 할 수 있습니다. 함수를 객체의 메서드로 강제 호출하는 것이 특히 유용합니다.

var obj = { foo: 'bar' };

function foo() {
    return this.foo;
}

fooObj = foo.bind(obj);

fooObj();

그러면 다음과 같이 기록됩니다.



Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow