-
[JS] 함수 - 유효범위, 콜백, 클로저, arguments, 호출생활코딩/ 2020. 9. 3. 18:35
[강의 출처] opentutorials.org/course/743/6583
유효범위(Scope):
-변수를 선언할 때 'var'는 해당 변수가 지역변수(local variable)임을 의미
-한번 지역변수로 선언한 이후에는 해당 범위(지역) 내에서는 지역변수로 사용됨
-자바스크립트는 함수 단위로 유효범위를 제공(if, for, while등의 블록 단위에는 제공 X)
-유효범위는 선언 맥락에 따라 유효범위가 결정되는 Lexical Scoping 방식을 따른다
var vscope = 'global'; // 전역변수 function fscope(){ var vscope = 'local'; // 지역변수 document.write(vscope); }; function fscope2(){ document.write(vscope); vscope = 'local'; }; function fscope3(){ document.write(vscope); }; fscope(); // 'local': 가깝게 선언된 변수(지역변수) 우선 이용 fscope2(); // 'global': 지역변수가 없기 때문에 전역변수 호출 fscope3(); // 'local'; fscope2()에서 vscope 재정의 했기 때문에 현재 vscope 값은 'local'
유효범위 관련하여 발생할 수 있는 이슈
-예상치못한 다른 맥락에서 변수가 변경되어 무한루프 등의 상황 발생 가능
function a (){ i = 0; }; for(var i = 0; i < 5; i++){ a(); document.write(i); }; /* 무한루프 * for문은 외부에 있기 때문에 for문의 i는 전역변수로 선언됨 * 함수 a()는 (var가 없기 때문에) 전역변수 i의 값을 0으로 재할당함 * 계속 i = 0 상태이므로 루프가 완료되지 않음 */
전역변수를 사용해야 할 때 중복을 피하려면
-전역변수를 생성 후 나머지 변수를 해당 변수의 프로퍼티 형태 생성하거나
-변수가 필요한 경우 익명함수 내에 선언해서 전역변수를 만들지 않기
(function(){ var MYAPP = {}; /* 전역변수 하나를 생성해서 */ MYAPP.calculator = { /* 나머지 변수를 그 변수의 소속으로 넣어주거나 */ 'left': null, 'right': null } MAPP.coordinate = { 'left': null, 'right': null } MYAPP.calculator.left = 10; MYAPP.calculator.right = 20; function sum(){ return MYAPP.calculator.left + MYAPP.calculator.right; } document.write(sum)); }()) /* 아예 해당 변수를 익명함수 내에 선언해서 전역변수를 만들지 않거나 */
JS는 Static Soping/Lexical Scoping (↔Dynamic Scoping)을 따르기 때문에
함수를 호출한 위치는 해당 함수가 참조하는 지역변수에 영향을 주지 않는다 [참고]
var i = 5; function a(){ var i = 10; b(); } function b(){ document.write(i); // 정의될 때의 i는 var i = 5이므로 } a(); // 5: 사용시점에 따라 영향을 받지 않는다(Lexical Scoping)
값으로서의 함수와 콜백
-JS에서 함수는 객체로 처리되므로, 다른 객체나 배열에 포함되거나, 다른 함수의 인자나 리턴값으로 전달될 수 있다.
-이런 특성을 first-class citizen or first-class object, first-class entity, first-class value라고 부른다.
-콜백(callback): 함수를 호출하는 작업을 콜백이라고 하고, 그렇게 호출한 함수를 콜백함수라고 한다.
function cal(mode){ var funcs = { 'plus': function(left, right){return left + right}, 'minus': function(left, right){return left - rigth} } return funcs[mode]; } console.log(cal('plus')(2,1)); // 3 /* cal('plus')는 funcs[mode]를 리턴하므로, * cal 내부의 프로퍼티인 funcs에 mode로 입력된 'plus'를 전달하여 * funcs['plus']가 실행되고, 인자로 (2,1)이 전달된다 */
-콜백은 비동기처리에 유용하게 사용된다. 자바스크립트의 경우 Ajax를 주로 이용한다.
- 비동기처리(Asynchronous): To-do처럼, 특정 연산을 바로 진행하지 않고 모아뒀다가 여건이 될 때 처리하는 방식
클로저(Closure)
-내부함수가 외부함수의 맥락(Context: 지역변수 등) 에 접근가능한 특성
-내부함수란 함수 내에 선언된 함수를 뜻함. 이때 내부함수를 갖고 있는 함수를 외부함수라고 부름
function outter(){ // 외부함수 var word = 'hello world'; function inner(){ // 내부함수 alert(word); } inner(); } outter(); /* 특정한 함수 내에서만 사용되는 함수가 있을 경우, * 따로 떨어뜨려놓으면 군집성이 떨어지므로 내부함수 형태로 선언하기도 함 */ /* 위 함수의 경우 변수 word는 inner()의 입장에서는 전역변수이기 때문에 * word가 outter()의 지역변수이더라도 inner() 안에서 접근이 가능함 */
-특이하게 외부함수가 종료되더라도 내부함수가 외부함수의 맥락에 접근 가능한데, 이런 특성을 클로저라고 부름
function outter(){ var word = 'hello world'; return function(){ alert(word); } } inner = outter(); inner(); // 'hello world'라고 정상 출력됨
-클로저를 통해 아래와 같은 (python class 스타일) 객체 구현이 가능
function bread(element){ return { get_element: function(){ // 함수 bread는 종료되었으나 return element; // get_element는 bread의 맥락에 접근가능 }, set_element: function(_element){ element = _element // 함수 set_element는 bread의 지역변수 element를 변경함 } } } console.log(cookie.get_element()); // 'Cheese' console.log(scone.get_element()); // 'Cheese' scone.set_element('Chocolate'); console.log(cookie.get_element()); // 'Cheese' console.log(scone.get_element()); // 'Chocolate' /* 외부함수인 bread는 return 시점에서 종료되었으므로 * 변수 element에 대한 접근은 메소드 get_element, set_element를 통해서만 가능하고, * 따라서 데이터 접근 채널이 한정되므로 안전해짐 */
-클로저 관련하여 발생할 수 있는 이슈
var arr = [], arr2 = [] for(var i = 0; i < 5; i++){ arr[i] = function(){ // 이 맥락에서 i는 외부변수에서 정의된 지역변수가 아니기 때문에 console.log(i); // 이 구문 이후로도 값이 변할 수 있음 }, arr2[i] = function(id){ return function(){ // function(id)는 function()를 리턴하는 시점에서 종료되어 return id; // id는 해당 시점의 i값을 가리키는 외부변수로 고정됨 } }(i); } // 만약 여기에서 i = 7;처럼 i값을 재할당한다면 arr[index]()는 7을 리턴함 for(var index in arr){ console.log(arr[index]()); // 5 5 5 5 5 console.log(arr2[index]()); // 0 1 2 3 4 }
arguments
-함수의 매개변수(parameter)를 정의하지 않고 사용하거나, 인자의 수가 정의한 것과 달라도 에러가 나지는 않음
-함수 내부에서 arguments라는 객체를 통해 인자에 접근 가능
-함수.length -> 함수 정의시에 입력한 인자의 개수
-arguments.length -> 호출시 입력받은 인자의 개수
function sum(){ var _sum = 0; for(var i = 0; i < arguments.length; i++){ document.write(i+' : '+arguments[i]+'<br />'); _sum += arguments[i]; } return _sum; } document.write('result : ' + sum(1, 2, 3, 4, 5)); function one(arg1){ console.log( 'one.length: ', one.length, 'arguments.length: ', arguments.length ); } one('val1', 'va12'); // one.length : 1 arguments.length: 2
함수를 호출하는 다른 방법
-함수는 function 객체의 인스턴스이므로, function의 apply 메소드로도 호출이 가능
o1 = {val1:1, val2:2, val3:3} o2 = {v1:10, v2:50, v3:100, v4:25} function sum(){ var _sum = 0; for(name in this){ // 이 경우 this의 맥락은 호출할 때 정해짐 _sum += this[name]; } return _sum; } console.log(sum.apply(o1)) // 6 console.log(sum.apply(o2)) // 185 /* apply 메소드의 첫번째 인자에 입력된 o1, 혹은 o2는 * var this = o1; 처럼 정의된 것과 같은 효과, * 함수가 마치 o1.sum, o2.sum처럼 적용된 것과 같은 효과를 냄 */
'생활코딩 > ' 카테고리의 다른 글
[JS] 탬플릿 리터럴(`를 이용한 표현식) (0) 2020.09.09 [JS] 객체 지향 - 표준내장객체와 확장, 데이터 타입, 참조 (0) 2020.09.07 [JS] 객체 지향 - 생성자와 new, 전역객체, this, 상속, prototype (0) 2020.09.05 [JS] 개요 - 함수, 배열, 객체, 모듈, Reference 참조, 정규식 (0) 2020.09.01 [JS] 개요 - 숫자와 문자열, 변수, 비교연산자, 조건문, 반복문 (0) 2020.08.31