Javascript this
자바스크립트의 함수는 호출될 때, 매개변수로 전달되는 인자값 이외에, arguments 객체와 this를 암묵적으로 전달 받는다.
1 2 3 4 5 6 7 8 9 10 function square (number ) { console .log(arguments ); console .log(this ); return number * number; } var result = square();
함수 호출 패턴과 this 바인딩
메소드 호출 패턴(Method Invocation Pattern)
함수 호출 패턴(Function Invocation Pattern)
생성자 호출 패턴(Constructor Invocation Pattern)
apply 호출 패턴(Apply Invocation Pattern)
1. 메소드 호출 패턴(Method Invocation Pattern)
this는 그 method를 소유한 객체를 가르킨다. 즉 그 method를 호출한 객체를 가르킨다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 var obj1 = { name: 'Lee' , sayName: function ( ) { console .log(this .name); } } var obj2 = { name: 'Kim' } obj2.sayName = obj1.sayName; obj1.sayName(); obj2.sayName();
프로토타입 객체도 메소드를 가질 수 있다. 프로토타입 객체 메소드 내부에서 사용된 this도 일반 메소드 방식과 마찬가지로 해당 메소드를 호출한 객체에 바인딩된다.
즉 함수를 부른 객체에 적용이된다.
Q. obj2에는 sayName함수가없는데 이것은 obj2__proto__, obj1__proto__가 같아서인가요?? 아니면 무슨이유인가요?
A. this의 특징인 method를 호출한 객체를 가르키기 때문이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function Person (name ) { this .name = name; } Person.prototype.getName = function ( ) { return this .name; } var me = new Person('Lee' );console .log(me.getName()); Person.prototype.name = 'Kim' ; console .log(Person.prototype.getName());
2. 함수 호출 패턴(Function Invocation Pattern)
전역객체(Global Object)는 모든 객체의 유일한 최상위 객체를 의미하며 일반적으로 Browser-side에서는 window, Server-side(Node.js)에서는 global 객체를 의미한다.
기본적으로 this는 전역객체(Global object)에 바인딩된다. 전역함수는 물론이고 심지어 내부함수의 경우도 this는 외부함수가 아닌 전역객체에 바인딩된다.
method 내부의 내부함수의 this도 전역을 가르킨다. (설계상 결점)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 var value = 1 ;var obj = { value: 100 , foo: function ( ) { console .log("foo's this: " , this ); console .log("foo's this.value: " , this .value); function bar ( ) { console .log("bar's this: " , this ); console .log("bar's this.value: " , this .value); } bar(); } }; obj.foo();
콜백함수의 경우에도 this는 전역객체에 바인딩된다.(설계적 결함)
1 2 3 4 5 6 7 8 9 10 11 12 13 var value = 1 ;var obj = { value: 100 , foo: function ( ) { setTimeout (function ( ) { console .log("callback's this: " , this ); console .log("callback's this.value: " , this .value); }, 100 ); } }; obj.foo();
this의 전역객체 회피방법
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 var value = 1 ;var obj = { value: 100 , foo: function ( ) { var that = this ; console .log("foo's this: " , this ); console .log("foo's this.value: " , this .value); function bar ( ) { console .log("bar's this: " , this ); console .log("bar's this.value: " , this .value); console .log("bar's that: " , that); console .log("bar's that.value: " , that.value); } bar(); } };
var that = this; 여기서 this는 obj이다 즉 that은 obj이므로 이상해지면 that을 써준다.
3. 생성자 호출 패턴(Constructor Invocation Pattern)
생성자 함수를 생성시 new를 쓴것과 안쓴것은 차이가 있다.
3.1 생성자 함수 동작 방식
빈 객체 생성 및 this 바인딩
this를 통한 프로퍼티 생성
생성된 객체 반환
생성자 함수 내부에는 return을 안써주는 것이다. 암묵적으로 해주기에 안써주는것이다 쓰면 코드가 꼬인다.
this는 생성자 함수가 생성할 객체를 가르킨다.
1 2 3 4 5 6 7 8 var Person = function (name ) { this .name = name; } var me = new Person('Lee' );console .log(me.name);
3.3 생성자 함수에 new 연산자를 붙이지 않고 호출할 경우 1 2 3 4 5 6 7 8 9 10 11 var Person = function (name ) { this .name = name; }; var me = Person('Lee' );console .log(me); console .log(window .name);
me에다가 값이 들어가지 않고 window에 값이 들어간다.
방어코드를 작성해서 new를 빼먹는 경우를 방어한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function A (arg ) { if (!(this instanceof arguments .callee)) { return new arguments .callee(arg); } this .value = arg ? arg : 0 ; } var a = new A(100 );var b = A(10 );console .log(a.value);console .log(b.value);
argument.callee는 생성자의 이름이 나타난다.
4. apply 호출 패턴(Apply Invocation Pattern)
유사배열 : 유사배열은 배열이아닌 객체라서 배열처럼 불를수있지만 원래의 순서가 보장이 안된다.
func.apply는 func의 양부인 Function.prototype 객체의 메소드이다.
[]는 option을 나타낸다.
1 2 3 4 func.apply(thisArg, [argsArray])
1 2 3 4 5 6 7 8 9 10 var Person = function (name ) { this .name = name; }; var foo = {};Person.apply(foo, ['name' ]); console .log(foo);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function convertArgsToArray ( ) { console .log(arguments ); var arr = Array .prototype.slice.apply(arguments ); console .log(arr); return arr; } convertArgsToArray(1 , 2 , 3 );
argumetns 객체는 배열 객체는 아닌데 slice를 쓰게해달라고 해주는게 apply이다 그래서 배열객체의.prototype에 .apply를 통해서 argument가 쓰게해준다.
1 2 3 Person.apply(foo, [1 , 2 , 3 ]); Person.call(foo, 1 , 2 , 3 );
call과 apply의 차이는 배열이냐 , 이냐의 차이이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function Person (name ) { this .name = name; } Person.prototype.doSomething = function (callback ) { if (typeof callback == 'function' ) { callback(); } }; function foo ( ) { console .log(this .name); } var p = new Person('Lee' );p.doSomething(foo);
callback함수에다가 직접적으로 call에 this를 명시해줘서 일반함수가 callback을 사용할 수 있게 해준다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function Person (name ) { this .name = name; } Person.prototype.doSomething = function (callback ) { if (typeof callback == 'function' ) { callback.call(this ); } }; function foo ( ) { console .log(this .name); } var p = new Person('Lee' );p.doSomething(foo);