개발 공부/JavaScript

this

무우너대갈 2022. 7. 14. 11:00
반응형
  1. 상황에 따라 달라지는 this
    • 전역 공간에서의 this
      • 전역 객체를 가리킴
      • 브라우저 환경 : window
      • Node.js 환경 : global
        • 자바스크립트의 모든 변수는 특정 객체의 프로퍼티로 동작
          • 특정 객체
          • : 실행 컨텍스트의 L.E(LexicalEnvironment)
      var a = 1;
      window.b = 2;
      console.log(a, window.a, this.a); //1 1 1
      console.log(b, window.b, this.b); //1 1 1
      
      • 삭제의 경우
        • 전역 변수로 선언 시 해당 프로퍼티의 configurable 속성 (변경 및 삭제 가능성)을 false로 정의함
      • : 처음부터 전역 객체의 프로퍼티로 할당한 경우 삭제 O(전역 변수로 선언시 삭제 X)
      var a = 1;
      delete window.a; //false
      console.log(a, window.a, this.a); //1 1 1
      
      var b = 2;
      delete b; //false
      console.log(b, window.b, this.b); //2 2 2
      
      window.c = 3;
      delete window.c; //true
      console.log(c, window.c, this.c); //오류
      
      window.d = 3;
      delete d; //true
      console.log(d, window.d, this.d); //오류
      
    • 메서드로 호출 시 메서드 내부에서의 this
      • 함수와 메서드
        • 독립성 차이
        • 함수 : 자체적으로 독립적 기능 수행
        • 메서드 : 자신을 호출한 대상 객체에 관한 동작 수행
        var func = function(x) {
        	console.log(this, x);
        };
        
        //함수 호출
        func(1) //Window{...}1
        
        var obj={
        	method : func
        };
        
        //메소드 호출
        obj.method(2); //{method:f}2
        obj['method'](2); //{method:f}2
        
        • 함수 앞 점(.) 또는 대괄호([])로 구분하기
      • 메서드 내부에서의 this
        • 어떤 함수를 메서드로 호출 시 호출 주체는 함수명 앞의 객체
        var obj = {
        	methodA : function() {console.log(this);},
        	inner : {
        		methodB : function() {console.log(this);}
        	}
        };
        
        obj.methodA(); //{methodA : f, inner:{...}} ( === obj)
        obj['methodA'];  //{methodA : f, inner:{...}} ( === obj)
        
        obj.inner.methodB();  //methodB:f}(=== obj.inner)
        obj.inner.['methodB']();  //methodB:f}(=== obj.inner)
        obj.['inner'].methodB();  //methodB:f}(=== obj.inner)
        obj['inner']['method'];()   //methodB:f}(=== obj.inner)
        
    • 함수로 호출 시 함수 내부에서의 this
      • 함수로서 지정할 경우 this 지정 X
      • 메서드 내부함수에서 this 우회
        • 변수 활용
        var obj = {
        	outer : function() {
        		console.log(this);  // (1) {outer:f}
        		var innerFunc1 = function () {
        			console.log(this);  // (2) Window{ ... }
        		};
        		innerFunc1();
        
        		// 변수명 : _this, that, _,,,
        		var self = this;
        		var innerFunc2 = function() {
        			console.log(self); // (3) {outer:f}
        		};
        		innerFunc2();
        	}
        };
        obj.outer();
        
      • this를 바인딩하지 않는 함수
        • ES6의 화살표 함수(arrow function)
          • 실행 컨텍스트 생성 시 this 바인딩 과정 빠짐
            var obj = {
            	outer:function() [
            		console.log(this); // (1) {outer:f}
            		var innerFunc = () ]> {
            			console.log(this); // (2) {outer : f}
            		};
            		innerFunc();
            	}
            };
            obj.outer();
            
          • ⇒ 상위 스코프의 this 그대로 활용
        • call, apply 메서드를 활용하여 명시적 this 지정 가능
    • 콜백 함수 호출 시 해당 함수 내부의 this
      • 콜백 함수의 제어권을 가지는 함수(메서드)가 결정
      • 정의하지 않은 경우 전역객체 바라봄
    • 생성자* 함수 내부의 this
      • 특정 함수가 생성자 함수로 호출된 경우
      • ⇒ 내부에서의 this는 새로 만들 인스턴스** 자신이 됨
      var Cat = function(name, age) {
      	this.bark = '야옹';
      	this.name = name;
      	this.age = age;
      };
      var choco = new Cat('초코', 7);
      var nabi = new Cat('나비', 5);
      console.log(choco, nabi);
      
      /* 결과
      Cat { bark : '야옹', name : '초코', age : 7}
      Cat { bark : '야옹', name : '나비', age : 5}
      */
      
    • 함수를 호출할 때 결정됨
    • : this는 실행 컨텍스트가 생성될 때 결정됨
  2. 명시적으로 this를 바인딩***하는 방법
    • call 메서드
      • 메서드의 호출 주체인 함수를 즉시 실행하도록 함
      var func = function(a, b, c) {
      	console.log(this, a, b, c);
      };
      
      func(1, 2, 3_; //Window{ ... } 1 2 3
      func.call({x:1}, 4, 5, 6); //{x:1} 4 5 6
      
      var obj = {
      	a : 1,
      	method : function(x,y) {
      		console.log(this.a, x, y);
      	}
      };
      
      obj.method(2,3);  //1 2 3
      obj.method.call({a:4}, 4, 5); // 4 4 5
    • Function.prototype.call(thisArg[, arg1[, arg2[, ...]]])
    • apply 메서드
      • call 메서드와 기능적으로 완전히 동일
      • 두번째 인자를 배열로 받아 요소들을 호출할 함수의 매개변수로 지정
      var func = function(a b, c) {
      	console.log(this, a, b, c);
      };
      func.apply({x:1}, [4, 5, 6]); //{x:1} 4 5 6
      
      var obj = {
      	a:1, 
      	method : function(x,y) {
      		console.log(this.a, x, y);
      	}
      };
      obj.method.apply({a:4}, [5, 6]); //4 5 6
      
    • Function.prototype.apply(thisArg[, argsArray]);
    • call.apply 메서드의 활용
      • 유사배열객체(array-like object)에 배열 메서드 적용
        • 객체에는 배열 메서드 직접 적용 불가
        • 배열 메서드 차용 가능한 경우
          • 키가 0 또는 양의 정수인 프로퍼티가 존재
          • length 프로퍼티의 값이 0 또는 양의 정수인 객체
        var obj = {
        	0 : 'a',
        	1 : 'b',
        	2 : 'c',
        	length : 3
        };
        Array.prototype.push.call(obj, 'd');
        console.log(obj); //{0:'a', 1:'b', 2:'c', 3:
        'd', length : 4}
        
        var arr = Array.prototype.,slice.call(obj);
        console.log(arr); //['a', 'b', 'c', 'd']
        
      • arguments객체, NodeList에 적용하기
      • function a () { var argv = Array.prototype.slice.call(arguments); argv.forEach(function (arg) { console.log(arg); }); } a(1, 2, 3); document.body.innerHTML = '<div>a</div><div>b</div><div>c</div>'; var nodeList = document.querySelectorAll('div'); var nodeArr = Array.prototype.slice.call(nodeList); nodeArr.forEach(function (node) { console.log(node); });
      • 문자열에 적용하기
        • length 프로퍼티가 읽기 전용이기에 원본 문자열에 변경 가하는 메서드(push, pop, shift, unshift, splice 등)의 경우 에러
        • 대상이 반드시 배열이어야 하는 경우(concat) 제대로 된 결과 X
        var str = 'abc def';
        
        Array.prtotype.push.call(str, ', pushed string');
        //에러 발생
        
        Array.prtotype.concat.call(str, 'string'); //[String {"abc def"}, "string"]
        
        Array.prtotype.every.call(str, funtion(char) {return char !== '';}); //false
        
        Array.prtotype.some.call(str, funtion(char) {return char === '';}); //true
        
        var newArr = Array.prototype.map.call(str, function(char) {return char + '!';});
        console.log(newArr);  //['a!', 'b!', 'c!', '!', 'd!', 'e!', 'f!']
        
        var newStr = Array.prototype.reduce.apply(str, [
        	function(string, char, i )) {return string + char + i;},
        	''
        ]);
        console.log(newStr); //"a-b1c2 3d4e5f6"
        
      • 배열 전환 메서드 Array.from
        • ES6에 도입됨
        • 유사배열객체 또는 순회 가능한 모든 종류의 데이터 타입을 배열로 전환
        var obj = {
        	0 : 'a',
        	1 : 'b',
        	2 : 'c',
        	length : 3
        };
        var arr = Array.from(obj);
        console.log(arr); // ['a', 'b', 'c']
        
      • 생성자 내부에서 다른 생성자 호출
        • 다른 생성자와 공통된 내용 있을 시 call, apply를 활용해 반복 줄이기 가능
        function Person(name, gender) {
        	this.name = name;
        	this.gender = gender;
        }
        function Student(name, gender, school) {
        	Person.call(this, name, gender);
        	this.school = shool;
        }
        function Employee(name, gender, company) {
        	Person.apply(this, [name, gender]);
        	this.company = company;
        }
        var by = new Student('보영', 'female', '단국대');
        var jn = new Employee('재난', 'male', '구글');
        
      • 여러 인수를 묶어 하나의 배열로 전달 - apply
//기존 코드
var numbers = [10, 20, 3, 16, 45];
var max = min = numbers[0];
numbers.forEach(function(number) {
	if(number > max) {
		max = number;
	}
	if(number < min) {
		min = number;
	}
});
console.log(max, min); //45 3

//apply 적용
var max = Math.max.apply(null, numbers);
var min = Math.min.apply(null, numbers);
console.log(max, min); //45 3

//펼치기 연산자(spread operator) 적용
var max = Math.max(...numbers);
var min = Math.min(...numbers);
console.log(max, min); //45 3
    • bind 메서드
      • ES5 추가됨
      • call과 비슷,즉시 호출 X, 넘겨받은 this 및 인수들을 바탕으로 새로운 함수를 반환함
      • 새로운 함수 호출 시 인수를 넘기면 기존 bind 메서드를 호출할 때 전달했던 인수들의 뒤에 이어 등록
      • 목적
        • 함수에 this를 미리 적용
        • 부분 적용 함수 구현
      var func = function(a, b, c,d) {
      	console.log(this, a, b, c, d);
      };
      func(1, 2, 3, 4); //Window{...} 1 2 3 4
      
      var bindFunc1 = func.bind({x : 1});
      bindFunc1(5, 6, 7, 8); //{x:1} 5 6 7 8
      
      var bindFunc2 = func.bind({x:1}, 4, 5,);
      bindFunc2(6, 7); // {x:1} 4 5 6 7
      bindFunc2(8, 9); // {x:1} 4 5 6 7
      
      • name 프로퍼티
        • bound 접두어
        • : bind 메서드를 통해 새로 만든 함수의 name 프로퍼티에 붙음
        var func = function (a, b, c, d) {
        	console.log(this, a, b, c, d);
        };
        var bindFunc = func.bind({x:1}, 4, 5);
        console.log(func.name); // func
        console.log(bindFunc.name); // bound func
        
      • 상위 컨텍스트의 this를 내부함수나 콜백 함수에 전달하기
        • apply, call, bind 메서드 이용
        //call 메서드
        {
        	var obj = {
        		outer:function() {
        			console.log(this);
        			var innerFunc = function() {
        				console.log(this);
        			};
        			innerFunc.call(this);
        		}
        	};
        	obj.outer();
        }
        
        //bind 메서드
        {
        	var obj = {
        			outer : function() {
        				console.log(this);
        				var innerFunc = function() {
        					console.log(this);
        				}.bind(this);
        				innerFunc();
        			}
        		};
        	obj.outer();
        }
        //bind메서드 내부함수에 this 전달
        {
        	var obj = {
        		logThis : function() {
        			console.log(this);
        		},
        		logThisLater1 : function() {
        			setTimeout(this.logThis, 500);
        		},
        		logThisLater2 : function() {
        			setTimeout(this.logThis.bind(this, 1000);
        		}
        	};
        	obj.logThisLater1(); //Window{...}
        	obj.logThisLater2(); //obj { logThis : f, ...}
        }
        
    • 화살표 함수의 예외사항
      • ES6 도입
      • 실행 컨텍스트 생성 시 this 바인딩하는 과정 제외 ⇒ 함수 내부에 this 존재 X
      • 접근하고자 할 시 스코프체인상 가장 가까운 this에 접근
      var obj = {
      	outer : function() {
      		console.log(this);
      		var innerFunc = () => {
      			console.log(this);
      		};
      		innerFunc();
      	}
      };
      obj.outer();
      
    • 별도의 인자로 this를 받는 경우(콜백 함수 내에서의 this)
      • 콜뱀 함수를 인자로 받는 메서드 중 일부는 추가로 this로 지정할 객체(thisArg)를 인자로 지정할 수 있는 경우 존재
        • 해당 메서드의 thisArg 값을 지정시 콜백 함수 내부에서 this 값을 원하는대로 변경
        • 배열 메서드에 많이 포진 ⇒ Set, Map 등
      	var report = {
      	sum : 0,
      	count : 0,
      	add.function() {
      		var args = Array.prototype.slice.call(arguments);
      		arg.forEach(function(entry) {
      			this.sum += entry;
      			++this.count;
      		}, this); // 해당 this가 콜백 함수 내부의 this로 바인딩 됨
      	},
      	average : function() {
      		return this.sum / this.count;
      	}
      };
      retport.add(60, 85, 95); //add 메서드의 this(report)를 그대로 가리킴
      console.log(report.sum, report.count, retport.average()); // 240 3 80
      
      • 이외의 thisArg를 인자로 받는 메서드
        • forEach()
        • map()
        • filter()
        • some()
        • every()
        • find()
        • findIndex()
        • flatMap()
        • from()

*생성자 : 구체적인 인스턴스를 만들기 위한 틀

**인스턴스 : 어떤 집합에 대해서 개별적인 요소

***바인딩 : 특정객체에서 실행되게끔 고정시키는 그런 역할

  • arguments 객체
  • : 함수에 전달된 인수에 해당하는 Array 형태의 객체
반응형

'개발 공부 > JavaScript' 카테고리의 다른 글

클로저  (0) 2022.07.16
콜백  (0) 2022.07.15
실행 컨텍스트  (0) 2022.07.13
데이터 타입  (0) 2022.07.12
ES11에 추가된 문법!  (0) 2022.01.28