조아마시

쓸모 있는 상세페이지 만들기

웹개발/javascript

[자바스크립트] this 키워드 / call(), apply(), bind() 메서드

joamashi 2024. 7. 21. 17:45

this 키워드

  • 객체 내부에서 사용될 때: this는 해당 객체 자신을 가리킵니다.
  • 함수 내에서 사용될 때: this의 값은 함수가 어떻게 호출되었는지에 따라 달라집니다.
    • 메서드로 호출될 때: this는 호출한 객체를 가리킵니다.
    • 일반 함수로 호출될 때: thiswindow 객체를 가리킵니다.
    • 생성자 함수로 호출될 때: this는 생성된 객체를 가리킵니다.
    • 이벤트 핸들러 내에서 사용될 때: this는 이벤트가 발생한 요소를 가리킵니다.

call() 메서드

  • 함수의 call() 메서드는 함수를 호출하고, 첫 번째 인자로 this를 지정할 수 있습니다.
  • 두 번째 인자부터는 함수에 전달할 인자를 나열합니다.
function greet(greeting, name) {
  console.log(`${greeting}, ${this.name}!`);
}

const person = { name: 'Alice' };
greet.call(person, 'Hello'); // Output: Hello, Alice!

apply() 메서드

  • 함수의 apply() 메서드는 call()과 비슷하지만, 두 번째 인자로 인자 배열을 전달합니다.
function greet(greeting, name) {
  console.log(`${greeting}, ${this.name}!`);
}

const person = { name: 'Bob' };
greet.apply(person, ['Good morning']); // Output: Good morning, Bob!

bind() 메서드

  • 함수의 bind() 메서드는 새로운 함수를 생성하고, this를 미리 바인딩합니다.
  • 생성된 함수는 나중에 호출할 수 있습니다.
function greet(greeting, name) {
  console.log(`${greeting}, ${this.name}!`);
}

const person = { name: 'Charlie' };
const boundGreet = greet.bind(person, 'Hi');
boundGreet('Charlie'); // Output: Hi, Charlie!

요약

  • this 키워드는 함수의 호출 컨텍스트에 따라 다르게 동작합니다.
  • call()apply()는 함수를 즉시 호출하고, this를 지정하는 데 사용됩니다.
  • bind()는 새로운 함수를 생성하고, this를 미리 바인딩합니다.

사용 시기

  • call()apply()는 함수를 즉시 실행해야 할 때 사용합니다.
  • bind()는 함수를 나중에 호출하거나, 다른 함수에 전달할 때 사용합니다.

주의 사항

  • this 키워드는 자바스크립트에서 혼란스러울 수 있으므로 주의깊게 사용해야 합니다.
  • call(), apply(), bind() 메서드는 함수의 유연성을 높여주지만, 오용하면 코드 가독성이 저하될 수 있습니다.
  • bind()로 생성된 함수는 성능상 약간의 오버헤드가 있을 수 있습니다.

추가 정보

  • call(), apply(), bind() 메서드는 함수의 프로토타입에 정의되어 있습니다.
  • bind() 메서드는 커링(currying)을 구현하는 데 사용될 수 있습니다.
  • this 키워드의 동작을 이해하려면 자바스크립트의 실행 컨텍스트와 호이스팅에 대한 지식도 필요합니다.

예제

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

Person.prototype.greet = function(greeting) {
  console.log(`${greeting}, ${this.name}!`);
};

const person1 = new Person('Alice');
const person2 = new Person('Bob');

person1.greet('Hello'); // Output: Hello, Alice!

const greetFunc = person2.greet.bind(person1);
greetFunc('Hi'); // Output: Hi, Alice!
  • 일반함수 호출한 객체의 this 사용 / 일반함수 this는 함수 선언 위치와 상관없이 함수 호출에 따라 this가 결정
  • 화살표함수를 감싸는 상위 스코프의 this 사용 / 화살표 함수 this는 함수 호출과 상관없이 함수 선언에 따라 this가 결정

this

console.log(this) // Window {}

function _this1 () { // 일반함수 호출한 객체(window)의 this 사용
  console.log(this) // Window {}
}
_this1() // window._this1()

// console.log(this) // Window {}
const _this2 = () => { // 화살표함수를 감싸는 상위 스코프의 this 사용
  console.log(this) // Window {}
}
_this2()

const obj = {
  name: 'A',
	
  // 일반함수
  _this0 () { // 일반함수 호출한 객체(obj)의 this 사용 
    console.log('_this0 : ', this) // { name: 'A' ... }
  },
  // 화살표함수
  _this1: () => { // 화살표함수를 감싸는 '상위 스코프'(window)의 this 사용
    console.log('_this1 : ', this) // Window {}
  },
  // _this1: this, // Window {}
 
  // setTimeout
  _this2 () { // 일반함수 호출한 객체(obj)의 this 사용 
    setTimeout(function () { // setTimeout(console.log(this)) // Window
      console.log('_this2 : ', this) // Window {}
    })
  },
  _this3 () { // 일반함수 호출한 객체(obj)의 this 사용
    console.log(this) // { name: 'A' ... }
    setTimeout(() => { // 화살표함수를 감싸는 '상위 스코프'의 this 사용
      console.log('_this3 : ', this) // { name: 'A' ... }
    })
  },
	 
  // 중첩함수
  _this4 () { // 일반함수 호출한 객체(obj)의 this 사용
    function inner () { // 일반함수 호출한 객체(window)의 this 사용
      console.log('_this4 : ', this) // Window {}
    }
    inner() // window.inner()
  },
  _this5 () { // 일반함수 호출한 객체(obj)의 this 사용
    console.log(this) // { name: 'A' ... }
    const inner = () => { // 화살표함수를 감싸는 '상위 스코프'의 this 사용
      console.log('_this5 : ', this) // { name: 'A' ... }
    }
    inner() // window.inner()
  },
	
  // bind()
  _this () { // 일반함수 호출한 객체(obj)의 this 사용
    const inner = function () { // bind 메서드 인자 값 this 사용
      console.log('_this6 : ', this) // { name: 'B' }
    }.bind({ name: 'B' })
    inner() // window.inner()
  }
}

obj._this0()
obj._this1()
obj._this2()
obj._this3()
obj._this4()
obj._this5() 
obj._this6()

const obj2 = {
  name: 'D',
  main: obj._this0  // 일반함수 호출한 객체(obj2)의 this 사용
}
obj2.main() // { name: 'D' ... }
  • this는 일반적으로 객체지향 프로그래밍에서 객체 자기 자신을 가리키는 키워드로 사용
  • this는 현재 실행 중인 함수의 컨텍스트를 나타냅니다.
  • 함수를 호출할 때 'this'는 호출한 객체에 따라 동적으로 결정된다.
  • 전역 범위에서 호출하면 'this'는 전역 객체를 가리키고, 객체의 메서드로 호출하면 해당 객체를 참조한다
  • 전역 범위에서 함수를 호출하면 'this'는 전역 객체를 가리킨다.
  • 현재 위치(객체) 안에서 사용 가능한 메서드나 프로퍼티를 확인할 때 많이 사용 - console.log(this)
  • 함수 실행시 결정된다. - 어디에 속해 있는지
  • 함수 호출 방식에 따라 this에 바인딩할 어떤 객체가 동적으로 결정 - 함수 호출 시 this가 바라보는 객체 결정 (동적으로)
  • "use strict"; // 엄격 모드 일 때 주의
  • this를 사용하지 않을 때만 화살표 함수 사용코드가 실행될 때 생성되는 환경을 나타냅니다.전역 컨텍스트는 코드의 최상위에서 시작되고, 함수가 호출될 때마다 새로운 실행 컨텍스트가 생성된다.실행 컨텍스트는 변수의 스코프, 호이스팅(Hoisting), 'this'의 값 등을 관리하여 코드 실행을 가능하게 한다.
  • 이 컨텍스트는 스택(Stack)에 쌓이며, 함수가 실행을 마치면 해당 컨텍스트가 스택에서 제거된다.
  • 이 컨텍스트 안에는 변수, 함수 선언, 'this'의 값 등이 저장된다.

실행 컨텍스트

코드가 실행될 때 생성되는 환경을 나타냅니다. 이 컨텍스트 안에는 변수, 함수 선언, 'this'의 값 등이 저장된다. 전역 컨텍스트는 코드의 최상위에서 시작되고, 함수가 호출될 때마다 새로운 실행 컨텍스트가 생성된다. 이 컨텍스트는 스택(Stack)에 쌓이며, 함수가 실행을 마치면 해당 컨텍스트가 스택에서 제거된다. 실행 컨텍스트는 변수의 스코프, 호이스팅(Hoisting), 'this'의 값 등을 관리하여 코드 실행을 가능하게 한다.

자바스크립트 실행 컨텍스트는 코드 실행 환경을 의미하며, 다음과 같은 요소들로 구성됩니다.

  • 변수: 실행 컨텍스트 내에서 선언된 변수에 대한 정보를 저장합니다. 변수 이름, 값, 속성 등이 포함됩니다.
  • 함수: 실행 컨텍스트 내에서 선언된 함수에 대한 정보를 저장합니다. 함수 이름, 코드, 매개변수, 반환 값 등이 포함됩니다.
  • this 객체: 실행 컨텍스트에서 활성화된 객체를 가리킵니다. 메서드 호출 시 this는 호출된 객체를 의미하며, 일반 함수 호출 시 this는 전역 객체를 의미합니다.
  • スコープ: 변수와 함수가 유효한 범위를 정의합니다. 자바스크립트에는 블록 스코ープ와 함수 스코ープ 두 가지가 존재합니다.

실행 컨텍스트 생성 과정:

  1. 스크립트 실행 시, 전역 실행 컨텍스트가 하나 생성됩니다.
  2. 함수가 호출될 때마다, 해당 함수에 대한 실행 컨텍스트가 새롭게 생성됩니다.
  3. 함수 실행이 종료되면, 해당 함수의 실행 컨텍스트가 해제됩니다.
  4. eval() 또는 new Function() 생성자를 사용하여 동적으로 코드를 실행할 때, 새로운 실행 컨텍스트가 생성됩니다.

실행 컨텍스트와 호이스팅:

호이스팅은 변수 선언이 실제 코드 위치와 관계없이 스크립트의 맨 위로 끌어올려지는 현상입니다. 하지만 호이스팅은 변수 선언만 끌어올리는 것이며, 변수 할당은 선언 위치에서 진행됩니다.

따라서 실행 컨텍스트가 생성되고 변수 선언이 수행될 때, 해당 변수는 아직 초기화되지 않은 상태입니다. 만약 선언된 변수를 선언 위치보다 앞서 사용하려고 하면 에러가 발생합니다.

실행 컨텍스트와 this:

this 키워드는 실행 컨텍스트에서 활성화된 객체를 가리킵니다. 함수 호출 시 this는 호출된 객체를 의미하며, 일반 함수 호출 시 this는 전역 객체를 의미합니다.

따라서 함수 내에서 this를 사용하여 객체 프로퍼티나 메서드에 접근하려면, 해당 함수가 어떤 객체에서 호출되었는지 고려해야 합니다.

실행 컨텍스트와 클로저:

클로저는 함수와 함께 함수가 선언된 당시의 실행 컨텍스트를 담은 객체입니다. 즉, 클로저 함수는 선언된 컨텍스트의 변수와 함수에 액세스할 수 있습니다.

클로저는 다음과 같은 상황에서 유용하게 사용됩니다.

  • 비동기 프로그래밍: setTimeout() 또는 setInterval()과 같은 비동기 함수 내에서 변수 값을 유지해야 할 때
  • 프라이빗 메서드 구현: 객체 내부에서만 사용되는 함수를 프라이빗하게 만들 때
  • 함수 콜백 설정: 이벤트 핸들러나 콜백 함수에 특정 컨텍스트를 전달해야 할 때

실행 컨텍스트 관련 주의 사항:

  • 서로 다른 실행 컨텍스트에서 선언된 변수는 서로 다른 변수로 간주됩니다.
  • 함수 내에서 this를 사용하려면, 해당 함수가 어떤 객체에서 호출되었는지 명확하게 파악해야 합니다.
  • 클로저를 사용하면 메모리 누수가 발생할 수 있으므로 주의해서 사용해야 합니다.

전역 - this

this // 전역객체 window 객체를 가리킴

this === window // true
this === global // true - nodejs

alert('window 객체 포함된 상태')
window.alert('window 생략가능')

var a = "a" // var로 선언 시 window 객체에 포함
window.a // "a"

var alert = "alert" // 기존 alert 경고창 "alert" 문자열 변수로 변환

function ab () {} // window 객체에 포함
window.ab // function ab () {}

// var 전역 객체로 사용할 때 유용. - jquery
// window.$
// $('body').hide() 

// 점(.) 연산자를 사용해서 메서드나 프로퍼티 접근함

// window 객체에 포함 X. 
// 블록 스코프이기 때문에 window 객체 내부의 블록에서 선언된 것으로 평가되어 전역 객체의 프로퍼티로 활용되기는 어렵다.
const c = "c" 
let l = "l"
window.c // undefined
window.l // undefined

함수 - this

// 함수 선언문

var a = 1

function func () {
  var a = 2

  console.log(this) // window
  console.log(this.a) // 1
  console.log(a) // 2

  function func1 () { // 중첩함수
    console.log(this) // window
  }
  func1()
}
func()

// 함수 표현식. 익명함수
// const func = function () {
// 	console.log(this) // window
// }
// func()
"use strict"; // 엄격 모드

var a = 1

function func () {
  var a = 2

  console.log(this) // undefined
  console.log(this.a) // Uncaught TypeError
}
func()
// 화살표 함수

var a = 1

const func = () => console.log(this) // window
func()
"use strict"; // 엄격 모드

var a = 1

const func = () => console.log(this) // window
func()

화살표 함수는 함수가 선언될 때의 문맥에서 this를 캡처하므로 bind 메서드를 사용하여 this를 변경할 필요가 없습니다. bind 메서드는 주로 일반 함수 선언 시에 this를 명시적으로 지정할 때 사용됩니다. 다만, 화살표 함수는 자체적으로 this를 갖지 않고, 선언된 곳의 스코프에서 this를 참조합니다. 따라서 bind 메서드를 사용하여 this를 변경하는 것은 화살표 함수에게는 적용되지 않습니다. 이는 화살표 함수가 lexical scoping을 따르기 때문입니다.

결론

  • 화살표 함수는 this가 없다.
  • 선언된 곳의 스코프에서 this를 참조 (렉시컬 스코프)

오브젝트 > 메서드 - this 화살표함수X

var a = 1

var obj = {
	a: 2, // 프로퍼티
  func: function () {
    console.log(this) // obj 객체 {a: 2, func: ƒ, func2: ƒ}
		console.log(this.a) // 2

		function func1 () { // 중첩함수
			console.log(this) // window
		}
		func1()
  },
	func2 () {} // function 생략가능
}
obj.func()
"use strict"; // 엄격 모드

var a = 1

var obj = {
	a: 2,
  func () {
    console.log(this) // obj 객체 {a: 1, func: ƒ, func2: ƒ}
		console.log(this.a) // 2

		function func1 () {
			console.log(this) // undefined
		}
		func1()
  },
}
obj.func()
({
  maxwidth : 600,
  maxheight : 400,
  gimmeMax : function () {
	  return this.maxwidth + "x" + this.maxheight
  },
  init : function () {
    console.log(this.gimmeMax())
  }
}).init()
var a = 1

const obj = {
	a: 2,
  func: () => {
    console.log(this) // window
		console.log(this.a) // 1

		function func1 () {
			console.log(this) // window
		}
		func1()
  },
	func2 () {
		const func3 = () => {
			console.log(this) // {a: 2, func: ƒ, func2: ƒ}
		}
		func3()
	}
}
obj.func()
obj.func2()
"use strict"; // 엄격 모드

var a = 1

const obj = {
	a: 1,
  func: () => {
    console.log(this) // window
		console.log(this.a) // 1

		function func1 () { // 중첩함수
			console.log(this) // undefined
		}
		func1()
  }
}
obj.func()

이벤트 - this 화살표함수X

// <button id="btn">버튼</button>

const btn = document.querySelector('#btn')
btn.addEventListener('click', function () {
  console.log(this) // <button id="btn">버튼</button>
	this.innerHTML = "THIS" // <button id="btn">THIS</button>
});
// <button id="btn">버튼</button>

const btn = document.querySelector('#btn')
btn.addEventListener('click', () => {
  console.log(this); // window
});

생성자 함수 - this

function Cat (name, age) {
  this.name = name
  this.age = age

	console.log(this) // {name: '쪼꼬', age: 5}

	// return 사용안함 X
}
console.log(typeof Cat) // 'function'

const choco = new Cat("쪼꼬", 5) // new 연산자 -> 클래스
choco // {name: '쪼꼬', age: 5}
choco.name // '쪼꼬'

console.log(typeof choco) // 'object'

// Object 생성
const choco = {}
choco.name = "쪼꼬"
choco.age = 5
console.log(choco) // {name: '쪼꼬', age: 5}

use strict > this

'use strict'

function _this1 () { // 일반함수
  console.log(this) 
}
_this1() // undefined

const _this2 = () => { // 화살표 함수
  console.log(this) 
}
_this2() // Window

call, apply

function _this () { 
  console.log(this) 
}

const obj3 = {
  list : [4, 5, 6]
}

_this.call(obj3) // { list: [4,5,6] }
_this.apply(obj3) // { list: [4,5,6] }

_bind()

function _bind (fn, context, ...arg) {
	// fn      : function add (a, b, c) { return a + b + c + this.age }
	// context : { age: 5 }
	// ...arg  : 1, 2
  return function (...arg2) { // 5
    return fn.apply(context, [...arg, ...arg2])
  }
}

const obj = { age: 5 }

function add (a, b, c) { // 1, 2, 5
  return a + b + c + this.age
}

const fun = _bind(add, obj, 1, 2)
fun(5) // 13
fun(10) // 18
fun(7) // 15
function _bind (fn, context, ...arg) {
	// fn      : function logTitle () { return this.title }
	// context : { title: '홍길동' }
	// ...arg  : 
  return function (...arg2) {
    return fn.apply(context, [...arg, ...arg2])
  }
}

const obj = { title: '홍길동' }

function logTitle () {
  return this.title
}

const fun = _bind(logTitle, obj)
fun() // 홍길동

class

class Member {
  numebr =3

  getName () {
    console.log(this) // Member { numebr: 3 }
    return '홍길동'
  }
}
const member = new Member()
member.getName() // 홍길동

 

call() 메서드 다양한 활용 예제

1. 객체 메서드 호출:

const person = {
  name: 'Alice',
  greet: function(greeting) {
    console.log(`${greeting}, ${this.name}!`);
  }
};

const stranger = { name: 'Bob' };

// person.greet() 메서드를 stranger 객체에서 호출
person.greet.call(stranger, 'Hello'); // Output: Hello, Bob!

2. 함수 프로토타입 메서드 호출:

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

Person.prototype.greet = function(greeting) {
  console.log(`${greeting}, ${this.name}!`);
};

const person1 = new Person('Alice');
const person2 = new Person('Bob');

// person2 객체의 greet 메서드를 person1 프로토타입으로 변경
person2.greet = person1.greet;

// person2.greet() 메서드를 호출
person2.greet('Hi'); // Output: Hi, Alice! (person1 프로토타입의 greet 메서드 실행)

3. 생성자 함수의 this 바인딩:

function Person(name) {
  // `this`를 사용하여 객체 생성
  this.name = name;

  // `call()`을 사용하여 `this`를 바인딩하고 새로운 객체 생성
  return new Person.prototype.constructor(name);
}

Person.prototype.constructor = Person; // 생성자 함수 재설정

const person1 = new Person('Alice');
console.log(person1.name); // Output: Alice

const person2 = Person.call({ name: 'Bob' });
console.log(person2.name); // Output: Bob

4. 내장 함수 호출:

const numbers = [1, 2, 3, 4, 5];

// `Array.prototype.sort()` 메서드를 `numbers` 배열에서 호출
numbers.sort.call(numbers, function(a, b) {
  return b - a; // 내림차순 정렬
});

console.log(numbers); // Output: [5, 4, 3, 2, 1]

5. 함수 콜백 설정:

const button = document.querySelector('button');

// `button.addEventListener()` 메서드를 사용하여 이벤트 핸들러 설정
button.addEventListener('click', function() {
  console.log('Button clicked!');
}, false);

// `call()`을 사용하여 이벤트 핸들러의 `this`를 특정 객체로 바인딩
button.addEventListener('click', function() {
  console.log(this.id); // this는 button 요소를 가리킴
}.bind(button), false);

6. 객체 프로퍼티 동적 변경:

const point = { x: 10, y: 20 };

// `point.distanceFromOrigin()` 메서드를 동적으로 생성
point.distanceFromOrigin = function() {
  return Math.sqrt(this.x * this.x + this.y * this.y);
};

// `call()`을 사용하여 `distanceFromOrigin()` 메서드를 호출
const distance = point.distanceFromOrigin.call(point);
console.log(distance); // Output: 22.360679774997896

7. 프라이빗 메서드 호출:

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

  greet() {
    console.log(`Hello, ${this.name}!`);
  }

  showAge() { // 프라이빗 메서드
    console.log(this.age);
  }
}

const person = new Person('Charlie');

// `showAge()` 메서드는 프라이빗이지만, `call()`을 사용하여 외부에서 호출 가능
person.showAge.call(person); // Output: undefined (프라이빗 프로퍼티 `age` 접근 불가)

apply() 메서드 다양한 활용 예제

1. 객체 메서드 호출:

const person = {
  name: 'Alice',
  greet: function(greeting) {
    console.log(`${greeting}, ${this.name}!`);
  }
};

const stranger = { name: 'Bob' };

// person.greet() 메서드를 stranger 객체에서 호출
person.greet.apply(stranger, ['Hello']); // Output: Hello, Bob!

2. 함수 프로토타입 메서드 호출:

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

Person.prototype.greet = function(greeting) {
  console.log(`${greeting}, ${this.name}!`);
};

const person1 = new Person('Alice');
const person2 = new Person('Bob');

// person2 객체의 greet 메서드를 person1 프로토타입으로 변경
person2.greet = person1.greet;

// person2.greet() 메서드를 호출
person2.greet(['Hi']); // Output: Hi, Alice! (person1 프로토타입의 greet 메서드 실행)

3. 생성자 함수의 this 바인딩:

function Person(name) {
  // `apply()`를 사용하여 `this`를 바인딩하고 새로운 객체 생성
  return new (Function.prototype.bind.apply(Person, [this, arguments]));
}

const person1 = new Person('Alice');
console.log(person1.name); // Output: Alice

const person2 = Person.apply({ name: 'Bob' });
console.log(person2.name); // Output: Bob

4. 내장 함수 호출:

const numbers = [5, 1, 4, 2, 3];

// `Array.prototype.sort()` 메서드를 `numbers` 배열에서 호출
numbers.sort.apply(numbers, [[function(a, b) {
  return b - a; // 내림차순 정렬
}]]);

console.log(numbers); // Output: [5, 4, 3, 2, 1]

5. 함수 인자 배열 전달:

function sum(a, b, c) {
  return a + b + c;
}

const numbers = [10, 20, 30];

// `sum()` 함수를 `numbers` 배열의 요소로 호출
const result = sum.apply(null, numbers);
console.log(result); // Output: 60

6. 객체 프로퍼티 동적 변경:

const point = { x: 10, y: 20 };

// `point.distanceFromOrigin()` 메서드를 동적으로 생성
point.distanceFromOrigin = function() {
  return Math.sqrt(this.x * this.x + this.y * this.y);
};

// `apply()`을 사용하여 `distanceFromOrigin()` 메서드를 호출
const distance = point.distanceFromOrigin.apply(point);
console.log(distance); // Output: 22.360679774997896

7. 프라이빗 메서드 호출:

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

  greet() {
    console.log(`Hello, ${this.name}!`);
  }

  showAge() { // 프라이빗 메서드
    console.log(this.age);
  }
}

const person = new Person('Charlie');

// `showAge()` 메서드는 프라이빗이지만, `apply()`을 사용하여 외부에서 호출 가능
person.showAge.apply(person); // Output: undefined (프라이빗 프로퍼티 `age` 접근 불가)

bind() 메서드 다양한 활용 예제

1. 객체 메서드 바인딩:

const person = {
  name: 'Alice',
  greet: function() {
    console.log(`Hello, ${this.name}!`);
  }
};

const boundGreet = person.greet.bind(person); // person 객체를 `this`로 바인딩

boundGreet(); // Output: Hello, Alice!

2. 이벤트 핸들러 바인딩:

const button = document.querySelector('button');

button.addEventListener('click', function() {
  console.log(this.id); // this는 button 요소를 가리킴
});

// button 요소의 ID를 출력하는 바인딩된 이벤트 핸들러 생성
const boundClickHandler = button.addEventListener.bind(button, function() {
  console.log(this.id);
});

// 이벤트 발생 시 바인딩된 핸들러 실행
boundClickHandler(); // Output: button

3. 생성자 함수 바인딩:

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

const PersonWithBoundGreet = Person.bind(null, 'Charlie'); // 생성자 함수 바인딩

const person1 = new PersonWithBoundGreet(); // 'Charlie' 이름으로 객체 생성
console.log(person1.name); // Output: Charlie

4. 함수 콜백 설정:

function forEach(array, callback) {
  for (let i = 0; i < array.length; i++) {
    callback.call(array[i], i, array[i]);
  }
}

const numbers = [1, 2, 3, 4, 5];

// `forEach()` 함수를 사용하여 배열 요소 출력
forEach(numbers, function(index, value) {
  console.log(index, value);
});

// `bind()`를 사용하여 콜백 함수의 `this`를 특정 객체로 바인딩
const boundForEach = forEach.bind(null, numbers);

// 바인딩된 `forEach()` 함수 사용
boundForEach(function(index, value) {
  console.log(this.name, index, value); // this는 numbers 배열을 가리킴
}, { name: 'Array' });

5. 커링 함수 생성:

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

// `multiply(2)`를 반환하는 바인딩된 함수 생성
const double = multiply.bind(null, 2);

// `double(3)`은 6을 반환
console.log(double(3));

6. 메서드 체이닝:

const numbers = [10, 20, 30, 40, 50];

// `numbers` 배열의 제곱을 계산하고 합계를 출력
const sumOfSquares = numbers
  .map(function(num) {
    return num * num;
  })
  .reduce(function(a, b) {
    return a + b;
  }, 0);

console.log(sumOfSquares); // Output: 300

7. 프라이빗 메서드 호출 (주의 필요):

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

  greet() {
    console.log(`Hello, ${this.name}!`);
  }

  showAge() { // 프라이빗 메서드
    console.log(this.age);
  }
}

const person = new Person('David');

// `showAge()` 메서드는 프라이빗이지만, `bind()`를 사용하여 외부에서 호출 가능 (주의!)
const boundShowAge = person.showAge.bind(person);
boundShowAge(); // Output: undefined (프라이빗 프로퍼티 `age` 접근 불가능)

demonstrateJavaScriptConcepts()

function demonstrateJavaScriptConcepts() {

  // this 키워드
  console.log("this 키워드 예시:", this); // 여기서 this는 전역 객체 (브라우저에서는 window)

  // 실행 컨텍스트 및 렉시컬 스코프
  let lexicalVar = "렉시컬 스코프 변수";
  function innerFunction() {
    // innerFunction은 외부 함수의 변수에 접근할 수 있습니다 (렉시컬 스코프)
    console.log("렉시컬 스코프 예시:", lexicalVar);
  }
  innerFunction();

  // 스코프 체인
  let outerVar = "외부 변수";
  function outerFunction() {
    let innerVar = "내부 변수";
    function innerFunction() {
      // 스코프 체인 예시: innerFunction은 innerVar 및 outerVar에 접근할 수 있습니다
      console.log("스코프 체인 예시:", innerVar, outerVar);
    }
    innerFunction();
  }
  outerFunction();

  // 고차 함수
  function higherOrderFunction(callback) {
    const data = "고차 함수 데이터";
    callback(data);
  }
  function logData(data) {
    console.log("고차 함수 예시:", data);
  }
  higherOrderFunction(logData);

  // 클로저
  function createCounter() {
    let count = 0;
    return function() {
      count += 1;
      console.log("클로저 예시 - 카운트:", count);
    };
  }
  const counter = createCounter();
  counter(); // 1
  counter(); // 2

	// call 메서드
  function showContext(a, b) {
    console.log("call 메서드 예시 - this:", this, "인자:", a, b);
  }
  const obj = {name: "객체"};
  showContext.call(obj, "a", "b"); // call 메서드를 사용하여 this를 obj로 설정하고, 인자를 전달
}
demonstrateJavaScriptConcepts();
728x90