2018. 03. 23
IT 개발2팀 UI파트 - 김재호
초창기 웹페이지의 보조적인 기능을 수행하기 위한 용도로 사용
브라우저에서만 사용하는 용도를 벗어나 다양한 환경에서 폭넓게 활용
최적화된 웹 성능에 모바일의 Native 기능을 결합한 최신 웹 앱
index.html
<!-- internal -->
<script type="text/javascript">
//코드 작성
//한줄주석
/* 여러줄 주석 */
</script>
<!-- external -->
<script type="text/javascript" scr="js/ui/ui-base.js"></script>
index.html
<!DOCTYPE html>
<html>
<head>
<title>HTML Page</title>
//Bad
<script scr="js/lib/jquery-1.9.1.min.js"></script>
<script scr="js/ui/ui-base.js"></script>
</head>
<body>
...
//Good
//body 요소 안, 맨 마지막 위치
<script scr="js/lib/jquery-1.9.1.min.js"></script>
<script scr="js/ui/ui-base.js"></script>
</body>
</html>
<script src="script.js">
<script async src="script.js">
<script defer src="script.js">
var number;
var str;
//한번에 선언 (콤마를 이용해 구분)
var number, str;
//숫자시작 X
var 1st = 10; //SyntaxError
//대소문자 구분, 완전히 다른변수
var value = 25;
var Value = 26;
//예약어 X
var function; //SyntaxError
변수의 중복 선언은 문법적으로 허용, 하지만 사용하지 않는 것이 좋음
var x = 1;
console.log(x); //1
//중복 선언 (재정의)
var x = 100;
console.log(x); //100
의도하지 않게 변수를 전역화할 수 있으므로 사용하지 않는 것이 좋음
x = 1;
console.log(x); //1
function myFunction() {
console.log(foo); //① undefined
//Why no ReferenceError? 예기치 않은 결과
if(true) {
var foo = 123;
console.log(foo); //② 123
}
}
myFunction();
코드를 해석하기 전 var 선언 범위를 각 함수(스코프)의 상단으로 끌어 올림
function myFunction() {
//var foo; -> 변수는 호이스트 되었습니다.
console.log(foo); //① undefined
if(true) {
var foo = 123;
console.log(foo); //② 123
}
}
myFunction();
//값을 할당하지 않은 변수는 기본적으로 undefined
//자료형이 결정되지 않은 상태
var foo;
console.log(typeof foo); //undefined
//null은 개발자가 의도적으로 빈 값을 할당 (비어있는 변수)
var foo = 'Kim';
foo = null; //참조 정보가 제거됨
foo = 3;
console.log(typeof foo); //number
foo = 'Hi';
console.log(typeof foo); //string
foo = true;
console.log(typeof foo); //boolean
숫자를 다루다 보면 숫자 범위를 초과하거나 오류로 NaN
NaN (Not a Number) 숫자가 아니라는 뜻
변수의 데이터 타입과 용도에 따라 접두사와 함께 붙여 사용 (헝가리안 표기법)
접두사 | 데이터 타입 | 예시 |
---|---|---|
a | Array | aUsers |
s | String | sCompanyName |
n | Number | nTotalCount |
b | Boolean | bFlag, bActivation |
o | Object | oController |
ht | Hash Table | htUserInfo, htColorCode |
d | Date | dToday, dCurrentTime |
el | Html Element | elTableList |
rx | Regular Expression | rxEmailFormat |
0 == '' //true
0 == '0' //true
1 == true //true
null == undefined //true
0 === '' //false
0 === false //false
1 === true //false
null === undefined //false
여러 개의 데이터 값을 하나의 변수에 할당할 때 사용
var arr1 = []; //배열 리터럴 사용
var arr2 = new Array();
//배열 요소에 index로 값 할당
var fruits = [];
fruits[0] = '사과';
fruits[1] = '배';
fruits[2] = '바나나';
//배열을 생성과 동시에 요소에 값 할당
var fruits = ['사과', '배', '바나나'];
var arr = [];
arr[0] = 'one';
arr[3] = 'three';
arr[7] = 'seven';
console.log(arr); //["one", empty × 2, "three", empty × 2, "seven"]
배열 뒤 [ ]안에 index를 넣어 요소에 접근
var fruits = ["사과", "배", "바나나"];
console.log(fruits[0]); //사과
console.log(fruits[1]); //배
console.log(fruits[2]); //바나나
//요소 갯수 (배열 길이)
console.log(fruits.length); //3
//for문 사용
for (var i = 0; i < fruits.length; i++) {
console.log(i, fruits[i]); //0 사과, 1 배, 2 바나나
}
Object | Properties | Methods |
---|---|---|
![]() |
car.name = 제네시스 | car.start() |
car.model = G70 | car.stop() | |
car.weight = 1,695kg | car.speedUp() | |
car.color = white | car.slowDown() |
var car = {}; //객체 리터럴 사용
var car = new Object();
//객체 생성 후 프로퍼티를 추가
var car = {};
car.name = '제네시스';
car.model = 'G70';
car.weight = '1,695kg';
car.color = 'white';
//객체 생성과 동시에 프로퍼티를 추가
var car = {
name: '제네시스',
model: 'G70',
weight: '1,695kg',
color: 'white'
};
console.log(car.name); //제네시스
메서드 내부의 this는 해당 메소드를 호출한 객체에 바인딩
var car = {
name: '제네시스',
model: 'G70',
weight: '1,695kg',
getName: function() {
return this.name;
}
};
var newCar = {
name: 'BMW'
};
newCar.getName = car.getName;
car.getName(); //this는 car, this.name -> 제네시스
newCar.getName(); //this는 newCar, this.name -> BMW
주로 반복적으로 사용되는 구문을 미리 작성해 두고 필요할 때 호출
function 함수이름(parameter1, parameter2,...) {
//code
}
function total(a, b) {
return a + b;
}
//함수 호출
total(10, 20); //30
함수표현식으로 정의한 함수는 함수명 생략 가능
// 익명 함수표현식
var total = function(a, b) {
return a + b;
};
//이전과 동일하게 호출
total(10, 20); //30
//함수선언식
foo(); //hello (함수 호출을 먼저 하고, 함수 정의는 나중에 정의하는 -_-;)
function foo() {
console.log('hello');
}
//함수표현식
foo(); //TypeError: foo is not a function
var foo = function() {
console.log('hello');
};
foo(); //hello
// 익명 즉시실행함수(immediately-invoked function expression)
(function () {
//code
}());
어떤 이벤트가 발생한 후 수행될 함수 (이벤트 핸들러, 비동기 처리)
<button id="myButton">Click me</button>
<script>
var button = document.getElementById('myButton');
button.addEventListener('click', function() {
//콜백 함수
});
$.ajax({
//..
success: function() {
//콜백 함수
},
error: function() {
//콜백 함수
}
});
</script>
//Global Scope
var global = 'global'; //전역변수
function foo() {
console.log(global); //global
}
foo();
var로 선언된 변수는 Block-Level Scope를 사용하지 않으므로 x는 전역 변수
//Global Scope
if (true) {
var x = 5; //전역변수
}
console.log(x); //5
function foo() {
//Local Scope
var local = 'local'; //지역변수
console.log(local); //local
}
foo();
console.log(local); //ReferenceError: local is not defined
var global = 'global';
function foo() {
var global = 'local';
console.log(global); //local
}
foo();
console.log(global); //global
변수를 찾을 때 스코프 체인을 거슬러 올라가며 추적
스코프 체인에서 가장 처음 발견한 변수의 값 반환
var number1 = 1;
//③ func1 부모함수가 없기 때문에 전역 스코프에서 값을 찾음
function func1(){ //② 여기에도 없으면
var number2 = 2;
function func2(){
var number3 = 3;
//① 지역 스코프에서 number1, number2, number3 변수를 찾음
//값이 없으면 부모 함수인 func1에서 검색
console.log(number1 + number2 + number3); //6
}
func2();
}
func1();
function add() {
var counter = 0; //counter는 외부에서 접근할 수 없는 비공개 멤버 (private)
return function() { //반환된 객체를 통해 외부에 공개
return ++counter;
};
}
var foo = add(); //인스턴스 foo 생성
foo(); //1
foo(); //2
var bar = add(); //인스턴스 bar 생성 (새롭게 반환되는 인스턴스로 counter 초기화)
bar(); //1
bar(); //2
전역 스코프에서 변수 x를 찾고 존재하지 않으면
변수 x를 암묵적으로 전역변수로 선언
function foo() {
x = 1; //var 생략 시 전역변수
var y = 2;
}
foo();
console.log(x); //1
//x.js
function foo (){
// var i = 0;
i = 0;
}
//y.js
for(var i = 0; i < 5; i++){
foo();
console.log(i); //무한 루프
}
<!DOCTYPE html>
<html>
<body>
<script scr="x.js"></script>
<script scr="y.js"></script>
</body>
</html>
전역변수 사용으로 인해 잠재적인 오류가 발생 할 수 있음
다음과 같이 전역변수 객체 하나를 만들어 사용 (네임스페이스)
//기존에 동일한 이름을 갖는 네임스페이스가 존재하는지 검사
//동일한 이름을 갖는 네임스페이스가 없을 경우에만 네임스페이스를 정의
if(typeof MYAPP === 'undefined') {
var MYAPP = {};
}
//위 코드를 간소화 하면 아래와 같이 작성
var MYAPP = MYAPP || {};
MYAPP.student = {
name: 'Kim',
gender: 'male'
};
console.log(MYAPP.student.name); //Kim
(function() {
var MYAPP = MYAPP || {};
MYAPP.student = {
name: 'Kim',
gender: 'male'
};
console.log(MYAPP.student.name);
//code...
}());
console.log(MYAPP.student.name); //ReferenceError: MYAPP is not defined
자바스크립트의 모든 객체는 자신의 부모 역할을 하는 객체와 연결 (__proto__ 숨은링크)
부모 객체의 프로퍼티, 메소드를 상속받아 사용 가능
자기 자신을 생성하기 위해 사용된 객체 원형을 프로토타입
객체 리터럴 방식으로 생성된 객체의 경우 부모는 Object.prototype
Object.prototype은 자바스크립트 모든 객체의 최상위 부모
student 객체는 __proto__ 라는 숨겨진 내부 프로퍼티에
자신의 부모 Object.prototype 객체와 연결
var student = {
name: 'Kim',
score: 90
};
console.log(student.valueOf()); //Object {name: "Kim", score: 90}
배열 역시 __proto__ 라는 숨겨진 내부 프로퍼티에 자신의 부모 Array.prototype 객체와 연결
모든 배열에 존재하는 length, push(), pop().. 등과 같은 메서드들을 상속
var myArray = [];
console.log(myArray.length); //0
__proto__ 라는 숨겨진 내부 프로퍼티에 자신의 부모 Function.prototype 객체와 연결
함수는 prototype 속성을 이용해 또 다른 객체의 원형이 될 수 있음
function sum(x, y) { ... }
프로토타입은 객체를 확장하고 객체 지향적인 프로그래밍이 가능
var Person = (function() {
//생성자 함수 Constructor
function Person(name) {
this._name = name;
}
//sayHi()함수를 하나만 만들어
//객체의 인스턴스들이 재사용을 위해서 프로토타입 안에 정의
Person.prototype.sayHi = function () {
console.log('Hi! ' + this._name);
};
return Person;
}());
var me = new Person('Kim'); //new 연산자를 통해 인스턴스를 me 생성
me.sayHi(); //Hi! Kim
var you = new Person('Lee'); //인스턴스 you 생성
you.sayHi(); //Hi! Lee
자바스크립트의 모든 객체는 Object.prototype을 기반으로 확장
이런 구조로 객체를 확장하는 방식을 프로토타입 기반 프로그래밍
아직 은퇴하지 않은 현역 입니다.
2018년 기준 상위 100만개의 웹 페이지중 74%가 jQuery 사용
index.html
<body>
//...
<script scr="js/lib/jquery-1.9.1.min.js"></script>
<script scr="js/app.js"></script>
</body>
app.js
$(document).ready(function(){
//DOM이 완전히 로드되기 전까지 대기하다가 로드가 완료되면 실행
});
//OR
$(function(){
// Do something...
});
여러개의 요소 선택, li 요소를 선택하고 이 요소들의 텍스트를 일괄 변경
index.html
<ul id="navigation">
<li>신세계몰</li>
<li>이마트몰</li>
<li class="howdy">하우디</li>
</ul>
app.js
$(function(){
console.log($('li'));
$('li').text('SSG');
});
app.js
var targets = document.getElementsByTagName('li');
for(var i = 0; i < targets.length; i++){
// text노드를 선택한 후, text를 변경
targets[i].firstChild.nodeValue = 'SSG';
}
가능하면 ID selector를 사용
document.getElementById()를 사용하기 때문에 ID selector가 빠름
app.js
//Tag Selector
$('li');
//ID Selector
$('#container');
//Class Selector
$('.articles');
index.html
<ul id="navigation">
<li>신세계몰</li>
<li>이마트몰</li>
<li class="howdy">하우디</li>
</ul>
app.js
$('#navigation').find('.howdy').css('color', 'red');
DOM에 새로운 요소를 추가/삭제, 복사, 속성 변경
index.html
<div class="item">
<h3 class="name">미네랄 워터 생수</h3>
<button>가격보기</button>
</div>
app.js
//append() 새로운 요소 추가
var price = $('<span>할인적용가 2,520</span>');
$('.item').append(price);
//remove() 요소 제거
$('button').remove();
DOM에 콘텐츠를 삽입할 때, 위치를 지정
index.html
[before]
<div class="item">
[prepend]
<h3 class="name">미네랄 워터 생수</h3>
<button>가격보기</button>
[append]
</div>
[after]
app.js
$('.item').prepend(price); //선택 요소의 여는 태그 뒤
$('.item').append(price); //선택 요소의 닫는 태그 앞
$('.item').before(price); //선택 요소의 앞
$('.item').after(price); //선택 요소의 뒤
이벤트를 바인딩하고 해당 이벤트가 발생했을 때 실행될 콜백 함수를 지정
버튼을 클릭하면 가격 보기, 버튼 삭제하기
index.html
<div class="item">
<h3 class="name">미네랄 워터 생수</h3>
<button>가격보기</button>
</div>
app.js
$('button').on('click', function (e) {
var price = $('<span>할인적용가 2,520</span>');
$('.item').append(price);
$('button').remove();
});
app.js
//$(this)는 $(e.target)
//closest() 메서드는 가장 근접한 상위 요소를 반환
$('button').on('click', function (e) {
var price = $('<span>할인적용가 2,520</span>');
$(this).closest('.item').append(price);
$(this).remove();
});
기존 기능을 확장할 수 있게 도와주는 프로그램
자주 사용되는 기능들을 플러그인으로 제작해서 사용
index.html
<div id="horizontal" class="slide_wrapper">
<div class="slide_container">
<div class="slide"><img src="http://static.ssgcdn.com/ui/sm/img/promotion/2016/160104_shopat/mobile/m_sa_01.jpg" alt=""></div>
<div class="slide"><img src="http://static.ssgcdn.com/ui/sm/img/promotion/2016/160104_shopat/mobile/m_sa_02.jpg" alt=""></div>
<div class="slide"><img src="http://static.ssgcdn.com/ui/sm/img/promotion/2016/160104_shopat/mobile/m_sa_03.jpg" alt=""></div>
<div class="slide"><img src="http://static.ssgcdn.com/ui/sm/img/promotion/2016/160104_shopat/mobile/m_sa_04.jpg" alt=""></div>
<div class="slide"><img src="http://static.ssgcdn.com/ui/sm/img/promotion/2016/160104_shopat/mobile/m_sa_05.jpg" alt=""></div>
<div class="slide"><img src="http://static.ssgcdn.com/ui/sm/img/promotion/2016/160104_shopat/mobile/m_sa_06.jpg" alt=""></div>
<div class="slide"><img src="http://static.ssgcdn.com/ui/sm/img/promotion/2016/160104_shopat/mobile/m_sa_07.jpg" alt=""></div>
</div>
<button class="button_prev slide_button"><span class="blind">이전</span></button>
<button class="button_next slide_button"><span class="blind">다음</span></button>
</div>
index.html
<body>
//...
<script scr="http://static.ssgcdn.com/ui/ssg/js/lib/jquery-1.9.1.min.js"></script>
<script src="http://static.ssgcdn.com/ui/ssg/js/lib/jquery.bxslider.min.js"></script>
</body>
app.js
var welHorizontal = $('#horizontal');
welHorizontal.find('.slide_container').bxSlider();
app.js
var welHorizontal = $('#horizontal');
welHorizontal.find('.slide_container').bxSlider({
mode: 'horizontal', //슬라이드 모드
controls: false, //슬라이드 컨트롤 사용 여부
pager: false, //슬라이드 페이지
infiniteLoop: false //반복 재생
});
app.js
var welHorizontal = $('#horizontal'),
welHorizPrev = welHorizontal.find('.button_prev'),
welHorizNext = welHorizontal.find('.button_next');
welHorizPrev.on('click', function() {
alert('처음 슬라이드 입니다.');
});
welHorizNext.on('click', function() {
alert('마지막 슬라이드 입니다.');
});
app.js
var oHorizontal = welHorizontal.find('.slide_container').bxSlider({
mode: 'horizontal',
controls: false,
pager: false,
infiniteLoop: false
});
oHorizontal.getSlideCount() //전체 슬라이드
oHorizontal.getCurrentSlide() //현재 활성화 슬라이드
oHorizontal.goToPrevSlide() //이전 슬라이드
oHorizontal.goToNextSlide() //다음 슬라이드
app.js
welHorizPrev.on('click', function() {
var curSlideIndex = oHorizontal.getCurrentSlide();
if(curSlideIndex === 0){
alert('처음 슬라이드 입니다.');
} else {
oHorizontal.goToPrevSlide();
}
});
welHorizNext.on('click', function() {
var curSlideIndex = oHorizontal.getCurrentSlide(),
lastSlideIndex = oHorizontal.getSlideCount() - 1;
if(curSlideIndex === lastSlideIndex){
alert('마지막 슬라이드 입니다.');
} else {
oHorizontal.goToNextSlide();
}
});
app.js
var welVertical = $('#vertical'),
welVerticalPrev = welVertical.find('.button_top'),
welVerticalNext = welVertical.find('.button_bottom');
var oVertical = welVertical.find('.slide_container').bxSlider({
mode: 'vertical',
controls: false,
pager: false,
infiniteLoop: false
});
app.js
welVerticalPrev.on('click', function() {
var curSlideIndex = oVertical.getCurrentSlide();
if(curSlideIndex === 0){
alert('처음 슬라이드 입니다.');
} else {
oVertical.goToPrevSlide();
}
});
welVerticalNext.on('click', function() {
var curSlideIndex = oVertical.getCurrentSlide(),
lastSlideIndex = oVertical.getSlideCount() - 1;
if(curSlideIndex === lastSlideIndex){
alert('마지막 슬라이드 입니다.');
} else {
oVertical.goToNextSlide();
}
});