자바스크립트 6

hee_hee_·2022년 12월 22일
0

딩..딩..

목록 보기
11/15
post-thumbnail

1. 자바스크립트 함수가 실행되는 과정

1) 어떤 코드도 없는 경우

어떤 코드가 없어도 세가지 변수를 초기화함.

  • this
  • 변수들
  • Scope chain

이때 this 는 코드가 실행되는 시점의 환경을 가리키는데
어떤 코드도 없는 경우의 this 는 window 를 가리키게 됨.
window 객체는 최상위 스코프.
변수들은 아무것도 선언된 게 없으므로 빈 객체{}를 나타냄
scope chain 또한 최상위이므로 연결된 스코프가 없어서 빈객체[]가 됨.

  • 정리
    자바스크립트 엔진은 코드가 없어도 실행 환경 (실행컨텍스트)를 초기화 함.
    스코프는 코드가 현재 실행되는 환경, 맥락을 의미한다.
    this 포인터, 스코프에 저장된 변수들, 스코프 체인 등이 환경에 포함됨.
    this의 경우 글로벌 스코프에서는 window를 가리킴.


2) 특정 함수가 존재하고 함수가 실행되는 경우

function A () {
	var a=1
    var b=2
    function B(n1,n2){
    	return n1+n2
	}
    return B(a,b)
}

A()

처음에는 최상위 컨텍스트 생성.

this : window
변수들 : a:function
scope chain : []

A가 실행되면 함수 컨텍스트 생성.

this : undefined => strict mode로 실행되면 저 값이 나옴
변수들 : a:1 b:2 B:function
scope chain : [global]

다음 B 함수가 호출되어 실행되면 컨텍스트가 생김

this : undefined
변수들 : n1:1 n2:2
scope chain : [A.global]

제일 먼저 B 함수 실행되고 끝나면 A가 실행되고 끝나면 최상위 컨텍스트로 돌아가고 모든 코드가 종료되면 최상위 컨텍스트도 사라진다.

  • 정리
    함수 실행시 함수 스코프에 따라 환경이 만들어짐
    this, 함수 스코프의 변수들 그리고 스코프 체인이 형성된다.
    스코프 체인을 따라 글로벌 환경에 도달한다.


3) 객체가 있고 그 객체의 메소드를 호출하는 경우

let a = { name : 'lee',
		  method : function(n) {
          	return this.name.repeat(n)
          }
}

function A () {
	let n = 2
    return a.method(n)
}

A()

처음 최상위 컨텍스트 생성

this : window
변수들 : a:{...} A:function
scope chain : []

다음 A함수 실행컨텍스트가 생성

this : undefined
변수들 : n:2
scope chain: [global]

다음 a.method를 호출해서 실행컨텍스트 생김

this : o => 어떤 객체의 메서드는 this 가 객체를 가리킨다.
변수들 : n:2
scope chain : [A.global]

  • 정리
    객체의 메서드의 경우, 메서드 환경의 this 는 해당 객체를 가리킨다.
    하지만 this가 가리키는 것은 환경에 따라 변할 수 있다.



함수 일급 객체 성질

  • 함수를 다른 함수의 인자로 넘기면 다른 함수 내부에서 그 함수를 호출할 수 있다
  • 함수 안에 함수를 만들고 외부에서 사용하려면 return 해준다.
  • 함수 실행이 끝나도 내부 변수를 유지할 수 있다.






2. 실행 컨텍스트 (Execution context)

실행 컨텍스트 혹은 실행 맥락은, 자바스크립트 코드가 실행되는 환경.
코드에서 참조하는 변수, 객체(함수 포함), this 등에 대한 레퍼런스가 있다.
실행 컨텍스트는 전역에서 시작해, 함수가 호출될 때 스택에 쌓이게 된다.
실행 => 전역 컨텍스트 => 함수1 => 함수2=> 함수3 로 쌓이고
실행이 끝나면 가장 마지막에 쌓인 스택부터 사라져 코드가 전부 종료되면 전역 컨텍스트까지 사라짐.



1) 실행 컨텍스트 스택

let a =1;
function f1(){
   let b = 2;
   function print(n) {console.log (n)}
   function f2(){
        let c = 3
        print(a + b + c )
   }
   f2()
}
f1()

첫번째로 전역 실행 컨텍스트가 쌓이고
거기에는 변수a 와 console f1 함수가 있음

두번째로 f1을 호출했을때 컨텍스트가 생기고
거기에는 변수 b와 print함수 f2함수가 있음

세번째로 f2 함수가 실행
거기에 변수 c가 있고 print가 호출되는데 여기서 a는 전역 컨텍스트에서 찾아오고 b는 f1함수 내부에서 찾아오고 c는 f2에서 값을 참조.
이것은 스코프 체인을 따라 찾아오는 것.

마지막으로 print 함수 실행
내부에 v 있고 콘솔 객체의 로그 함수 참조.

순서 역순으로 마무리되면 빠져 사라짐.



2) 전역 실행 컨텍스트, 함수 실행 컨텍스트

자바스크립트가 실행될 때 전역 실행 컨텍스트가 만들어지고
함수가 실행될 때 함수 실행 컨텍스트가 만들어진다.



3) 렉시컬 환경

식별자와 식별자에 연결된 값을 저장하는 자료구조.

let foo = "riri";

function bar() {
	let title = "name";
    return title;
}

이때 전역에서 생성되는 렉시컬 환경에는 foo와 bar 두개의 식별자와 식별자에 연결된 값이 저장된다.
foo 에는 riri 가 들어있지만,
bar 에는 스코프 체인으로 bar 와 연결되어 있음.
bar 함수의 렉시컬 환경에서는 title 이라는 1개의 식별자와 연결된 값이 저장되어있고 전역 렉시컬 환경에 스코프 체인으로 연결되어 있음.
=> 전역에는 bar 라는 정보를 가지고 있음






3. this 가 가리키는 것

1) dynamic binding

함수가 호출되는 상황

  1. 함수 호출 - 함수를 직접 호출
    foo()
  2. 메서드 호출 - 객체의 메서드를 호출 이때 this는 객체를 가리키게 된다.
    a.method()
  3. 생성자 호출 - 생성자 함수를 호출. new 키워드 사용
    function Foo{
         this.name = 'name'
    }
    let a = new Foo()
  4. 간접 호출 - call, apply 등으로 함수를 간접 호출. 함수 객체의 메서드.
    f.call(null,a)

이 외에도 콜백 함수 호출이 있음.

  • 콜백 함수 : 특정 동작 이후 불려지는 함수. 보통 다른 함수의 인자로 보내지는 함수.

    function foo(){
         console.log("hi")
    }
    
    function bar(name, cb) {
    	 console.log("name : ", name)
         cb(name)
    }
    
    bar("riri",foo)
    
    =>
    name :  riri
    hi

    + setTimeout도 콜백함수 사용하는 함수.

이처럼 다양한 환경에서 함수가 호출되는데 호출되는 환경에 따라 this는 동적으로 세팅이 됨.
이것을 동적 바인딩(Dynamic binding) 이라 함.

bind, apply, call 등으로 this가 가리키는 것을 조작할 수 있음.

  • 화살표 함수로 호출 시엔 this 는 생성된 환경을 가리키도록 고정되고
    일반함수로 호술 시 this는 호출된 환경을 가리키지만 동적으로 바뀔 수 있다. => 객체로 호출 시 객체가 this 로 할당됨.
    setTimeout으로 함수 실행 환경을 바꿀 수 있다.

2) this를 조작하는 경우

bind, apply, call 등의 함수로 조작 가능.
setTimeout은 함수 호출과는 다른 콜백 호출.

let a = {
	title = "love",
    printTitle: function() {
    	console.log(this.title);
    },
};

a.printTitle();    => love
setTimeout(a.printTitle,1000);    => undefined
setTimeout(a.printTitle.bind(a),2000);    => love

위와 같이 bind 함수를 사용해 this가 a를 가리키도록 할 수 있음




함수에서의 this!

1) 일반 함수에서의 this

일반 함수에서 this는 window를 가리킨다.

function foo() {
	const title = "javascript";
    console.log(this)
}

foo() => window

따라서 foo함수 내부에 있는 title에 접근할 수 없다.

2) 생성자 함수에서의 this

생성자 함수 내부의 this 는 new 키워드를 통해 앞으로 만들어질 인스턴스 객체를 가리킨다.

function Person(name) {
	this.name = name;
    this.getName = function {
    	console.log ("getName this : " + this)
        return this.name;
    };
    
    console.log("Person this : " + this)
}

const bar = new Person("riri");
bar.getName()

=>
person this : [object Object]
getName this : [object Object]

함수를 객체로 생성하는 방법에만 this가 생성하는 함수 내부를 가리킨다.

3) 객체에서의 this

객체에서의 this는 메소드를 호출한 객체를 가리킨다.

const a = {
	name : "riri",
    foo : function () {
    	console.log(this)
    }
}

a.foo()

=>{ name: 'riri', foo: [Function: foo] }

객체 안 함수 내부 함수에서의 this 는 동적 바인딩으로 인해 전역 객체를 가리킨다.

const a = {
	name : "riri",
    foo : function () {
    	return function(){
        	console.log(this)
        }
    }
}

a.foo()()

=>Object [global] {...}

4) this 고정

function a () {
    this.fo = function () {
         console.log("a this : ", this)
         return function () {console.log("fo return this :" , this)}
    }
}

const b = new a ()

b.fo()()


=>  a this :  a { fo: [Function (anonymous)] }
    fo return this : <ref *1> Object [global] {

함수 내부에서 함수를 만들고 그 안에서 this를 출력하면 global을 가리키게 된다.
이것을 화살표 함수로 바꾸면 함수를 가리키게 만들 수 있다.

function a () {
     this.fo = function () {
          console.log("a this : ", this)
          return () => {console.log ("a return this : ",this)}
     }
}

const b = new a ()

b.fo()()

=>  a this :  a { fo: [Function (anonymous)] }
    a return this :  a { fo: [Function (anonymous)] }

call, apply, bind 로도 this를 고정할 수 있다.

function a () {
    this.fo = function () {
         console.log("a this : ", this)
         return function () {
            console.log("fo return this :" , this)
        };
    };
}

const b = new a ()


b.fo().call(b, null)
b.fo().apply(b, null)
b.fo().bind(b)();


=> 
    a this :  a { fo: [Function (anonymous)] }
    fo return this : a { fo: [Function (anonymous)] }

    a this :  a { fo: [Function (anonymous)] }
    fo return this : a { fo: [Function (anonymous)] }

    a this :  a { fo: [Function (anonymous)] }
    fo return this : a { fo: [Function (anonymous)] }






4. 화살표 함수와 일반 함수의 this

1) 화살표 함수와 일반함수

화살표 함수의 this => 호출된 함수를 둘러싼 실행 컨텍스트 가리킴
일반함수 this => 새롭게 생성된 실행 컨텍스트 가리킴

화살표 함수는 화살표 함수가 선언될 때의 this 가리키며 call, apply, bind 로 값으 바꾸려 해도 바뀌지 않는다.

일반 함수는 새롭게 생성된 컨텍스트를 가리키며 안에 정의된 this는 call, apply, bind 로 바꿀 수 있다.

const a = {
    func(){
        console.log("context : ", this)
        let b1 = function () {
            console.log("b1 this : " , this)
        }
        let b2 = () => {
            console.log("b2 this : " , this)
        }
        b1()
        b2()
    },
};

a.func()

=>
context :  { func: [Function: func] }
b1 this :  <ref *1> Object [global] {...}
b2 this :  { func: [Function: func] }

위 예시에서 b1은 일반함수로, b2는 화살표 함수로 선언이 되어 있다.

func 함수 안에서의 this 는 a객체를 가리키며

f1이 실행되면 global을 가리킨다.
=> f1이 실행되면서 새로운 컨텍스트가 생성되고 바인딩 된 컨텍스트가 없기 때문.

f2는 함수 컨텍스트가 생성될 때 this가 그 함수를 호출하는 환경인 a 를 가리키게 된다.

profile
딩코딩코딩

0개의 댓글