관리 메뉴

HAMA 블로그

잊을만할때 다시 훑어보는 Javascript 본문

Javascript

잊을만할때 다시 훑어보는 Javascript

[하마] 이승현 (wowlsh93@gmail.com) 2015. 5. 26. 10:42


어떻게 이렇게 어려운(직관적이지않은) 언어가 세상을 지배하게 됬는지 정말 불가사의하다.


함수 

함수생성법

객체생성법

함수 호이스팅

함수 프로퍼티 

함수 형태  

함수호출과 This 바인딩 

함수 리턴 

프로토타입 체이닝

실행 컨텍스트

스코프 체인 

클로저 

객체지향프로그래밍

함수형 프로그래밍




자바스크립트에서 함수


C/JAVA/C# 등에서 보통 함수는 특정기능을 제공하고 결과값을 얻는 행위 only     

                                                  VS

자바스크립트에서의  함수는 위에것 + 모듈화 처리 + 클로저 + 객체생성등등 JS의 근간이 되는 많은 기능이 추가되어있다. 


“자바스크립트 공부에서 가장 중요한것은  함수의 기능에 대한 기존 프레임에서 알을 깨고 나오는것” 그리고 this 에 대한 감각.


1. 함수안에 함수가 들어가서 c++/java 의  멤버함수 (js에서는 메소드) 역할을 할 뿐 아니라 

2.변수에 할당이 가능하고 매개변수로도 넘길수있고,리턴값 및 배열의 값으로 사용 

3.결정적으로 클래스 역할도 한다. 




함수 정의의 3가지 방법 

 

함수 선언문 

function add(x,y){

return x+y;

}

“반드시 함수명이 정의되어야 한다” 

“function 이라는 키워드를 명시적으로 사용한다”

“ 리턴값과 매개변수로 넘기는값에 변수 타입을 기술하지 않는다”


함수 표현식 (1)

var  add = function(x,y){

return x+y;};


var plus = add;

함수 선언문 방식과 거의 유사하다.

유일한 차이점은 함수이름이 선택사항, 보통 사용하지 않음. 

위의 add와 plus 는 동일한 값(익명함수) 을 참조한다. 

관례적으로 함수뒤에 ; 를 넣는다. 


함수 표현식 (2) 

var  add = function sum(x,y){

return x+y;};


console.log(sum(3,4));  

어떻게 될까? 



console.log(sum(3,4));   // 에러!!!


함수 표현식에서 사용된 함수이름은 외부코드에서 접근 불가능!! 
(내부에서는 가능)



Function() 생성자 함수를 통한 생성  

new Function(arg1,arg2,...argN, func body);

var add = new Function(‘x’,’y’, ‘return x+y’);
console.log(add(3,4)) // 7



함수선언문,함수표현식 둘다 내부적으로 이것을 이용한다.
일반적으로 자주 사용되지는 않는다.
이런게 있다는것만 알아두자.



함수 호이스팅 


add(2,3); // 5 <------- (1)

function add(x,y){  <-------- (2)
return x+y;}

add(3,4); // 7  <--------(3) 


(1) 시점에는 함수 (2) 가 정의되지도 않았는데 호출이 가능하다 . 
함수 선언문 형태로 정의한 함수의 유효범위는 코드의 맨 처음으로  끌어올려 Hoisting 진다..
따라서 함수 선언문보다는 함수 표현식을 사용하기를 강력히 권고한다.  (코드구조가 엉성해짐) 
 


함수도 객체다.   

function add(x,y){
return x+y;}

add.result = add(3,2);
add.status = ‘OK’; 


보통 객체처럼 result / status 같은 프로퍼티를 가질수있다.
근데 놀라운것은 나중에 필요할때마다 추가할수있다는점이다. 



객체 생성 in Javascript 

// (생성자)  함수를 통함. 
function Person(name,age){
            this.name = name;
            this.age    = age
 
           // 메서드도 추가 가능 
};
 
var foo = new Person('foo',30);  // 이미 선언되있던 함수를 가지구  new 를 사용하여 만든다. 


//객체 리터럴을 통해서 (JSON 처럼) 
var foo = { name : ‘foo’,  
           age : 30
};



함수는 일급객체이다. 


//변수나 프로퍼티의 값으로 할당 

var foo = 100;
var bar = function(){return 100; };
console.log(bar()); // 100 

var obj = {};
obj.baz = function(){return 200;}
console.log(obj.baz()); // 200 


//함수 인자로 전달

    var foo = function(func){
func(); // 인자로 받은 함수 호출 
    };

    foo(function(){
                 console.log(“hello”);
          }
    );



//리턴 값으로 활용  

var foo = function(){
return function(){
console.log(“hello”);
                         };
                };

var bar = foo();
bar();

> hello


함수 객체의 기본 프로퍼티   


function add(x,y){return x+y;}

console.log(add);

argument,
caller,
length,
name,
prototype,
__proto__   

등과 같은 다양한 프로퍼티가 기본적으로 생성되어 있다. 
(ECMA표준도 있고, 브라우저별 프로퍼티도 있음) 


[[Prototype]] 


모든 자바스크립트 객체는 자신의 프로토타입을 가리키는 
[[Prototype]] 내부 프로퍼티를 가진다. 크롬브라우저에서는
__proto__ 라고 표현.


Function.prototype 
Object.prototype 
Array.prototype
Person.prototype
Number.prototype / String.prototype

constructor 프로퍼티
toString() 메서드
apply(thisArg,argArray) 메서드
call(thisArg,[,arg1[,arg2,]]) 메서드 
bind(thisArg,[,arg1[,arg2,]]) 메서드



함수 형태 


콜백 함수

비동기 처리에서 어떤 신호가 왔을때
웹페이지에서 특정 DOM이벤트에 대응하는 함수 


즉시 실행 함수(1) 
(function(name){
console.log(‘hello -> ‘ + name);
})(‘foo’);

다시 호출안됨. 초기화코드에서 사용하면 좋음.


즉시 실행 함수(2)  
(funtion(window,undefined){

..
})(window);

프레임워크에서 사용됨
jquery 에서 전체를 묶음(바운더리만듬)


내부 함수

자신을 둘러싼 부모함수의 변수에 접근 가능

        (클로저) 


함수를 리턴하는 함수

var self = function(){
         console.log(‘a’);
      return function(){
                  console.log(‘b’);
                  }
}

self = self(); // result  >>  a
self();          // result  >>  b





호출패턴 과 This바인딩 

자바스크립트에서 함수를 호출할때 기존 매개변수로 전달되는 인자값에 더해, arguments 객체 및  This인자가 함수 내부로 

암묵적으로 전달됨. 자바스크립트에서 This 는 매우 중요하다. 함수가 호출되는 방식(호출패턴)에 따라 This 가 다른 객체를 

참조(This바인딩) 하기 때문이다.


This바인딩(객체의 메소드호출시) 

객체의 메서드를 호출할때, 메서드 내부에서 사용하는 This 는 해당메서드를 호출한 객체로 바인딩된다. 

var myObject = {
name: ‘foo’,
sayName : function(){
console.log(this.name);
}
};

var otherObject = { name : ‘bar’ };

otherObject.sayName = myObject.sayName;

myObject.sayName(); // ‘foo’
otherObject.sayName(); // ‘bar’



This바인딩(함수 호출시1) 

This는 전역 객체에 바인딩된다. 

-브라우저라면 window 객체
- Node 라면 globlal 객체 

“자바스크립트의 전역변수는 실제로는 전역객체의 프로퍼티들이다.”
var foo = “hello world”;
console.log(foo);   // hello world
console.log(window.foo); // hello world

var test = ‘This is test’;
console.log(window.test);

var sayFoo = function(){
console.log(this.test);
}

sayFoo();   // 'This is test' 



This바인딩(함수 호출시2) 

이런 특성은 내부 함수를 호출할때도 마찬가지이다.  즉 외부에서 객체를 호출한게 아니라면 this 는 전역임(아 어렵다 ..OTL) 
var value = 100;
var myObject={
value : 1,
func1 : function(){
this.value +=1;
console.log(‘ func1 this.value : ‘ + this.value);
func2 = function(){
this.value += 1;
console.log(‘ func2 this.value : ‘ + this.value);
}
func2();
}
}; 

myObject.func1();

  • " func1 this.value : 2"
  • " func2 this.value : 101"


This바인딩(함수 호출시3) 

다음과 같이  해결한다. 
var value = 100;
var myObject={
value : 1,
func1 : function(){
var that = this; 
this.value +=1;
console.log(‘ func1 this.value : ‘ + this.value);
func2 = function(){
this.value += 1;
console.log(‘ func2 this.value : ‘ + that.value);
}
func2();
}
}; 

myObject.func1();

  • " func1 this.value : 2"
  • " func2 this.value : 2"



This바인딩(생성자 함수 호출) 

그냥 기존함수에 new연산자를 붙여서 호출하면 해당 함수는 생성자 함수. 

new연산자로 함수를 생성자로 호출했을때 일어나는 일  (굉장히 중요함!!!) 

1.  빈 객체 생성 및 this 바인딩 
2.   this  를 통한 프로퍼티 생성 
3.  생성된 객체 리턴 

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

var foo = new Person("foo");
console.log(foo.name);  

  • "foo"
1. Person() 함수가 생성자로 호출되면, 일단 빈객체가 생성된다. this 를 빈객체와 바인딩 
2.빈객체는 Person() 의 prototype 프로퍼티가 가르키는 객체를 연결해서 자신의 프로토타입으로 설정
3. this 가 가르키는 빈 객체에  name 이라는 동적 프로퍼티 생성 
4. 리턴값이 없으므로  this  로 바인딩한  객체가 리턴값으로 반환되어 foo변수에 저장 





객체 생성 


객체 리터럴 vs 생성자 함수


리터럴은 한번만 가능 /   생성자함수를 사용하면 같은 형태를 재 생성 가능   




생성자 함수 호출시 

new없이   vs  new 를 붙여서 

this바인딩이 다르기때문에 오류발생가능 큼 
개발할때 큰 주의 요망. 

많은 라이브러리에서 내부코드에 저것을 방지하기위한 코드패턴이들어가있다. 

function A(arg){
                            if(!(this instanceof A))
                                         return new A(arg);





This바인딩(call, apply ) 

둘다 기능은 같으니 apply로 설명.
지금까지는 this 가  강제로 바인딩되었지만 apply를 사용하면 명시적으로 바인딩 

apply는 Function.property 객체의 메서드이다.
다음과 같이 호출가능.
function.apply(thisArg, argArray) 

function Person(name,age,gender){
this.name = name;
this.age = age;
this.gender = gender;
}

var foo = {};

Person.apply(foo, [‘foo’, 30, ‘man’]);      // foo 객체가 Person 객체의 this 로 바인딩됨. 
console.dir(foo);

  • [object Object] {
      age
    : 30,
      gender
    : "man",
      name
    : "foo"
    }



함수 리턴  (1)

일반 함수나 메서드는 리턴값을 지정하지 않을 경우, undefined값이 리턴 

var noReturnFunc = function(){
console.log(‘This function has no return statement.’);

};


var result = noReturnFunc();
console.log(result);    // undefined




함수 리턴  (2)

생성자 함수에서 리턴값을 객체로 지정할경우 그 객체가 리턴된다. 

function Person(name,age,gender){
this.name = name;
this.age    = age;
        this.gender = gender;

return {name: "bar", age:20, gender : "woman"};
};

var foo = new Person("foo",30,"man");
console.log(foo);

  • [object Object] {
      age
    : 20,
      gender
    : "woman",
      name
    : "bar"
    }

   

생성자 함수의 리턴값으로 넘긴 값이 객체가 아니라 불린,숫자,문자열일경우는 무시. 



prototype 프로퍼티 vs [[Prototype]] 







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

var jane = new Person(‘Jane’);    // Person 객체는 상위에 프로토타입을 가지고있고, jane 이라는 새 객체가 생성되면 
                                                // jane 에 [[Prototype]] 이 생기고 그것은 Person 의 프로토타입을 가르키게 된다. 
                                                // 공통 상위단이 생긴다고 생각하면 된다.



프로토타입 체이닝

자바스크립트에서 객체는 자기 자신의 프로퍼티뿐만 아니라 ,
자신의 부모역할을 하는 프로토타입객체의 프로퍼티 또한 마치 자신의 것 처럼 접근하는게 가능하다. 이걸 가능케하는게 바로 프로토타입 체이닝!!




프로토타입 체이닝(1)

객체 리터럴 방식으로 생성된 객체의 프로토타입 체이닝 
Object.prototype 객체 

name = "john";
var myObject = {
name : ‘foo’,
sayName: function(){
console.log(‘My name is ‘ + this.name);    // 여기서  this 는  myObject  이다.
}
};

myObject.sayName();   
myObject.hasOwnProperty(‘name’);

  • "My name is foo"
  • true


name = "john";
var myObject = {
name : "foo",
  func1 : function(){
  sayName =  function() {
console.log("My name is " + this.name);     
};
        sayName();  // 이거 호출시  this 는 전역이다.  이런거 너무 헥깔린다;;
}
};

myObject.func1();   
myObject.hasOwnProperty("name");

  • "My name is john"
  • true



프로토타입 체이닝(2)

생성자 함수로 생성된 객체의 프로토타입 체이닝  
Person.prototype (함수에 연결된 프로토타입 객체는 디폴트로 constructor프로퍼티만 가진 객체이다) 

Person() -> Person.prototype->Object.prototype 

function Person(name,age,hobby){
this.name = name;
this.age    = age;
        this.hobby = hobby;
}

var foo = new Person(‘foo’,30,’tennis’);

foo.hasOwnProperty(‘name’); // true




프로토타입 체이닝(3)

프로토 타입 체이닝의 종점 Object.prototype 

객체 리터럴 방식이나 생성자 함수 방식에 상관없이 모든 자바스크립트 객체는 프로토타입체이닝으로 
Object.prototype 객체가 가진 프로퍼티와 메서드에 접근하고,서로 공유가 가능하다. 


그리고 표준 빌트인 프로토타입 객체에도 사용자가 직접 정의한 메서드를 넣을수 있다. 

String.prototype.testMethod = function(){
console.log(‘hello’);
};

var str = ‘this is test’;
str.testMethod();



프로토타입 체이닝(4)

프로토타입 메서드와 this 바인딩 

메서드 호출 패턴에서의 this 는 그 메서드를 호출한 객체에 바인딩!!

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

Person.prototype.getName= function(){
return this.name;};

var foo = new Person(‘foo’);
foo.getName();  //  foo 

Person.prototype.name = ‘person’; // 동적 추가 
Person.prototype.getName(); // person 



프로토타입 체이닝(5)

디폴트 프로토타입(Person.prototype같은) 은 다른 객체로 변경이 가능 하다.

디폴트 프로토타입 객체는 함수가 생성될때 같이 생성되며, 함수의 prototype 프로퍼티에 연결 된다. 

자바스크립트에서는 이렇게 함수를 생성할때 해당 함수와 연결되는 디폴트 프로토타입 객체를 다른 일반 객체로 변경하는것이 

가능하다. 이러한 특징을 이용해서 객체지향의 상속을 구현한다.   


프로토타입 체이닝

객체의 프로퍼티 읽기나 메서드를 실행할때만 프로토타입 체이닝이 동작한다.




실행 컨텍스트

실행 가능한 코드를 형상화하고 구분하는 추상적인 개념 

실행컨텍스트가 형성되는 경우는 전역코드함수로 실행되는 코드, eval() 함수로 실행되는 코드 3가지 

현재 실행되는 컨텍스트에서 이 컨텍스트와관련 없는 실행 코드가 실행되면, 새로운 컨텍스트가 생성되어 스택에 들어가고 

제어권이 그 컨텍스트로 이동한다. 



실행 컨텍스트 생성과정 

1. 활성 객체 생성 

(해당 컨텍스트에서 실행에 필요한 여러가지 정보를 담을 객체)

- 매개변수
- 사용자가 정의한 변수 및 객체
- 스코프 정보

등이 앞으로 담겨짐..  

변수객체로 사용됨. 
 

2.  arguments 객체 생성
  - 매개변수를 담는다. 


3. 스코프 정보 생성  
 -리스트형식으로 만들어짐 이것은 상위 실행컨텍스트의 변수도 접근가능
 -이 리스트를 스코프 체인이라고 함

4. 변수 생성 
   - 지역변수의 생성이 이루어진다.
          (생성은 변수객체에 이루어짐,활성객체가 변수객체로 사용됨) 
   - 생성이 이루어지는것이지 초기화가 이루어지지는 않는다.
   - 초기화는 표현식이 실행될때 이루어진다. 
           (변수객체 생성이 다 이루어진후 표현식 실행됨)

5. this 바인딩 



스코프 체인 

void A()
{
for(.....)
{
int a = 10;
}

printf(“ a: %d”, a);   // 컴파일 에러  c 언어는 for, if문도 하나의 스코프
}  

자바스크립트는 오직 함수만이 유효범위의 한 단위가 된다.
유효범위는 스코프체인을 따라서 위로 계속 올라가면서 넓어짐.. 

- 각 함수객체는 [[scope]] 프로퍼틸 현재 컨텍스트의 스코프체인을 참조한다. 
- 한 함수가 실행되면 새로운 실행 컨텍스트가 만들어지는데, 이 새로운 실행 컨텍스트는 현재 실행되는 함수객체의 
  스코프 프로퍼티를 복사하고 새롭게 생성된 변수객체를 해당 체인의 제일 앞에 추가한다. 



스코프 체인 예제(1)

var value = “value1”

function printFunc(){
var value = “value2”;
function printValue(){
return value;
}

console.log(printValue());
}

printFunc();

  • "value2"


스코프 체인 예제(2)

var value = “value1”

function printValue(){
return value;
}

function printFunc(func){
var value = “value2”;

console.log(func());
}

printFunc(printValue);

  • "value1"
각 함수객체가 처음 생성될 당시 실행 컨텍스트가 무엇인지를 생각해야한다. 




클로저

function outerFunc(arg1, arg2){
var local = 8;
function innerFunc(innerArg){
console.log((arg1 + arg2) / (innerArg + local));
}
return innerFunc;
}

var exam1 = outerFunc(2,4);
exam1(2);   
  • 0.6



클로저(1)

function outerFunc(){
var x = 10;
var innerFunc=function(){ console.log(x)};
return innerFunc;
}

var inner = outerFunc();
inner(); 
  • 10

outerFunc 실행컨텍스트는 사라졌지만 outerFunc 변수객체는 여전히 남아있다.



클로저(2)
함수의 캡슐화
( I am brad. I live in seoul. I’am 28 years old 문장 출력하기)

var buffAr = [
‘I am’,
‘’,
‘.I live in ‘,
‘’,
‘. I \’ am’,
‘’,
‘years old.’,
]; 

function getCompletedStr (name, city, age) 
{
buffAr[1] = name;
buffAr[2] = city;
buffAr[5] = age;

return buffAr.join(‘’);
};

var str = getCompletedStr(‘brad’, ‘seoul’, 28);
console.log(str);



객체지향 프로그래밍 

var person = { 
name = “brad lee”,
getName : function(){
return this.name;
},
setName : function(arg){
this.name = arg;
}
};

function create_object(o){
function F(){};
F.prototype = o;
return new F();
}

var student = create_object(person);

student.setName(“ john “);
console.log(student.getName());  // john 


student 객체는 person 객체를  상속받은후에 다른 기능을  추가도  할수있게 된다. 

student.setAge = function (arg) { .....}  



하지만 이렇게 되면 코드가 지져분해지는데 , 그래서  라이브러리들은  extend() 함수를 제공

jQuery 1.0 의 extend() 를 살펴보자

jQuery.extend = jQuery.fn.extend = function(obj, prop) {
if( ! prop ){ prop = obj ;  obj = this; }
for(var i in prop ) {
obj[i] = prop[i];   // 얕은 복사 
}
return obj;
}; 


jQuery 1.7 에서는 깊은복사/얕은복사 선택을 해준다. 

jQuery 에서 이런것은 extend 는 

내가 만든 기능을 jQuery 에 플러그인으로 집어넣기 위해 사용할수 있겠다.


(function ($) {
$.extend( $.fn , {
my_func : function() {
...
}
} );
}) (  jQuery );






함수형  프로그래밍 

function sum( arr ){
var len = arr.length;
var i = 0 , sum = 0;

for ( ; i < len ; i++) {
sum += arr[i];
}

return sum;
}

var  arr = [ 1, 2, 3, 4, 5];
console.log(sum (arr));
  • 15

//////////////////////////////////////////////////////////////////////////////////

// 함수를 인자로 매개변수로 넘겨서 계산하게함. 
var sumFunc = function (x,y){
return x + y;
};

function reduce ( func, arr ){
var len = arr.length;
var i = 0 , sum = 0;
for( ; i < len ; i++){
sum  = func(sum, arr[i]);
}
return sum;
}

var  arr = [ 1, 2, 3, 4, 5];
console.log(reduce(sumFunc, arr));

  • 15






참고 

http://www.yes24.com/24/goods/11781589?scode=032&OzSrank=1
http://www.yes24.com/24/goods/3071412?scode=032&OzSrank=10



Comments