레이블이 JavaScript인 게시물을 표시합니다. 모든 게시물 표시
레이블이 JavaScript인 게시물을 표시합니다. 모든 게시물 표시

2017년 1월 30일 월요일

[JavaScript] 자바스크립트 이벤트 핸들링(Event Handling)

Events란 사용자, 또는 브라우저에 의해 실행되는 행위들을 말한다. 이러한 event들에는 click, load, mouseover, focus 등이 있다. Event가 있으면 이들을 사용하는 것도 있는데, event에 반응하는 함수(function)들을 event handler, 또는 event listener라 부른다. event listener 들의 이름은 간단하다. Onclick, onload, onmouseover, onfocus처럼 원하는 event에 on을 붙이면 된다.
이러한 Event Handler들을 등록하는데 3가지 방법이 있다

HTML Event Handler



<div clicked="" onclick="(function(){alert(onmouseover=alert('mouseover')">
</div>

원하는 event 프로퍼티에 javascript statement들을 적어주면 된다. Javascript statement가 들어가기 때문에, self executing function 또는 함수 호출 또한 적을 수 있다. 주의할 점은, 자바스크립트 코드들이 HTML의 프로퍼티 값으로 들어가게되어, HTML 문법에 포함되는 문자들을 사용해서는 안된다. 예를들어, <, >, “, 등을 escape character 가 아닌 방식으로 사용하게 되면 페이지에 에러가 발생할 수도 있다. 그러니 " < > 등의 특수문자 코드를 이용하여야 한다.

이런 방식으로 등록된 event handler들은 attribute란에 있는 자바스크립트 구문들로 function을 생성하는것과 같은 효과를 내게 된다. 관련 event 객체에 접근할 수 있는 event라는 로컬 변수를 가지게 되며, event 객체를 새로 선언하거나 argument 리스트에서 객체를 찾아오지 않고도 event 객체를 사용할 수 있게 해준다.


<div onclick="”(function(){alert(event.type);})()”">
</div>

또한 이렇게 생성된 함수 scope내의 this 값은 event handler가 등록된 html element가 되며, document 객체와 this 객체 내부 변수들을 로컬 변수처럼 사용할 수 있다. 이는 html event handler 가 지금은 deprecated 된 with 문을 이용해 함수를 구성하기 때문이다.


<div onclick="”(function(){alert(this.type);})()”">
</div>
//위의 코드는 아래와 같다
 
function(){
with(document){
with(this){
alert(this.type):
}
}
}

그러므로 this.type을 그냥 type으로 교체해도 함수는 잘 작동하게된다. 또 이로인해서 event handler 내에서 이벤트가 발생한 html 엘리멘트의 프로퍼티에 더 쉽게 접근할 수 있다.

HTML event handler의 단점을 말하자면, 첫번째로 시점 문제를 들 수 있다. 예전에 script defer, async 태그 글에서 말했던것 처럼, script가 로딩과 html 페이지 로딩, 렌더링은 모두 같은 시점에서 완료되지 않는다. 만약 우리가 html event handler에서 별도의 스크립트 파일 내의 함수를 호출하게 될때. Script 가 더 늦게 로딩된다면 웹브라우져는 그 함수를 찾지 못하고 에러를 발생시키게 될것이다. 그렇기 때문에 대부분의 html event handler들은 try catch문을 필수적으로 사용하게 된다.

또다른 단점으로는 script와 html간의 의존성이다. Html안에 script가 들어간 형식이기 때문에 script의 수정은 바로 html의 수정으로 이어지고, 역으로 html의 수정은 script의 수정으로 이어지게 된다. 이때문에 코드가 조금만 복잡해져도 일이 2배가 되니, 가능하면 html event handler의 사용을 피하도록 하자.


DOM Level 0 Event handler


HTML Element 객체를 호출해 객체의 프로퍼티에 어싸인하는 방식이다.

var button = document.getElementById(“login”);
button.onclick = function(){
alert(this.id); // “login”
process_login();
}

window, document 객체를 포함한 모든 Element 객체는 이벤트 핸들러 프로퍼티를 가지고 있다. 단 모든 이벤트 핸들러 프로퍼티는 모두 소문자로 되어있으며(onclick, onfocus, etc.), 이 프로퍼티에 함수를 어싸인하여 이벤트를 핸들할 수 있다.

위의 예제에서는 login이라는 id를 가진 html element 객체가 호출되었고, 해당 객체의 onclick 프로퍼티에 함수를 어싸인하였다. Event handler 가 스크립트 상에서 어싸인되기때문에, 해당 코드가 실행될때까지는 이벤트가 발생해도 핸들링 되지 않는다. 또한, 어싸인된 이벤트핸들 함수는 이벤트가 발생한 HTML Element 객체의 프로퍼티이기 때문에 그 함수내에서 this 값은 이벤트가 발생한 Element 객체와 같다.

이렇게 어싸인된 이벤트 핸들러를 제거하려면 해당 프로퍼티에 null 값을 어싸인해주면 된다

button.onclick = null;

이 이벤트핸들러는 DOM Level 0 에서 소개되었기 때문에 그냥 DOM Level 0 Event handler로 불린다.

DOM Level 2 Event Handler


DOM Level 0 에서 객체의 프로퍼티를 이용해 이벤트 핸들러를 어싸인한것과 달리, DOM Level 2 에서는 2개의 함수를 이용해 이벤트핸들러를 추가, 제거하는 방법을 소개했다. 모든 DOM node들에는 addEventListener() 함수와 removeEventListener()함수가 존재하며 이 함수들은 핸들할 이벤트 이름, 핸들 함수, 그리고 Event Capture 단계에서 이벤트를 핸들할지(true), Bubble단계에서 이벤트를 핸들할지(false) 여부에 대한 Boolean 값을 포함해 총 3개의 argument들을 받는다.

예를들어 bubble 단계에서 click 이벤트를 핸들할 함수를 어싸인하는것은 아래아 같다


var button = document.getElementID(“button”);
var showAlert =  function(){
alert(“you clicked” + this.id);
}
button.addEventListener(“click”,showAlert,false);

DOM Level 0 과 같이 this 값은 해당 element 객체와 같다.

DOM Level 2 방식의 가장 큰 장점은 1개의 이벤트에 여러개의 이벤트 핸들러를 어싸인 가능하다는 것이다.


var button = document.getElementID(“button”);
var showAlert =  function(){
alert(“you clicked” + this.id);
}
button.addEventListener(“click”,showAlert,false);
button.addEventListener(“click”,function(){alert(“second message”);},false);

위의 경우에 이벤트 핸들러는 추가된 순서대로 실행되게 된다.

DOM Level 2 방식으로 추가된 핸들러들은 DOM Level 2 방식으로만 제거될 수 있다. 즉 addEventListner 함수로 추가한 이벤트 핸들러가 있다면 같은 argument로 호출된 removeEventHandler만이 제거할 수 있다는 뜻이다. 그렇기 때문에 만약 익명함수의 이벤트 핸들러를 추가했다면, 그 이벤트 핸들러의 제거는 불가능 하다는 뜻이기도 하다.


var button = document.getElementID(“button”);
var showAlert =  function(){
alert(“you clicked” + this.id);
}
button.addEventListener(“click”,showAlert,false);
button.addEventListener(“click”,function(){alert(“second message”);},false);
button.removeEventListener(“click”,showAlert,false);
button.removeEventListener(“click”,function(){alert(“second message”);},false);

위의 예제에서 첫번재 이벤트 핸들러는 제거되었지만 2번째 이벤트 핸들러는 제거되지 않았다. 2번째 이벤트 핸들러 제거를 위해 완벽히 일치하는 argument를 넘긴것 같지만, 지난 함수 글에서 말했듯 함수는 객체이고, 새로운 객체인 anonymous 함수를 생성하였기 때문에 2번째 핸들러는 제거되지 않는다.

2015년 10월 6일 화요일

[JavaScript] 문서 객체 모델(DOM, Document Object Model)

문서 객체 모델(DOM, Document Object Model)은 HTML과 XML 문서에 사용되는 API(Application Programming Interface)이다. DOM은 주어진 문서를 노드(node)를로 이루어진 계층구조의 트리(hierarchical tree)로 나타내며, 원하는 노드를 추가, 제거, 그리고 수정할 수 있게 한다. 이런 DOM의 가장 초기 버젼인 DOM Level 1은 1998년 W3C에 처음 표준으로 도입되었으며 현재 거의 모든 웹브라우져들, Chrome, Firefox, Safari, Internet Explorer, Microsoft Edge, Opera 등에 사용된다.

DOM Tree의 Node

각각의 태그들로 구성된 HTML 문서와 XML 문서를 Tree 형태로 표현하기 위해서 Node 들은 각각의 태그들을 구성하게 된다. 이 때문에 각각의 태그들은 그에 알맞는 Type을 가진 Node 행태로 저장되며, 서로 다른 Type의 Node 들은 그만의 특징, 데이터, 메소드, 다른 노드들과의 관계를 가지게 된다.

Document Node 와 Node Type

Document Node는 모든 Node들의 최상위에 위치하는 Root 노드이다.


<!DOCTYPE html>
<html>
 <head>
  <title>DOM Example</title>
 </head>
<body>
 <p>This is HTML Example</p>
</body>
</html>


예를들어 위의 html 코드를 DOM Tree 형식으로 표현한다면 아래와 같은 형태를 띄게된다.


Document Node 가 최상위에 위치하며 그 바로 아래 HTML Element Node 가 위치한다. Document Node는 최상위 Node로 문서내 모든 Node들의 ownerDocument 프로퍼티가 가리키는 node이다.
HTML Element Node는 Document Element 라 불리기도 하며, 다른 Element Node들의 최 상위에 위치하는 Element Node이다. 그렇기 때문에 Document Node는 단 1개의 Document Element를 자기 child 로 가지며, Document Element는 다른 element 들을 자기 children으로 가지게 된다.

HTML문서의 경우에는 html 태그가 Document Element이며, 이렇게, 모든 markup들은 Node로 Tree에 저장된다. 대부분의 markup 들은 Element Node 가 되며, attribute 들은 attribute node, markup에 감싸진 텍스트 들은 text node, 주석들은 comment node가 된다. 이렇게 총 12개 타입의 노드들이 존재한다.

  • Node.ELEMENT_NODE - 1
  • Node.ATTRIBUTE_NODE - 2
  • Node.TEXT_NODE - 3
  • Node.CDATA_SECTION_NODE - 4
  • Node.ENTITY_REFERENCE_NODE - 5
  • Node.ENTITY_NODE - 6
  • Node.PROCESSING_INSTRUCTION_NODE - 7
  • Node.COMMENT_NODE - 8
  • Node.DOCUMENT_NODE - 9
  • Node.DOCUMENT_TYPE_NODE - 10
  • Node.DOCUMENT_FRAGMENT_NODE - 11
  • Node.NOTATION_NODE - 12
각각의 타입은 숫자로 표현될 수 있다. 모든 Node 들은 nodeType 프로퍼티를 가지므로 위의 numeric constant로 비교가 가능하다.


if(node.nodeType == Node.ELEMENT_NODE){
 console.log("This is Element Node");
}


nodeType 프로퍼티가 node의 타입을 가르쳐 준다면, nodeName 프로퍼티는 해당 node의 태그이름을 저장하는 값이다.
nodeName 값과 nodeType 값은 node의 타입에 관계없이 모든 node가 가지고 있는 프로퍼티이다.


document.images[0].nodeName
//"IMG"
document.images[0].nodeType
//1


DOM(Document Object Model) 수정하기

모든 tree 구조가 그렇듯, 각각의 node 들은 다른 node 들과 특정 관계(Parent-Child, Siblings, First Child, Last Child)에 있다. 이러한 관계를 통해 개발자는 문서 객체 모델을 조금 더 쉽게 수정 가능하다.
우선 모든 Node 들은 자신의 프로퍼티로 childNodes라는 프로퍼티를 가지는데 이는 자신의 자식 node들의 리스트를 저장하는 프로퍼티 이다.childNodes 프로퍼티는 자식 노드들을 NodeList 타입의 자료구조로 저장한다. NodeList는 Array와 매우 비슷한 자료 구조로, Array와 마찬가지로 괄호([])를 이용해 내부 데이터에 접근이 가능한 구조이다. 하지만 Array Constructor를 통해 생성된 인스턴스는 아님을 염두에 두자. NodeList 객체는 실시간으로 DOM 스트럭쳐와 반응하며, 이때문에 NodeList에 생기는 변화는 곧바로 DOM 구조, 웹문서와 직결된다. NodeList 타입은 Array와 마찬가지로 []를 사용할 수도 있고, item 메소드를 이용해 접근할수도 있다.


node.childNodes[0];
node.childNodes.item(1);
node.childNodes.length;
node.firstChild == node.childNodes[0];//true
node.lastChild == node.childNodes[node.childNodes.length];//true


firstChild, lastChild 프로퍼티를 이용해 NodeList의 처음과 마지막 node에 접근이 가능하다.
또한 childNodes 프로퍼티와 마찬가지로 parentNode는 해당 Node의 상위 Node를 가르키는 포인터 역할을 한다. childNodes 리스트에 속한 Node들은 서로와 sibling 관계에 있으며 nextSibling, previousSibling 프로퍼티를 이용해 전 node, 다음 node로 접근이 가능하다.


node.childNodes[0].previousSibling;//null
node.childNodes[0].nextSibling;// == node.childNodes[1]
node.childNodes[node.childNodes.length-1].nextSibling;//null


리스트의 처음과 끝에 있는 node들은 previousSibling, nextSibling 프로퍼티로 null 값을 가지게 된다. 이런 포인터들은 모두 read-only 값을 가진 프로퍼티이기 때문에 위 프로퍼티들을 통해 직접적은 node 수정은 불가능하다.
그 때문에 수정시에는 DOM 수정을 위핸 메소드들을 이용해야한다.

Node 추가, 수정, 제거하기

appendChild() 메소드는 해당 노드의 childNodes 리스트의 마지막에 새로운 Node를 추가하고 추가된 node를 반환하는 메소드이다.


var newlyAddedNode = node.appendChild(newNode);
newlyAddedNode == newNode//true
node.lastChild == newNode//true


insertBefore() 메소드는 원하는 노드의 앞에 새로운 노드르 삽입하는 메소드이다. 새로 추가하는 노드, 삽입 위치의 뒤에있는 노드, 이렇게 2개의 argument를 가지며 새로 삽입된 노드를 반환한다.


var newlyAddedNode = node.insertBefore(newNode,node.childNodes[0]);
newlyAddedNode == newNode//true
node.firstChild == newNode//true
//만약 두번째 argument가 주어지지 않거나 null이 되면 nodeList의 마지막에 삽입된다.


replaceNode() 메소드는 원하는 노드를 새로운 노드로 교체하는 메소드 이다. 새로 교체할 노드, 교체될 노드, 이렇게 2개의 argument를 받으며 교체된 노드가 반환된다.


var oldNode = node.replaceNode(newNode,node.childNodes[0]);


removeNode() 메소드는 원하는 노드를 제거하는 노드이다. argument로는 제거할 노드를 가리키는 포인터를 받으며, 제거된 노드가 반환된다. 이렇게 제거된 node들은 제거된 이후에도 document에 의해 소유된다. 하지만 HTML Element 하위 트리에 속해있지 않기 때문에 사용자에게 보여지지 않는다.


var removed = node.removeNode(node.childNodes[0]);


Node 복사하기

문서 객체 모델 구조의 특성상 1개의 노드는 2개의 위치에 존재할 수 없다. 만약 그렇다면 이는 해당 노드의 포인터 프로퍼티가 2개의 값을 가지고 있음을 뜻하며 현실적으로 불가능한 일이다. 그렇기 때문에 이미 문서에 추가된 노드를 다른 위치에 삽입하려 시도하게 되면 원래 추가되었던 노드가 제거되는 일이 발생한다.


var oldNode = node.childNodes[0];
node.appendChild(oldNode);
node.childNodes[0] == oldNode;// false
node.lastChild == oldNode;// true


이러한 이유 때문에 이미 추가된 node를 문서의 다른 위치에 추가하기 위해서는 같은 프로퍼티를 가진 새로운 node 인스턴스를 생성해야할 필요가 있다. cloneNode() 메소드는 이러한 작업을 충실하게 수행하는 메소드이다. 이 메소드는 boolean 값 1개만을 argument로 받으며 이 boolean 값은 node의 하위 sub-tree까지 모두 포함하여 복사할지(true), 아니면 해당 node만 복사할지(false)를 정하는 argument 이다.


var clone1 = node.cloneNode(true);
var clone2 = node.cloneNode(false);


normalize() 메소드

normalize() 메소드는 메소드가 실행된 노드의 sub-tree에서 empty text node를 제거하는 메소드이다.normalize 메소드는 이런 empty text node를 제거하고, 여러개의 text node가 sibling 관계로 존재할시 해당 node들을 병합(merge)하는 메소드 이다.


<!DOCTYPE html>
<html>
 <head>
  <title>DOM Example</title>
 </head>
<body>
 <p id = "paragraph">
  This is HTML Example.
 </p>
</body>
</html>


위의 html 코드에서 p태그에 새로운 text를 추가한후에 childNodes 리스트를 보게되면 2개의 text node가 sibling 관계로 존재하는걸 알 수 있다.이를 normalize 메소드를 이용해 합쳐주자


document.getElementById("paragraph").appendChild(document.createTextNode("NewLine"));
document.getElementById("paragraph").childNodes;//["This is HTML Example.", "NewLine"]
//이를 normalize 메소드를 이용해 합쳐주자
document.getElementById("paragraph").normalize();
document.getElementById("paragraph").childNodes;//["This is HTML Example.NewLine"]

2015년 9월 29일 화요일

[JavaScript] 브라우져 객체 모델(BOM, Browser Object Model) - history 객체

history 객체는 현재 웹페이지를 표시하는 브라우져 윈도우의 방문기록을 저장하는 객체이다. history 객체는 window 객체의 프로퍼티 중 하나이기 때문에 다른 윈도우의 방문기록에는 접근하지 못한다. 또한 보안상의 이유로 history 객체에 저장된 url들에도 접근이 불가능하기 때문에, 오직 웹페이지 이동만이 가능하다.

history 객체는 length 프로퍼티를 가지는데 length 값은 history 스택의 길이이다. 즉 history.length 가 0이라면 이는 history 스택이 empty 상태란것을 말하게 된다.

history 객체의 go 메소드를 이용해 방문기록으로 이동이 가능하다.
go 메소드는 1개의 argument 를 받는다. 해당 argument 는 숫자 또는 문자열이 될 수 있으며 argument 의 타입에 따라 다른 결과를 낳는다.


//숫자를 argument로 전달할시
history.go(-1)//바로 전 페이지로 이동
history.go(-2)//전전 페이지로 이동
history.go(0)//현재페이지를 리로딩
history.go(1)//앞페이지로 이동

//문자(url)을 argument로 전달할시
history.go("google.com")//가장 최근의 google.com 페이지로 이동한다
history.go("naver.com")//가장 최근의 naver.com 페이지로 이동한다

go 메소드처럼 forward, back 메소드를 이용, 앞페이지, 전페이지로 이동이 가능하다.


history.back();
history.forward();

[JavaScript] 브라우져 객체 모델(BOM, Browser Object Model) - location 객체

브라우져 객체 모델을 구성하는 객체중 하나인 location 객체, location 객체는 현재 윈도우에 로드된 웹문서에 대한 정보를 제공한다. location 객체는 window.location, 또는 document.location으로 접근 가능하다. location 객체는 JavaScript 에서 유일무희하게 현재 웹페이지의 url에 접근 가능한 객체이다. location 객체의 property 들은 아래와 같다.

Property설명예) "http://muckycode.blogspot.kr/2015/05/javascript-prototype.html#tableContent" 기준
hashurl hash, # 문자가 없을경우에는 """#tableContent"
hostserver 이름과 port"muckycode.blogspot.kr"
hostnameserver 이름"muckycode.blogspot.kr"
href전체 url"http://muckycode.blogspot.kr/2015/05/javascript-prototype.html#tableContent"
pathname현재 url의 directory "/2015/05/javascript-prototype.html"
portport 넘버""
protocol프로토콜http:
search현재 url의 query string, ?로 시작하는 문자열""

location 객체를 이용해 url 조작하기

브라우져의 현재 위치는 다양한 방법에 의해 조작될 수 있다. 가장 쉬운 방법으로는 location객체의 assign 메소드를 이용하는것이다.


location.assign("http://google.com");
위 코드는 현재 페이지를 http://google.com 으로 리디렉트 한다.

또 다른 방법으로는 location.href 프로퍼티나 location 객체에 새로운 String 값을 어싸인 하는 법이 있다.


window.location = "http://google.com";
location.href = "http://google.com";

웹페이지 새로고침

location.reload 메소드를 이용하면 웹페이지를 리로딩 할 수 있다. reload 메소드는 별다른 argument를 필요로 하지 않으나, 경우에 따라 true 값을 전달하지 않을 시에 브라우져 캐시를 이용하여 페이지를 다시 로딩할 수도 있다.
만약 true 값을 argument로 전달한다면, 해당 웹페이지는 서버를 통해 reload 된다.


location.reload();//브라우져에 따라 cache를 통해 reload 할 수도 있다.
location.reload(true);//sever에 다시 연결새 reload 한다. 

2015년 9월 24일 목요일

[JavaScript] 브라우져 객체 모델(BOM, Browser Object Model) - window 객체

Browser Object Model, BOM은 ECMAScript, DOM(Document Object Model)와 함께 JavaScript를 구성하는 3가지 요소 중 하나이다. BOM은 웹페이지의 컨텐츠에 관계없이 브
라우져에 대한 정보를 제공해주는 객체이다.

window 객체와 global 스코프


이런 BOM의 중심에는 window 객체가 함께하고 있는데, 이 window 객체는 브라우져 윈도우의 정보를 저장함과 동시에 ECMAScript의 Global 객체의 역할을 수행하기도 한다. 즉, 우리가 작성하는 스크립트 내에서 선언되는 모든 객체, 변수, 함수들이 global 객체window 객체 내의 프로퍼티(property)로써 선언된다는 뜻이며, window 객체가 global scope의 역할을 수행한다는 말이다.

아래 코드를 보자.


var t = "test";

function printT(){
console.log(t); //test
}

printT();

console.log(window.t); //test
window.printT(); //test

console.log(delete window.t); //false
console.log(delete window.printT); //false 

window.g = "window";

console.log(g); //window
console.log(delete window.g); //true
console.log(window.g); //undefine
console.log(g); //error

선언된 변수 twindow 객체를 이용해 접근이 가능하기도, window 객체를 통하지 않고도 접근이 가능하다. 선언된 함수 printT 또한 window 객체를 이용해 접근이 가능하다.
이렇게 글로벌 스코프에서 선언된 함수들과 변수들이 window 객체의 프로퍼티가 되지만, 실제 window 객체의 프로퍼티로써 선언된 변수와 함수들과는 작은 차이를 가진다.

바로 delete 키워드를 이용해 객체의 프로퍼티를 삭제할 때의 경우인데, 이 경우에는 window 객체를 통해서 선언되지 않은 함수들과 변수들은 삭제가 불가능하다.
위의 코드에서 window 객체를 통하지 않고 선언된 변수 t 와 함수 printT의 경우에는 delete 키워드를 이용해 프로퍼티 삭제를 시도할 경우에 false 값을 반환하게 된다. 하지만 window 객체를 통해 선언된 프로퍼티 g 같은 경우에는 delete 키워드를 이용해 삭제를 시도할시 true 값을 반환하며 성공적으로 프로퍼티를 삭제하게 된다. 이는 var 키워드를 이용해 선언된 변수들의 configurable attribute(Property Attribute 중 하나, 참조글)가 false로 세팅되기 때문이다.


window 객체와 BOM(Browser Object Model)


HTML frame

웹페이지에 존재한는 모든 frame 태그들은 window.frames 프로퍼티를 통해 접근이 가능하다. window.frame 프로퍼티는 콜랙션 프로퍼티로 frame 태그들은 html 코드에 적힌 순서대로 frame 프로퍼티에 저장된다(window.frame[index], index는 0부터 시작한다).

window 객체와는 별개로 최상위 frame(Browser Window)를 참조하는 top 객체 또한 존재하는데, top 객체의 frames 프로퍼티를 이용해 하위 프레임에 접근하는 것도 가능하다.


<html>
    <head>
        <title></title>
    </head>
    <frame name="fr1" src="frame01.html"></frame>
    <frame name="fr2" src="frame02.html"></frame>
    <frame name="fr3" src="frame03.html"></frame>
    <body>
        <script type="text/javascript">
        var fr1 = window.frames[0];
        var fr2 = window.frames[1];
        var fr3 = top.frames[3];
        </script>
    </body>
</html>

Position

window 객체는 screenX 프로퍼티와 screenY 프로퍼티를 통해 현재 웹브라우저 윈도우의 위치를 제공한다.


window.screenX
window.screenY
//각각의 프로퍼티는 브라우져 최상단 좌측 픽셀의 위치를 알려준다.

Size

window 객체의 outherWidth, outherHeight, innerWidth, innerHeight 프로퍼티를 통해 브라우져 윈도우의 너비,높이와, 웹페이지가 표시되는 top frame의 너비,높이에 접근할 수 있다.


window.outerHeight
//1040
window.outerWidth
//1920
window.innerHeight
//955
window.innerWidth
//1153

Pop Up Window, 팝업 윈도우

window.open 메소드는 사용자가 새로운 팝업 윈도우를 열거나 특정 윈도우를 원하는 URL로 향할수있게 만들어준다.


window.open("http://google.com", "_blank");
//위의 코드는 아래의 html 코드와 같은 기능을 한다. 
<a href = "http://google.com" target = "_blank">

window.open 메소드는 3개의 String 아규먼트를 받는다. 3개의 argument 들은 모두 optional argument 이니, 이 argument들 없이도 window.open 메소드를 호출하는게 가능하다.
  1. 새로 오픈할 윈도우의 url
  2. 타겟 윈도우 프레임의 name 속성
    • _blank : 새로운 윈도우
    • _self : 현재 코드가 실행된 프레임
    • _top : 현재 코드가 실행된 프레임의 최상위 프레임
    • _parent : 현재 코드가 실행된 프레임 의 parent 프레임
    • 원하는 윈도우 프레임 이름 : 만약 해당 name 아트리뷰트를 가진 프레임이 존재하지 않을 경우에 _blank와 같이 새로운 윈도우가 실행되며, 실행된 윈도우 프레임의 name attribute 가 전달된 윈도우 프레임 이름으로 설정된다.
  3. full screen, height, width 등 각종 설정 옵션들을 모아 놓은 specification string, 설정 가능한 스펙들은 아래와 같다. 

설정 이름
height숫자새 윈도우의 height 값, 최소값은 100
width숫자새 윈도우의 width 값, 최소값은 100
location"yes" 또는 "no"location 바가 표시되어야 하는지, 기본값은 no
menubar"yes" 또는 "no"menu 바가 표시되어야 하는지, 기본값은 no
resizable"yes" 또는 "no"사이즈 조절이 가능한지, 기본값은 no
scrollbars"yes" 또는 "no"스크롤링이 가능한지, 기본값은 no
status"yes" 또는 "no"status 바가 표시되어야 하는지, 기본값은 no
toolbar"yes" 또는 "no"툴바가 표시되어야하는지, 기본값은 no
left숫자윈도우의 (0,0) 픽셀의 위치 x값, 최소값은 0
top숫자윈도우의 (0,0) 픽셀의 위치 y값, 최소값은 0

height 500, width 500 사이즈 윈도우를 화면의 (50,50)위치에서 열기위해서는

"height=500,width=500,top=50,left=50" 문자열을 3번째 argument로 전달해주면 된다.

System Dialog

alert, confirm, prompt 메소드를 이용해 알림창, 확인창, 입력창을 띄울 수 있다.

window.alert()

alert() 메소드는 알림창을 띄울때 사용된다.


window.alert("알림창입니다.")

alert 창

위의 코드는 이와 같은 창을 띄우게 된다.

window.confirm()

confirm 메소드는 확인창을 띄울때 사용한다.


var s = window.confirm("confirm 창입니다.")

confirm 창

confirm 메소드는 위와같이 확인창을 띄우며 user가 ok버튼을 누를시 true 값을 cancel 버튼을 누를시 false값을 리턴한다.

window.prompt()

prompt 메소드는 user로부터 문자열을 받아올 때 사용한다.


var s = window.prompt("prompt 창입니다.")

prompt 창

prompt 메소드를 호출할시 위와같은 창을 띄우게 되며, 사용자가 ok 버튼을 누를시에 입력된 문자열을, cancel 버튼을 누를시에 null 값을 리턴한다.

2015년 5월 17일 일요일

[JavaScript] Closure(클로져)

Closure(클로져) 란 무엇인가?


자바스크립트에서 클로져란(Closure) 다른 함수의 스코프(Scope)내에 있는 변수들에 접근할 수 있는 함수를 뜻한다. 아래 코드를 보자.


function closureExample(exVar){
  return function(){
    return exVar;
  };
}

var foo = closureExample("foo");
var bar = closureExample("bar");

console.log(foo());
console.log(bar());

closureExample이라는 함수는 매개변수로 exVar를 받으며, 내부에서 anonymous function(익명 함수, 람다 함수)을 선언 후 반환한다. 반환되는 함수 closureExample함수의 스코프에서 선언되었기 때문에 closureExample함수의 매개변수로 주어진 exVar이라는 변수에 접근할 수 있다. 그리고 반환된 익명 함수는 function expression 방식으로 foobar라는 변수에 어싸인되게 된다.

그렇게 foo와 bar 함수를 실행하면, closureExample함수로 전달한 매개변수에 접근하는 함수가 반환된게 된다.

이게 가능한 이유는 자바스크립트의 Execution Context가 다른 Execution Context에 의해 참조되는 한 파기되지 않기 때문이다. Execution Context 와 스코프에 대해 자세한 글을 읽어볼려면 여기를 클릭하자

조금 더 정확히 말하자면, 반환되는 익명함수는 호출된 closureExample함수내에서 선언되었기 때문에 closureExample함수의 Execution Contextvariable environment 객체를 참조하고 있는데, 자바스크립트의 가비지 콜랙터(Garbage Collector)는, 객체가 다른 인스턴스에 의해 참조되고 있는 동안은 해당 객체를 사용 중으로 판단하여 객체에 할당된 메모리를 수집하지 않는다.

closureExample 함수 내에서 선언된 익명함수 객체가 반환되면서 function expression 방식으로 foo라는 변수에 의해 참조되게 되는데, 이렇게 되면서, 해당 익명함수의 variable environment 객체는 사용 중으로 마크되며, 사용 중으로 마크된 익명함수의 execution context가 closureExample 함수의 variable environment 객체를 참조하게 되니, 마찬가지로 closureExample의 variable environment 객체도 사용중으로 마크되게 된다.

이 때문에, closureExmaple함수의 실행이 끝나 정해진값을 반환했음에도 우리는 closureExample이 호출될 때 생성된 variable environment에 계속 접근이 가능하다.



2015년 5월 13일 수요일

[JavaScript] Regular Expression(정규표현식)과 RegExp 객체

정규표현식이란 특정 패턴의 문자열을 의미하며, 이를 통해 이 문자열에서 특정 단어나, 특정 패턴을 가진 문자열을 찾아낼 수 있다.

자바스크립트는 기본 내장(Built-in)객체로 RegExp 객체를 탑재하며, RegExp 객체의 test 또는 exec 메소드를 이용해 정규표현식을 이용한 패턴 매칭이 가능하다.

정규 표현식 패턴(Regular Expression Pattern)생성하기


정규 표현식의 패턴 여러 문자들로 이루어지며 사용자는 원하는 패턴을 두개의 /(Slash)사이에 적게된다. /(Slash)는 delimiter로써 프로그래밍 언어에서 해당 패턴의 시작과 끝을 알린다. 또한 / 뒤에 정규 표현식 검색 옵션을 적을 수도 있다.


var expression = /abc/; //abc문자열을 뜻한다.
var expression = /at/; //at문자열을 뜻한다.

이에 더해서 몇가지 특수문자를 이용해 조금 더 복잡한 패턴 생성이 가능하다.

특수문자의미
일반
\1. 일반 문자 앞에 올 경우에는, \를 포함한 일반문자를 특수문자로 해석한다.
2. 특수 문자 앞에 올 경우에는, Escape Chracter 의 역할을 한다, 즉 /\\/은 일반문자 "\"를 표현하는 표현식이다.
^문자열의 시작과 일치한다.
예) /^E/는 E로 시작하는 모든 문자열과 일치한다.
$문자열의 끝과 일치한다.
예)/E$/는 E로 끝나는 모든 문자열과 일치한다.
** 선행 문자가 0번이상 반복된다. {0,}과 같은 의미를 가진다.
/ab*/는 abbbbb, a, ab, 등과 일치하며, /ab*c/는 abbbbc, ac, abc, 등과 일치한다.
Quantifiers
++ 선행문자가 1번이상 반복된다. {1,}과 같은 의미를 가진다.
/ab+/는 abbbbb, ab 등과 일치하나, a와는 일치하지 않는다.
/ab+c/는 abbbc, abc등과 일치하나 ac와는 일치하지 않는다.
?1. 선행 문자가 0번 또는 1번 존재한다. {0,1}과 같은 의미를 가진다
2. 수량자(Quantifier, *, +, ?,{})의 뒤에 올때는 선행 수량자의 최소값만큼 일치한다.
예) /a{1,4}/ 패턴을 "aaaa"라는 문구에 사용시, "aaaa" 전체와 일치하나,
/a{1,4}?/ 패턴을 사용시에는 제일 앞의 a 한글자에만 일치한다
{n}선행 문자가 양의 정수 n 회만큼 발생한다
{n,m}선행 문자가 최소 n회, 최대 m회만큼 발생한다.
m이 비게되면 무한을 뜻한다, {n,}은 최소 n회이상을 의미
Group Constructors
(x)하위 패턴을 생성하며 \1, \2 와 같이 \숫자 문구를 이용해서 패턴을 재사용 가능하다.
예) 표현식 \([1-9]{3,}?)\-\1\-\1\ 은 \([1-9]{3,}?)\-([1-9]{3,}?)\-([1-9]{3,}?)\와 같다.
표현식 \(foo) (bar) \1 \1 \2\ 은 \(foo) (bar) \(foo) \(foo) \(bar)\와 같다.
(?:x)(x)와 같이 하위패턴을 생성하나, 패턴 재사용이 불가능하다.
x(?=y)x의 뒤에 y가 올때만 x와 일치한다.
x(?!y)x의 뒤에 y가 오지 않을 때만 x와 일치한다.
(x|y)x 또는 y와 일치한다.
Character Set
[xyz]문자 세트를 생성한다. -(hypen)을 이용해서 범위를 정할 수도 있다.
/[abcdefg]/ 는 /[a-g]/와 같으며 a,b,c,d,e,f,g 한 글자와 일치 한다.
[^xyz]문자 세트를 생성한다. 단, 해당 문자가 포함되지 않을 때 일치한다.
/[a-g]/는 a,b,c,d,e,f,g가 아닌 다른 문자 1개와 일치한다.
[x-z][xyz]와 같은 표현
[^x-z][^xyz]와 같은 표현
[x-zX-A]x-z범위 사이, 또는 X-A범위 사이의 모든 문자와 일치한다
Meta Character
[\b]Backspace와 일치한다.
\b와는 다른 의미를 가지니 조심할것.
.모든 문자와 일치한다.
\bWord Boundary(단어간의 경계)를 의미한다,
단어간의 경계는 길이가 0 이므로 \b는 언제나 다른 문자와 함께 사용된다.
예) /\bace/ 는 "acehigh"의 ace와 일치하며 /high\b/는 "acehigh"의 high와 일치한다.
단, /eh\b/나 /\beh/는 "acehigh"와 부분적으로 전혀 일치하지 않으며,
이는 "eh"문구가 단어내부에 포함되어 있기 때문이다
\B\b와는 반대로 non-word boundary를 의미한다,
위의 "acehigh"에서 "/\Beh\B/를 사용했을 때 "eh"와 일치하며,
이는 "eh" 앞뒤로 단어가 이어지기 때문이다.
\cX제어문자와 일치한다.
\d0-9 사이의 숫자와 일치한다.
\D0-9 사이의 숫자와 일차하지 않는 모든 문자와 일치한다.
[^0-9]와 같다.
\fform feed와 일치한다.
\nline feed와 일치한다.
\rcarriage return와 일치한다.
\ttab(탭)과 일치한다.
\vVertical Tab(수직 탭)과 일치한다.
\sspace, form feed, line feed, carriage return, tab등의 공간 문자와 일치한다.
\Sspace, form feed, line feed, carriage return, tab, vertical tab등의
공간 문자를 제외한 모든 문자와 일치한다.
\w"_"(underscore)을 포함한 모든 숫자 + 알파벳 문자와 일치한다.
/[A-Za-z0-9_]/와 같다.
\W\w와 일치하지 않는 모든 문자와 일치한다.
\숫자()로 생성되는 하위패턴을 재사용 가능하다.
\0Null 문자와 일치한다.
\xhh2자리 16진수 코드 hh를 가진 문자와 일치한다.
\uhhhh4자리 16진수 코드 hhhh를 가진 문자와 일치한다.


또한 RegExp 패턴 검색 옵션을 정하는 총 3개의 flag가 있다

g : Global Search, 패턴 검색이 처음 일치하는 스트링에서 멈추는 것이 아니라 전체 텍스트에 적용된다.
i : ignore case, 대소문자 관계없이 검색된다
m: multiline mode, 한 줄이 끝나고 다음 줄까지도 검색이 이어진다다다

RegExp 객체 생성하기

RegExp 객체는 2가지 방법으로 생성이 가능하다.

Regular Expression Literal


var expression = /abc/g;
// "/"는 delimiter며 "/"사이에 패턴이 들어가고 그 후에 flag 표시를 하게된다.
// /abc/g는 전체 텍스트에서 "abc" 문자열을 일치한다.

RegExp Constructor


var rep1 = new RegExp('abc', 'g');
var rep2 = new RegExp(/abc/, 'gmi');
// 첫번째 파라미터로 패턴이 들어가며, 두번째 파라미터로 flag가 들어가게 된다
// 단 첫번째 경우처럼 '(single quote)로 패턴 문자열을 구성할 경우에, \(backslash)는 \\로 표현된다.
// 즉 /\d+/ 라는 패턴은 '\\d+'로 적어야 한다.

각각의 RegExp 객체는 총 6가지 프로퍼티를 가지고 있다.

  • global : 표현식이 g flag를 가졌는지를 가리키는 Boolean 값
  • ignoreCase : 표현식이 i flag를 가졌는지를 가리키는 Boolean 값
  • lastIndex : 다음 매칭이 시작될 문자의 위치를 가리키는 Integer 값, 기본값은 언제나 0이다.
  • multiline : 표현식이 m flag를 가졌는지를 가리키는 Boolean 값
  • source : Regexp에 사용된 정규 표현식, 표현식의 delimiter(/) 내부 패턴


var rep = new RegExp('foo.+', 'gi');

console.log(rep.global); //true
console.log(rep.ignoreCase); //true
console.log(rep.lastIndex); //0
console.log(rep.multiline); //false
console.log(rep.source); //foo.+

Regexp.prototype.exec()


exec()메소드는 파라미터로 주어진 문자열을 검색해 해당 표현식에 일치하는 결과값을 Array 형식으로 반환한다.

Parameter : 검색을 실행할 문자열(String)

Return : 표현식과 일치하는 문자열이 존재할 떄 Array형식으로 결과값을 반환하며, 해당 RegExp 인스턴스의 프로퍼티를 업데이트 한다.
반환되는 Array의 첫번째 인덱스에 해당 표현식과 일치하는 문자열이 저장되며, 그 후로 각각의 인덱스에 표현식에서 생성한 하위패턴['(',')'를 이용하는것]에 일치하는 문자열들이 순차적으로 저장되게 된다
표현식과 일치하는 문자열이 존재하지 않는다면, null 값을 반환한다.


var reg = /(java).*(\d{3}).*(fun)/gi

var str = "My JavaSrcipt101 class is fun. Good Class";

var result = reg.exec(str);
console.log(result);

//  reg.exec(str)메소드 실행 후 반환되는 Array 값
//
//  result[0] : 'JavaSrcipt101 class is fun',
//  result[1] : 'Java',
//  result[2] : '101',
//  result[3] : 'fun',
//  result["index"] : 3,
//  result["input"] : 'My JavaSrcipt101 class is fun. Good Class'

반환되는 Array 객체 정보

배열 인덱스(Array Index)저장되는 값예제
[0]표현식에 일치하는 문자열JavaSrcipt101 class is fun'
[1], [2], [3]....[n] 각각의 하위 패턴에 일치하는 문자열[1] : 'Java'
[2] : '101'
[3] : 'fun'
배열 객체 프로퍼티(Property)저장되는 값예제
"index"검색이 실행된 문자열에서
첫번째로 표현식이 일치하는 문자의 위치(최소 0에서 시작한다)
3
"input"검색이 실행된 문자열My JavaSrcipt101 class is fun.'

위의 형식에 일치하는 배열(Array)이 반환되며 exec() 메소드를 실행한 Regex 인스턴스는 아래와 같이 업데이트 된다.

Regex 인스턴스의 프로퍼티(Property)저장되는 값예제
lastIndex다음 검색을 실행할 문자열의 인덱스
일치하는 문자열의 마지막 문자의 인덱스 + 1이 된다.
만약 g 플래그를 사용하지 않는다면 언제나 0이 된다.
29
"fun" 뒤에오는
마침표 문자 (.) 의 인덱스
ignoreCasei 플래그의 사용여부true
globalg 플래그의 사용여부true
multilinem 플래그의 사용여부false
source검색에 사용한 표현식(java).*(\d{3}).*(fun)

RegExp.prototype.test()


test() 메소드는 파라미터로 주어진 문자열에 표현식에 일치하는 문자열이 존재하는지를 확인한 후 그 결과값을 true 또는 false의 Boolean 값으로 반환한다.

Parameter : 검색을 실행할 문자열(String)

Return : 표현식과 일치하는 문자열이 존재할때는 true 값을 반환하며, 아닐시는 false 값을 반환한다


var reg = /[A-Za-z]/;

var str = "My JavaSrcipt101 class is fun. Good Class";

var resultStr = reg.test(str);

console.log(resultStr); //true

var num = "4132";

var resultNum = reg.test(num);

console.log(resultNum); //false

2015년 5월 6일 수요일

[JavaScript] 자바스크립트 프로토타입(Prototype) 객체 이해하기

프로토타입(Prototype)이란?


프로토타입(Prototype), 이름에서 알 수 있듯 프로토타입이란 객체의 기본형인 객체를 뜻한다. 모든 객체는 프로토타입 객체를 가지며 프로토타입의 프로퍼티에 접근이 가능하다. 또한 이 프로토타입 객체도 객체인만큼 포로토타입 객체를 가진다. 즉 가기장 기본 Object 객체를 제외한 모든 객체들은 프로토타입 객체를 가지며 모든 객체의 최상위 프로토타입은 기본 Object.prototype 객체가 된다. 즉, 모든 객체들은 1개의 객체 인스턴스와 해당 인스턴스의 속성들을 공유하며 상속한다는 말이 된다.

이 프로토타입(Prototype)이 중요한 이유는 OOP의 상속 컨셉을 자바스크립트에서 구현할 수 있게하기 때문이다. 만약 객체가 뭔지 모른다면, 우선 이 글(클릭하기)을 읽고 와주기를 바란다.

우선 객체의 프로토타입은 ECMAScript 에서 지원하는 표준 메소드인 Object.getPrototypeOf() 메소드를 통해 접근할 수 있으며, 이 메소드는 프로토타입을 얻어(get)오는 메소드이므로 이메소드를 통해서 프로토타입을 수정하는것은 불가능하다.

*하지만 곧 ECMAScript 6에서 Object.setPrototypeOf() 메소드가 생긴다.

위의 표준 메소드와 별개로 Chrome, Internet Explorer, Mozilla FireFox 등에서는 __proto__따프로퍼티를 이용해 객체의 프로토타입에 접근할 수 있다.


var ob1 = {};
var ob2 = {};

console.log(ob1.__proto__); //Object
console.log(Object.getPrototypeOf(ob2)); //Object
console.log(ob1.__proto__ == Object.getPrototypeOf(ob2)); //true
//기본으로 생성되는 객체는 모두 같은 Object 프로토타입 객체 인스턴스을 참조한다.

여기서 ob1과 ob2 객체에 프로토타입을 설정해준다면


var protoObj = {
  getFirstName: function() {
    return this.firstName;
  }
}
var ob1 = {};
var ob2 = {};
ob1.__proto__ = protoObj;
ob2.__proto__ = protoObj;

ob1.firstName = "Jason";
ob2.firstName = "Natali";
console.log(ob1.getFirstName()); //Jason
console.log(ob2.getFirstName()); //Natali

protoObj.getFirstName = function(){
  return "Function changed";
};

console.log(ob1.getFirstName()); //Function changed
console.log(ob2.getFirstName()); //Function changed
console.log(ob1.__proto__==ob2.__proto__); //true
console.log(protoObj.__proto__ == Object.prototype); //true

이렇게 다른 언어의 객체 상속과 같은 결과를 낳게 된다. 또한 두 객체 모두 같은 프로토타입 객체 인스턴스를 참조하고 있으므로 주어진 프로토타입 객체의 수정은 해당 프로토타입 객체를 참조하는 모든 객체에 적용된다.

위 객체들의 최종 관계도를 UML 다이어그램으로 나타내자면 아래와 같다.

프로토타입 객체 인스턴스와 객체 인스턴스들의 관계도

앞서 말했듯, 모든 사용자 생성 객체는 생성될 때 Object.prototype 객체를 프로토타입으로 가진다.
그리고 ob1과 ob2는 같은 protoObj 인스턴스를 프로토타임으로 가지니, protoObj 인스턴스에 생기는 변화는 obj1과 obj2에 모두 적용된다.

그리고 Object.prototype에는


Object.prototype.constructor
Object.prototype.hasOwnProperty()
Object.prototype.isPrototypeOf()
Object.prototype.propertyIsEnumerable()
Object.prototype.toLocaleString()
Object.prototype.toString()

와 같이 생성되는 객체에 기본으로 탑재되는 메소드들이 프로퍼티로 저장되 있다.

*참고로 Object.prototype 객체 프로퍼티들의 속성은
Writable : false
Enumerable : false
Configurable : false
이니 어떤 방법을 써서도 코드 프로퍼티 리스트나 프로퍼티 수정이 불가능하다.


그렇다면 Object.prototype에서 Object란 무었일까? 이를 이해하기 위해서는 우선 constructor에 대해 알아야하니 모른다면 이 글(클릭하기)을 읽고 오자.

프로토타입과 생성자 함수(Constructor)


우리가 Constructor 라 부르는 함수들은 function 객체로 여러가지 프로퍼티를 가지는데, 그 중 prototype 이라는 프로퍼티가 존재한다. 이 prototype이라는 프로퍼티는 해당 생성자 함수에 의해 성성되는 객체들에 부여되는 프로토타입 객체를 참조한다.

*참고로 이 prototype 프로퍼티는 함수(Function) 객채의 프로퍼티이고, Object.getPrototypeOf 메소드로 얻어지는 프로토타입 객체는 함수(Function) 객체의 프로토타입이다. 자바스크립트에서는 함수도 객체라는것을 잊지 말자


function Foo(){};

var foo = new Foo();
var too = new Foo();

console.log(foo.__proto__ == Foo.prototype); //true
console.log(foo.__proto__ == Object.getPrototypeOf(Foo)); //false
console.log(Object.getPrototypeOf(Foo) == Function.prototype); //true
console.log(foo.__proto__ == too.__proto__); //true
console.log(Foo == Foo.prototype.constructor); //true

함수 Foo의 프로토타입 객체가 바로 Function.prototype 객체이며, 이는 Foo.prototype 프로퍼티가 참조하는 객체와는 다른 객체이다.

그리고 해당 함수의 prototype 프로퍼티가 참조하는 객체의 constructor 프로퍼티는 해당 함수를 참조한다.
관계도를 그린다면 아래와 같이 된다.

Constructor Foo로 생성되는 객체 인스턴스들과 프로토타입의 관계도

생성자 함수 Foo에 의해 생성된 객체 footoo는 모두 함수 Fooprototype 프로퍼티가 참조하는 객체 Foo.prototype을 프로토타입으로 두고 있다. 즉 Foo.prototype의 수정은 footoo 객체 모두에 영향을 끼치게 된다.

*단, 아래의 경우처럼 프로토타입을 새로이 어싸인(Assign)할 경우에는 해당 객체가 참조하는 프로토타입만이 바뀔 뿐, 원 프로는는토타입이나 같은 프로타입을 가진 다른 객체들은 아무런 영향을 받지 않는다.


function Foo() {};

var foo = new Foo();
var too = new Foo();

var newPrototype = new Object();

foo.__proto__ = newPrototype;

console.log(Foo.prototype == foo.__proto__); //false
console.log(Foo.prototype == too.__proto__); //true
console.log(foo.__proto__ == too.__proto__); //false

위의 코드가 실행되면 관계도는 아래처럼 변한다.

생성된 객체에 새로운 프로토타입 어싸인(Assign) 후 관계도

객체 foo에 새로이 어싸인된 프로토타입은 constructor로 기본 Object 함수를 참조하고 있는데, 이는 newPrototype이 사용자에 의해 생성된, 즉 Object() 생성자 함수를 이용해 생성되었기 때문이다.

저 constructor도 프로퍼티 이기 때문에
foo.__proto__.constructor, 또는 newPrototype 객체 생성시 newPrototype.constructor 프로퍼티를 이용해 원하는 생성자 함수를 어싸인해주면 된다.

Property Lookup(프로퍼티 검색)과 프로퍼티 overriding


어떤 객채이든 프로퍼티 검색은 해당 객체의 본체에서부터 시작된다. 아래의 코드를 보자.


var fooProto = {
  write: function() {
    return "write";
  },
  read: function() {
    return "read";
  }
};

function Foo() {
  this.write = function() {
    return "No write";
  }
}
Foo.prototype = fooProto;

foo = new Foo();

console.log(foo.write()); //No write
console.log(foo.read()); //read

too = new Foo();
too.read = function() {
  return "No read";
}

console.log(too.write()); //No write
console.log(too.read()); //No read

delete too.read; //too.read 프로퍼티 삭제

console.log(too.read()); //read

객체의 프로퍼티 호출이 일어나게 되면 자바스크립트 엔진은 가장 먼져 호출이 된 해당 객체(base object)에서 프로퍼티를 검색한다. 그리고, 만약 찾지 못하면 해당 객체의 프로토타입 객체에서 프로퍼티를 검색하고, 그리고도 찾지못다면 프로토타입의 프로토타입 객체까지 검색을 이어간다. 이 검색은 프로퍼티가 발견되거나, 또는 더 이상 검색을 이어갈 프로토타입이 없을 때(Object.prototype까지 도달할 떄)까지 계속 이어진다. 즉 하위 객체에 해당 프로퍼티가 존재한다면 프로퍼티 Overriding이 가능하다는 것이다.

이 때문에 위의 코드에서도 foo.write() 메소드는 Foo함수에서 정의한대로 실행되고, foo.read() 메소드는 Foo함수 생성자의 프로토타입 프로퍼티가 참조하는 fooProto 객체에서 정의한대로 실행되는 것이다. 이는 too.write(), too.read()의 실행결과에서도 잘 알 수 있다.

또 위의 코드를 보면 too.read 프로퍼티를 삭제하게되면 too.read() 메소드가 프로토타입 객체에서 정의한대로 작동한다. 즉, 프로토타입 객체를 직접 호출하지 않고는 하위 객체에서 프로토타입의 프로퍼티를 수정하는 것이 불가능하다.

Object.prototype.hasOwnProperty() 메소드와 in Operator(연산자)


hasOwnProperty() 메소드는 해당 객체에  파라미터로 주어진 이름을 가진 프로퍼티가 있는지를 확인한다.

앞서 말했듯, 자바스크립트 엔진은 프로퍼티 호출이 발생하면, 해당 객체에서 먼져 프로퍼티를 검색한 후, 찾지 못한다면 프로토타입으로 검색을 이어간다. 이 때문에, 특정 프로퍼티가 호출되어도 개발자들은 해당 프로퍼티가 프로토타입에 속한 프로퍼티인지, 하위 객체에 속한 프로퍼티인지 알 수 없게 되는데, hasOwnProperty() 메소드는 그 점을 해결해주는 메소드 이다.

사용법은 간단하다. 확인을 원하는 객체와 프로퍼티 이름으로 메소드를 호출하면 Boolean(true||false) 값을 리턴한다.

hasOwnProperty()와는 달리 in 연산자는 해당 객체에서 간접적으로 접근이 가능한 모든 프로퍼티에 대해 true 값을 반환단다.


var fooProto = {
  a: "Letter A",
  b: "Letter B",
};

function Foo() {
  this.b = "Letter B";
  this.c = "Letter C";
}

Foo.prototype = fooProto;

foo = new Foo();

console.log(foo.hasOwnProperty("a")); //false
console.log("a" in foo); //true
console.log(foo.hasOwnProperty("b")); //true
console.log("b" in foo); //true
console.log(foo.hasOwnProperty("c")); //true
console.log("c" in foo); //true

Object.prototype.isPrototypeOf() 메소드


isPrototypeOf 메소드는 해당 객체가 파라미터로 주어진 객체의 프로토타입인지를 확인하는 메소드 이다.

물론 Object.getPrototypeOf() 메소드 또는 __proto__ 프로퍼티와 '=' 연산자(equal operand)를 이용해서도 확인이 가능하지만, 이 방법을 대체할 수 있게 해주는 메소드 이다.


var fooProto = {
  a: "Letter A",
  b: "Letter B",
};

function Foo() {
  this.b = "Letter B";
  this.c = "Letter C";
}

Foo.prototype = fooProto;

foo = new Foo();

console.log(foo.isPrototypeOf(fooProto)); //false

//아래 3가지 확인법들은 모두 같은 결과를 반환한다.
console.log(fooProto.isPrototypeOf(foo)); //true
console.log(fooProto == Object.getPrototypeOf(foo)); //true
console.log(fooProto == foo.__proto__); //true


2015년 4월 27일 월요일

[JavaScript] 자바스크립트 생성자(Constructor) 함수와 객체

객체(Object)는 Object Literal Notation을 사용해서 생성할 수도 있을 뿐 아니라, Constructor 라는 함수를 이용해서도 생성이 가능하다. 이 Constructor 함수는 Object.constructor 프로퍼티를 이용해 접근이 가능하며, 우리가 일반적으로 new Object() 또는 Object Literal Notation을 이용해 생성한 객체들은 모두 자바스크립트 내장 함수인 Object() 라는 객체 생성자(Constructor)함수를 이용해 생성되는 것 이다.


var person1 = {
  firstName: "Jason",
  lastName: "Bourne",
  age: 25  
};

var person2 = new Object();

console.log(person1.constructor); //function Object() 
console.log(person2.constructor); //function Object() 
console.log(person1.constructor == person2.constructor); //true

이렇듯 person1 객체나 person2 객체 모두 같은 constructor에 의해 생성된다. 또 number, boolean string, array, regex 등도 각각의 constructor에 의해 생성된다.


var array = [];
var number = new Number(3);
var bool = new Boolean(true);
var str = new String("String");

console.log(array.constructor); //function Array()
console.log(number.constructor); //function Number()
console.log(bool.constructor); //function Boolean()
console.log(str.constructor); //function String()

즉 constructor(생성자)는 함수일 뿐이며, constructor 함수는 언제든지 생성될 수 있다는 뜻 이기도 하다.
예를들어, 위의 person 객체의 생성자 함수를 만든다 하면 아래와 같이 만들 수 있다.


function Person(firstName, lastName, age) {
  this.firstName = firstName;
  this.lastName = lastName;
  this.age = age;
  this.getInfo = function() {
    return "Name : " + firstName + " " + lastName + "\nAge : " + age;
  }
}

var person1 = new Person("Jason", "Bourne", 33);
var person2 = new Person("Jenny", "Laurence", 18);

console.log(person1.constructor); //function Person
console.log(person2.constructor); //function Person

new 키워드없이 생성자(Constructor)함수를 호출하게 되면 this 키워드때문에 윈도우 객체에 변수들이 추가되므로 주의하도록 하자(자세한건 this 키워드 관련 글을 참고하기).

2015년 4월 15일 수요일

[JavaScript] 자바스크립트 객체(Object)의 프로퍼티(Property)와 속성(Property Attribute)

예전에 적었던 자바스크립트 객체 소개글에서 말했던것 처럼, 객체란 여러가지 자료(Data)들과 함수(Function)들의 집합이다. 매우 간단하게 선언할 수 있고 접근될 수 있다. 이 글에서는 객체의 프로퍼티(Property)에 대해 이야기 해볼까 한다.

프로퍼티(Property)란 객체에 속한 데이터(Data)를 뜻한다. 아래의 코드에서는 name, age, occupationperson 객체의 프로퍼티가 되겠다.


var person = {
 name: "Jason",
 age: 25,
 occupation: "Student",
 getPersonProfile: function() {
  return "Name : " + this.name +
   "\nAge : " + this.age +
   "\nOccupation : " + this.occupation;
 }
};
person.newProperty = "newProperty";

console.log(person);
// { name: 'Jason',
//   age: 25,
//   occupation: 'Student',
//   getPersonProfile: [Function],
//   newProperty: 'newProperty' }

delete person.newProperty;

console.log(person);
// { name: 'Jason',
//   age: 25,
//   occupation: 'Student',
//   getPersonProfile: [Function] }

console.log(person.getPersonProfile());
// Name : Jason
// Age : 25
// Occupation : Student


위의 코드를 보면 person 객체 내부에 저 값들이 모두 저장된것 같지만, 사실 name, age, occupation 은 모두 값(Data)들이 저장된 메모리 위치를 가리킬 뿐이다. 그러므로, 프로퍼티에 저장된된 Data의 수정, 변경등은 모두 프로퍼티가 가리키는 메모리 위치에서 이루어 지는 일이다.

우리가 흔히 메소드(Method)라 부르는 항목은, 객체 내부에서 프로퍼티로 선언된 함수(Function)을 뜻하며, 위의 코드에서는 returnPersonProfile 프로퍼티가 바로 메소드 이다.

프로퍼티는 객체 선언시점에서와, 선언된 이후 시점 모두에서 추가가 가능하며, delete 키워드를 이용해서 삭제가 가능하다.

프로퍼티는 named data property, named accessor property 2가지 종류로 나뉘지며 named data propertynamed accessor property든, 프로퍼티들은 총 4가지의 속성(Attribute)으로 이루어지는데, 이러한 속성(Attribute)들은 특정 조건에서 프로퍼티의 행동양식(Behavior)을 저장하며, 또한 프로퍼티의 값(Value)를 저장한다.

Data Property와 Accessor Property는 2개의 같은 속성들(Configurable, Enumerable)과 2개의 다른 다른 속성들([Writable, Value], [Get, Set])로 구성된다

Named Data Property(데이터 프로퍼티)


저장된 Data 값에 관련된 프로퍼티이며 Value를 제외한 모든 속성(Attribute)들이 true||false, boolean 값으로 이루어져 있다. 위의 Person 객체에서는 name, age, occupation 프로퍼티들이 데이터 프로퍼티에 속한다
  • Configurable
    • 해당 프로퍼티가 delete 키워드 등으로 재정의(Redefine)될 수 있음을 가리킨다
    • 객체 프로퍼티 생성시 기본값으로 true 값을 가지며 false로 변경할 경우,  해당 프로퍼티 삭제되지 못하며, 이는 해당 프로퍼티의 속성(Attribute)에도 적용된다.
    • 객체 선언시 정의되지 않은 프로퍼티 같은경우에는 기본으로 false 값을 가진다.
  • Enumerable
    • 객체가 for-in 루프를 통과할시, 해당 프로퍼티가 for-in 루프에서 반환되어야(읽혀져야) 할지를 가리킨다.
    • 기본값으로는 true 값을 가진다.
    • 객체 선언시 정의되지 않은 프로퍼티 같은경우에는 기본으로 false 값을 가진다.
  • Writable
    • 프로퍼티의 Data 값이 수정될 수 있음을 가리킨다.
    • 기본값으로는 true 값을 가진다.
  • Value
    • 프로퍼티의 Data 값이다.
    • Value가 정해지지 않은경우에는 undefined값을 가진다.
    • 위의 Person 객체의 name 프로퍼티의 value 값은 "Jason"이다.

Named Accessor Property(접근 프로퍼티)


접근 속성은 프로퍼티의 데이터 값(Value)에 대한 접근에 관련된 속성이다. 데이터를 읽어(Retrieve)오는 Get 속성과 데이터를 저장하는 Set 속성을 가지며, 프로퍼티를 읽어오고 수정하는 기능을 한다. 위의 Person 객체에서는 getPersonProfile 메소드가 바로 Get의 역할을 한다 할 수 있다.

  • Configurable
    • 해당 프로퍼티가 delete 키워드 등으로 재정의(Redefine)될 수 있음을 가리킨다
    • 프로퍼티 생성시 기본값으로 true 값을 가지며 false로 변경할 경우,  해당 프로퍼티 삭제되지 못하며, 이는 해당 프로퍼티의 속성(Attribute)에도 적용된다.
    • 객체 선언시 정의되지 않은 프로퍼티 같은경우에는 기본으로 false 값을 가진다.
  • Enumerable
    • 객체가 for-in 루프를 통과할시, 해당 프로퍼티가 for-in 루프에서 반환되어야(읽혀져야) 할지를 가리킨다.
    • 기본값으로는 true 값을 가진다.
    • 객체 선언시 정의되지 않은 프로퍼티 같은경우에는 기본으로 false 값을 가진다.
  • Get
    • 프로퍼티가 읽어질 때, 호출되는 함수를 정한다. 이 속성을 통해 프로퍼티가 읽어져 반환되기 전에 어떠한 과정을 수행할지를 정할 수 있다.
    • Get 속성에 어싸인된 함수는 Parameter를 받을 수가 없다.
    • 기본값은 undefined.
  • Set
    • 프로퍼티가 수정될 때, 호출되는 함수를 정한다. 이 속성을 통해 프로퍼티가 수정되기 전에 어떠한 과정을 수행할지를 정할 수 있다.
    • Set 속성에 어싸인된 함수는 2개 이상의 Parameter를 받을 수가 없다.
    • 기본값은 undefined.

이러한 속성을 이용해서 아래와 같이 getPersonProfile 메소드를 대체할 수 있다.


var person = {
  name: "Jason",
  age: 25,
  occupation: "Student",
};

Object.defineProperty(person, "profile", {
  get: function() {
    return "Name : " + this.name +
      "\nAge : " + this.age +
      "\nOccupation : " + this.occupation;;
  }
});

console.log(person.profile);
// Name : Jason
// Age : 25
// Occupation : Student


프로퍼티(Property) 속성(Attribute) 정의하기


프로퍼티의 속성은
  • Object.defineProperty()
  • Object.defineProperties()
두가지 메소드를 이용해서 정의가 가능하다. 두 메소드의 차이는 단일 프로퍼티를 정의하는지(defineProperty), 여러가지 프로퍼티를 정의하는지(defineProperties)이다.

Object.defineProperty()

Object.defineProperty() 메소드는 프로퍼티를 소유한 객체, 프로퍼티의 이름, 프로퍼티 속성(Attribute) 정의객체(Descriptor Object), 이렇게 3개의 매개변수(Parameter)를 받는다.

사용법은 아래와 같다.


var person = {
  name: "Jason",
  age: 25,
  occupation: "Student",
  returnPersonProfile: function() {
    return "Name : " + this.name +
      "\nAge : " + this.age +
      "\nOccupation : " + this.occupation;
  }
};

//"name" 프로퍼티 속성을 정의한다
Object.defineProperty(person, "name", {
  configurable: true,
  enumerable: false,
  writable: false,
  value: "Laura"
});

console.log(person.name); //값이 Jason에서 Laura로 변경됬다

//프로퍼티 정의 객체 이기때문에 이렇게 객체를 선언해 사용할 수도 있다
var propertyDescriptor = {
  configurable: false,
  enumerable: false,
  writable: false,
  value: "New Property Value"
};

Object.defineProperty(person, "newProperty", propertyDescriptor);

//프로퍼티를 원하는 속성으로 생성할 수도 있다.
console.log(person.newProperty);

// configurable 을 false로 설정했기 때문에 아래 코드는 에러를 발생시킨다.
delete person.newProperty;
Object.defineProperty(person, "newProperty", {
  enumerable : true
});


Object.defineProperties()


defineProperties()가 여러개의 프로퍼티를 한꺼번에 정의한다. 매개변수로는 프로퍼티를 정의할 객체, 그리고 프로퍼티들의 속성 정의객체.

사용법은 아래와 같다.


var person = {
  name: "Jason",
  age: 25,
  occupation: "Student",
};

Object.defineProperties(person, {
  name: {
    configurable: false,
    enumerable: true,
    writable: false,
    value: "Laura"
  },

  profile: {
    get: function() {
      return "Name : " + this.name +
        "\nAge : " + this.age +
        "\nOccupation : " + this.occupation;;
    }
  }
});

console.log(person.profile);
// Name : Laura
// Age : 25
// Occupation : Student


프로퍼티 속성 읽어오기


프로터티의 속성은 Object.getOwnPropertyDescriptor() 메소드를 이용해 읽어올 수 있다. 매개변수로는 프로퍼티가 속한 객체와, 프로퍼티의 이름을 받는다.


var person = {
  name: "Jason",
  age: 25,
  occupation: "Student",
};

Object.defineProperty(person, "profile", {
  get: function() {
    return "Name : " + this.name +
      "\nAge : " + this.age +
      "\nOccupation : " + this.occupation;;
  }
});

var propertyDescriptor_name = Object.getOwnPropertyDescriptor(person, "name");

console.log(propertyDescriptor_name);
// { value: 'Jason',
//   writable: true,
//   enumerable: true,
//   configurable: true }

var propertyDescriptor_age = Object.getOwnPropertyDescriptor(person, "age");
console.log(propertyDescriptor_age);
// { value: 25,
//   writable: true,
//   enumerable: true,
//   configurable: true }


var propertyDescriptor_occupation = Object.getOwnPropertyDescriptor(person, "occupation");
console.log(propertyDescriptor_occupation);
// { value: 'Student',
//   writable: true,
//   enumerable: true,
//   configurable: true }

var propertyDescriptor_Profile = Object.getOwnPropertyDescriptor(person, "profile");
console.log(propertyDescriptor_Profile);
// { get: [Function],
//   set: undefined,
//   enumerable: false,
//   configurable: false }
// 객체 선언시 내부에서 정의된 프로퍼티가 아니기 때문에 configurable 과 enumerable 값이 모두 false 이다.