본문 바로가기
개발/날씨의 아이

[javascript] 한 페이지 요약(전공자 기준)

by 박영귤 2023. 9. 7.

react native를 배우기 위해 선수로 javascript를 배우기로 하였다. 이 한 페이지에 js에 대해 배운 모든 내용을 담을 것이다. 생활코딩 선생님의 js강의를 듣기로 결정하였다.

https://www.youtube.com/watch?v=PZIPsKgWJiw&list=PLuHgQVnccGMA4uSig3hCjl7wTDeyIeZVU&index=1 

https://opentutorials.org/course/743

이미 알고 있는 내용은 적지 않고 꼭 필요한 내용만 적을 것이다.

9.7 시작 ~ 9.10 끝


웹 서버 역할

이렇게 js는 웹서버 역할도 할 수 있다.

node.js는 서버, 웹브라우저는 클라이언트

환경마다 다른 함수

웹 브라우저에서 경고창을 띄우기 위해서는 alert, node.js로 웹서버를 열어 그 곳에 메시지를 출력하기 위해서는 write, 구글의 spread sheet를 사용해 메시지를 띄우기 위해서는 msgBox라는 함수를 사용한다. 이렇듯 같은 기능이지만 환경이 달라서 다른 함수를 사용하곤 한다.

변수 선언

var라는 예약어를 사용함.

var a = 1; -> a라는 변수에 1 할당

비교 연산 '==', '==='

==은 자료형이 같지 않아도 알아서 비교해주는 연산, ===은 자료형을 포함해 완전히 같은지 비교해주는 연산

==은 null과 undefined를 구분하지 못한다.

(참고 : undefined은 변수를 선언하고 값을 할당하지 않은 상태, null은 변수를 선언하고 빈 값을 할당한 상태(빈 객체)이다. 즉, undefined는 자료형이 없는 상태이다.)

for문 문법
for(var i = 0; i < 10; i++){
    document.write('coding everybody'+i+'<br />');
}

c언어와 유사하다.

break와 continue도 사용 가능하다.

함수 문법
function get_argument(arg){
    return arg;
}
 
alert(get_argument(1));
alert(get_argument(2));

이렇게 function이라는 예약어를 사용하면 된다. 파라미터도 그냥 괄호 안에 넣으면 된다. 이 부분도 c언어랑 유사하다.

var get_argument = function (arg) {
    return arg;
}

alert(get_argument(1));
alert(get_argument(2));

아래는 함수를 정의하는 또 다른 방법이다. 변수에 담아두는 형식으로 정의할 수도 있다. 사용방법은 동일하다.

배열 문법
var member = ['egoing', 'k8805', 'sorialgi']
alert(member[0]);
alert(member[1]);
alert(member[2]);

배열을 정의하는 방법은 파이썬과 유사하다.

배열은 length라는 속성를 가지고 있다.

var member = ['egoing', 'k8805', 'sorialgi']

for(i = 0; i < member.length; i++){
    document.write(member[i].toUpperCase());
    document.write('<br />');
}

이렇게 반복문과 사용할 수 있다.

배열에서 제공하는 기본적인 함수가 여럿 있다. 아래에 코드를 참고하자.

var li = ['a', 'b', 'c', 'd', 'e'];
li.push('f');
console.log(li); // [ 'a', 'b', 'c', 'd', 'e', 'f' ]

var li = ['a', 'b', 'c', 'd', 'e'];
li = li.concat(['f', 'g']);
console.log(li); // [ 'a', 'b', 'c', 'd', 'e', 'f', 'g' ]

var li = ['a', 'b', 'c', 'd', 'e'];
li.unshift('z');
console.log(li); // [ 'z', 'a', 'b', 'c', 'd', 'e' ]

var li = ['a', 'b', 'c', 'd', 'e'];
li.splice(2, 2, 'B'); // 첫 번째 인자 인덱스부터 두 번째 인자 만큼(개수) 제거, 그 뒤에 세 번째 인자를 추가
console.log(li); // [ 'a', 'b', 'B', 'e' ]

var li = ['a', 'b', 'c', 'd', 'e'];
li.shift();
console.log(li); // [ 'b', 'c', 'd', 'e' ]

var li = ['a', 'b', 'c', 'd', 'e'];
li.pop();
console.log(li); // [ 'a', 'b', 'c', 'd' ]


var li = ['c', 'e', 'a', 'b', 'd'];
li.sort();
console.log(li); // [ 'a', 'b', 'c', 'd', 'e' ]

var li = ['c', 'e', 'a', 'b', 'd'];
li.reverse();
console.log(li); // [ 'd', 'b', 'a', 'e', 'c' ]
객체 생성
// 1
var grades = {'egoing': 10, 'k8805': 6, 'sorialgi': 80};
// 2
var grades = {};
grades['egoing'] = 10;
// 3
var grades = new Object();
grades['egoing'] = 10;
// 이렇게 .으로 key에 들어있는 value에 접근할 수 있다.
alert(grades.sorialgi);
객체 반복문 접근
var grades = {'egoing': 10, 'k8805': 6, 'sorialgi': 80};
for(key in grades) {
    document.write("key : "+key+" value : "+grades[key]+"<br />");
}

배열도 마찬가지 방법으로 in을 사용하면 인덱스 정보를 담게 된다.

객체의 value값의 형태

value에 또 다른 객체도 담길 수 있고, 함수도 담길 수 있음.

var grades = {
    'list': {'egoing': 10, 'k8805': 6, 'sorialgi': 80},
    'show' : function(){
        for(var name in this.list){
            document.write(name+':'+this.list[name]+"<br />");
        }
    }
};
grades.show(); // grades['show'](); 로도 가능
모듈

코드의 재사용성을 높이기 위해 필요함.

자바스크립트에서는 모듈 자체를 지원하는 건 아니지만, 서로 다른 개발 환경에서 각각이 모듈을 지원한다.

웹 브라우저에서는 위처럼 다른 파일을 가져올 수 있다.

node.js에서 모듈을 로드하는 방법이다.

 

개발자 도구의 네트워크 탭에서 로드한 js파일이 있으면 볼 수 있다.

UI, API

정규표현식

https://opentutorials.org/module/532/6580

js에서 정규표현식을 사용하는 법을 배우고 싶다면 위 링크로 이동하면 된다.

이번 프로젝트에서는 사용하지 않을 것이기 때문에 패스하고, 필요할 때 다시 시청할 것이다.

전역변수를 사용하지 않기 위해서
// 1. 한 개의 전역변수 안에 다른 변수를 모두 넣는다.
MYAPP = {}
MYAPP.calculator = {
    'left' : null,
    'right' : null
}
MYAPP.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());
// 2. 함수로 감싸고, 바로 실행시키게끔 마지막에 ()를 붙여준다.
// 이렇게 하면 함수조차도 전역변수가 아니기 때문에, 전역변수 0개를 사용한 것이라고 볼 수 있다.
// 전체를 또 한번 괄호로 감싸야한다. 그렇지 않으면 실행이 안된다.
(function(){
    var MYAPP = {}
    MYAPP.calculator = {
        'left' : null,
        'right' : null
    }
    MYAPP.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());
}())
지역변수 유효범위

자바스크립트는 함수에 대한 유효범위만을 제공한다. 많은 언어들이 블록(대체로 {,})에 대한 유효범위를 제공하는 것과 다른 점이다.

함수는 값이다.
// a라는 변수 안에 함수가 담겨있는 것.
function a(){}

// 함수가 파라미터로 전달될 수 있음.
function cal(func, num){
    return func(num)
}
function increase(num){
    return num+1
}
function decrease(num){
    return num-1
}
alert(cal(increase, 1)); // 2
alert(cal(decrease, 1)); // 0
콜백 - 함수가 받는 인자가 함수인 것

// sort의 인자로 함수를 주게 되면 함수의 동작을 바꿀 수 있음. 처리의 위임.
function sortNumber(a,b){
    return b-a;
}
var numbers = [20, 10, 9,8,7,6,5,4,3,2,1];
alert(numbers.sort(sortNumber)); // array, [20,10,9,8,7,6,5,4,3,2,1]


(참고 : js의 sort는 string으로 정렬함)

ajax - 비동기 처리

웹페이지가 변경되지 않고, 서버와 네트워크가 조용히 연결해주는 기능.

웹페이지가 단순히 문서에서, 어플리케이션으로 변할 수 있게 해주는 기능이다.

어떤 함수를 호출하더라도 다른 기능을 사용할 수 있게끔 (백그라운드에서 처리하게끔) 해준다.

비동기 콜백

$.get이라는 것은 비동기적으로 json형태의 데이터 파일을 가져오고, 그 데이터를 이용해 무언가를 하겠다는 이야기이다. 하지만, 아래처럼 작성하면 undefined가 출력된다.

<script type="text/javascript">
	var data;
    $.get('./datasource.json.js', function(result){
        data = result;
    }, 'json');
    console.log(data); // undefined
</script>

왜냐하면 데이터를 읽어오라고 시켜놓고 읽어올 동안 출력을 시도했기 때문이다. 이런 경우는 읽어온 후에 출력을 하도록 해야한다. 아래처럼 고치면 해결할 수 있다.

<script type="text/javascript">
    $.get('./datasource.json.js', function(result){
        console.log(result); // 읽은 데이터가 출력됨.
    }, 'json');
</script>
클로저

클로저는 중급 이상의 개발자에겐 필수적으로 필요한 내용이다. 이번엔 처음 들어서 어느정도 이해했지만, 나중에 잊어버릴 수도 있으니 반복학습을 할 계획이다.

https://opentutorials.org/module/532/6544

 

외부함수와 내부함수에 관련된 용어이다. 어떤 함수 내에서만 사용하는 함수라면, 그 함수 안에 함수를 정의해서 사용할 수 있다. 그럴 때는 내부함수에서 외부함수의 지역변수를 사용할 수 있다.

function outter(){
    var title = 'a';  
    function inner(){        
        alert(title);
    }
    inner();
}
outter(); // a

외부함수가 이미 종료되었어도 외부함수의 지역변수에 접근이 가능하다. 아래 코드를 보자.

function outter(){
    var title = 'a';  
    return function(){        
        alert(title);
    }
}
inner = outter();
inner(); // a

이미 outter는 리턴되어 종료되었음에도 불구하고, outter의 title이란 변수에 접근을 하는 모습이다.

클로저란 내부함수가 외부함수의 지역변수에 접근 할 수 있고, 외부함수는 외부함수의 지역변수를 사용하는 내부함수가 소멸될 때까지 소멸되지 않는 특성을 의미한다.

var arr = []
for(var i = 0; i < 5; i++){
    arr[i] = function(){
        return i; // 생성된 다섯 개의 함수가 같은 변수를 가리키게끔 함.
    }
}
for(var index in arr) {
    console.log(arr[index]()); // 5 5 5 5 5
}
    var arr = []
    for(var i = 0; i < 5; i++){
        arr[i] =  function(id){
            return function(){
                return id; // 이렇게 외부 함수의 지역변수를 가리키게 해야함.
            }
        }(i); 
    }
    for(var index in arr) {
        console.log(arr[index]()); // 0 1 2 3 4
    }
arguments

함수의 정의부분에 매개변수가 없거나 1개만 있어도, 두개, 세개의 매게변수를 전달할 수 있다. 아래가 그 예시이다.

function sum(){
    var i, _sum = 0;    
    for(i = 0; i < arguments.length; i++){
        _sum += arguments[i];
    }   
    return _sum;
}
document.write('result : ' + sum(1,2,3,4));

그렇게 들어온 매개변수에는 arguments라는 변수를 이용해서 접근할 수 있다.

arguments.length를 사용해서 전달된 인자의 개수를 구할 수 있다. 함수이름.length를 사용해서는, 함수에 정의된 파라미터의 개수를 구할 수 있다.

apply

함수를 호출하는 새로운 방법이다. 함수.apply(인자)의 인자로 무언가를 주게 되면 그 인자는 그 함수의 this에 저장되게 된다. 아래의 예시를 보자.

function sum(){
    var _sum = 0;
    for(name in this){
        _sum += this[name];
    }
    return _sum;
}

o1 = {val1:1, val2:2, val3:3}
o2 = {v1:10, v2:50, v3:100, v4:25}
alert(sum.apply(o1)) // 6
alert(sum.apply(o2)) // 185

만약 함수가 받는 매개변수가 있다면, apply의 두 번째 인자로 그 함수에 전달할 인자들의 배열을 넣어주면 된다. 아래의 예시를 보자.

function sum(arg1, arg2){
    return arg1+arg2;
}
alert(sum.apply(null, [1,2]))
객체지향
생성자
function Person(){}
var p1 = Person();
var p2 = new Person();

console.log(p1); // undefined
console.log(p2); // Person {}

js에는 클래스가 없다. 함수를 실행할 때 앞에 new를 붙여주면 그게 객체를 생성한다는 의미이다.

function Person(name){
    this.name = name;
    this.introduce = function(){
        return 'My name is '+this.name; 
    }   
}

var p1 = new Person("yk");
p1.introduce(); // My name is yk

함수 안에 this를 사용한다면 프로퍼티나 메소드를 정의할 수 있다. 다른 언어에서와는 다르게 클래스 자체는 정의되어있지 않지만, 함수로 클래스를 구현하여 사용한다는 것은 비슷하다.

new를 사용하여 객체를 생성한다면 Person이라는 함수가 처음부터 끝까지 실행된다. 이것이 다른 언어에서의 생성자라고 불리는 것이다.

전역객체

js에는 전역객체라는 것이 있다. 개발자가 정의하는 함수나, 변수 등이 모두 전역객체에 추가가 되는 것이다. 개발환경에 따라 전역객체의 이름이 다른데 웹브라우저에서는 window라는 전역객체가 있다.

window.p1.introduce()는 위에서 p1.introduce()와 같은 것이다.

this

콘솔창에서 this는 전역객체이다. console.log(this)를 츨력하면 window가 출력된다.

apply
var o = {}
var p = {}
function func(){
    switch(this){
        case o:
            console.log('o');
            break;
        case p:
            console.log('p');
            break;
        case window:
            console.log('window');
            break;          
    }
}
func(); // window
func.apply(o); // o
func.apply(p); // p

apply를 사용하면 그 메소드의 this를 객체(전달한 인자)로 만들어준다. 즉, 어떤 객체에서 그 함수를 호출한 것과 같은 역할을 해준다는 의미이다.

일반적인 언어에서 객체, 메소드 관계
js에서 객체, 메소드 관계

다른 언어에서는 객체 - 메소드가 주인 - 노예 관계이라면, js에서는 동등한 레벨이다. 따라서 다른 객체로 적용시키는 것이 가능하다.

prototype
function Person(name){
    this.name = name;
}
Person.prototype.name=null;
// prototype을 사용해서 Person 에 introduce라는 함수를 추가시켜주는 것
Person.prototype.introduce = function(){
    return 'My name is '+this.name; 
}
var p1 = new Person('egoing');
console.log(p1.introduce());
상속

어떤 객체를 상속받고 싶으면 prototype에 객체를 생성해서 넣어주면 된다.

function Person(name){
    this.name = name;
}
Person.prototype.name=null;
Person.prototype.introduce = function(){
    return 'My name is '+this.name; 
}
 
function Programmer(name){
    this.name = name;
}
Programmer.prototype = new Person(); // 어떤 객체를 상속받고 싶으면 prototype에 객체를 생성해서 넣어주면 된다.
 
var p1 = new Programmer('egoing');
console.log(p1.introduce());

Programmer 객체이지만, 프로토타입이 Person인 것을 볼 수 있다.

 

Programmer.prototype.coding = function(){
    return "hello world";
}
p1.coding(); // hello world

이렇게 Programmer.prototype에 coding을 설정해주면, 프로그래머 객체에서 coding메소드를 호출할 수 있다. Person 객체에서는 coding메소드를 사용할 수 없다. 상속받은 객체에 새로운 기능을 추가할 수 있는 것이다.

 

Programmer.prototype이라는 프로퍼티에는 객체가 담겨있다. 그 객체에 coding이라는 프로퍼티를 정의하는 것이다.

 

상속받고자 할 때, Programmer의 프로토타입에 새로운 객체를 넣어주어야한다. 만약, Programmer의 프로토타입에 Person.prototype을 넣는다면, 잘 작동되는 것 처럼 보이지만, Programmer의 변경 내용이 Person에도 영향을 미친다. 즉, 자식에서 일어난 것들이 부모에도 적용된다는 의미이다.

prototype chain
function Ultra(){}
Ultra.prototype.ultraProp = true;
 
function Super(){}
Super.prototype = new Ultra();
 
function Sub(){}
Sub.prototype = new Super();
 
var o = new Sub();
console.log(o.ultraProp); // true

자식에서 부모의 프로퍼티에 접근 가능하다.

 

function Ultra(){}
Ultra.prototype.ultraProp = true;
 
function Super(){}
Super.prototype = new Ultra();
Super.prototype.ultraProp = 1;
 
function Sub(){}
Sub.prototype = new Super();
Sub.prototype.ultraProp = 2;
 
var o = new Sub();
console.log(o.ultraProp); // 2

부모와 자식에 모두 정의되어있는 프로퍼티라면, 가장 자식쪽에 있는 프로퍼티에 접근하게 된다.

표준 내장 객체

표준 내장 객체 : js가 개발자에게 제공하는 객체

  • Object
  • Function
  • Array
  • String
  • Boolean
  • Number
  • Math
  • Date
  • RegExp

이것들을 상속받거나 이용해서 새로운 메소드를 정의해 사용할 수 있다.

// Array라는 생성자 함수에 rand라는 메소드를 추가함.
Array.prototype.rand = function(){
    var index = Math.floor(this.length*Math.random());
    return this[index];
}
var arr = new Array('seoul','new york','ladarkh','pusan', 'Tsukuba');
console.log(arr.rand());

표준 내장 객체의 prototype을 사용해서 확장시키는 코드이다.

Object

모든 객체의 가장 기본적인 형태. 모든 객체는 Object 라는 객체를 상속받는다. 따라서, 모든 객체에 정의하고싶은 프로퍼티가 있다면, Object의 프로토타입을 설정해주면 된다.

Object에 정의되어있는 프로퍼티들이 많다. 그것들은 레퍼런스를 찾으면 배울 수 있다. 

var o = {"name" : "yk", "age":20}
console.log(Object.keys(o)); // ["name", "age"]

Object.keys()는 Object 자체에서 부를 수 있는 메소드이고, Object.prototype.toString()은 객체에서 부를 수 있는 메소드이다.

 

예시

Object.prototype.contain = function(neddle) {
    for(var name in this){
        if(this[name] === neddle){
            return true;
        }
    }
    return false;
}
var o = {'name':'egoing', 'city':'seoul'}
console.log(o.contain('egoing')); // true
var a = ['egoing','leezche','grapittie'];
console.log(a.contain('leezche')); // true

이런 식으로 모든 객체에서 호출 가능한 contain이라는 메소드를 정의할 수 있다.

하지만 이런 방식은 편리하면서 위험할 수 있다.

모든 프로퍼티를 출력하게 되면 contain도 포함하여 출력하게 된다. 이 때는 아래와 같은 방식으로 해결할 수 있다.

hasOwnProperty는 인자의 프로퍼티가 부모가 아닌 자신의 생성자에 있는지 없는지를 리턴하는 함수이다.

원시 데이터 타입

원시 데이터 타입 : 객체가 아닌 데이터 타입

  • 숫자
  • 문자열
  • 불리언(true/false)
  • null
  • undefined
래퍼 객체
var str = 'coding';
console.log(str.length);        // 6
console.log(str.charAt(0));     // "C"

str에 .을 사용해 length나 charAt을 호출하는 것이 가능하다. str이 객체이고, str의 프로퍼티에 접근한다는 의미이다. 하지만 문자열은 원시데이터 타입이다. 그렇다면 어떻게 프로퍼티가 있을 수 있냐는 것이다. 그것은, str.length라는 것을 호출할 때, 자동으로 str을 객체로 감싸기 때문이다. 그것이 래퍼객체이다. 즉, var str = new String("coding");를 실행시켜준다는 의미이다. 그 후, str객체가 사용이 끝났으면, 자동으로 객체를 없애준다.

var str = 'coding';
str.prop = 'everybody';
console.log(str.prop);      // undefined

이것을 보면 이해가 잘 갈 것이다. prop이라는 프로퍼티에 'everybody'를 넣어줄 때는, 래퍼객체를 만들어 str을 객체화 시켜주지만, 그 후 다음 문장을 실행시키기 전에 str이라는 객체를 없애주기 때문에 str.prop을 출력해도 undefined가 되는 것이다.

숫자는 Number, 문자열은 String, 불리언은 Boolean이라는 래퍼객체가 있다.

복제, 참조 / 값형, 참조형

원시 데이터 타입은 값형이고 객체는 참조형이다.