1. 배열
순서가 있는 값
인덱스(index) | [0] | [1] | [2] | [3] | [4] |
요소(element) | 73 | 98 | 86 | 61 | 96 |
let myNumber = [73, 98, 86, 61, 96]
- 대괄호(square bracket)를 이용해서 배열을 만듭니다
- 각각의 요소(element)는 쉼표(comma)로 구분해줍니다
Q ) myNumber의 1번째 인덱스 값의 0번째 인덱스 값은?
let myNumber =[[13,30], [78,8], [44,17]];
myNumber [1] [0];
//78
배열의 반복
let myNum = [10,20,40,10]
let sum = 0;
for(let n=0;n<myNum.length;n++) {
sum = sum +myNum[n];
}
//sum = 0 + 10
--> sum 초기값을 지정안해주면 undefined + 10 // NaN
//sum = 10 + 20
//sum = 30 + 40
//sum = 70 + 10
배열인지 아닌지 확인하기
let words = ['피','땀','눈물'];
let obj = { a:1 }
typeof '문자열';
//"string"
typeof 123
//"number"
typeof words
//"object"
typeof [1,2,3]
//"object"
typeof obj
//"object"
Array.isArray('문자열')
//false
Array.isArray(words) //배열인 words 넣기
//true
Array.isArray([1,2,3]) //직접 배열 넣기
//true
Array.isArray([]) // 빈 배열
//true
Q) arr라는 배열이 빈 배열인지 확인하는 알맞은 방법은?
arr === []
//false
//주소가 다른 빈 배열이라고 생각하기 때문에
arr.length === 0
//true
arr = []
//arr의 값을 []로 바꾸는 코드
Array.isArray(arr) === 0
//Array.isArray는 전달인자의 타입이 배열인지 체크해서 blloean값을 리턴하는 메소드
//0이 나올수가 없다
배열에서 indexOf, includes 활용
let words = ['Redagast', 'the', 'Brown']
words.indexOf('the') //배열의 인덱스 확인
//1
words.indexOf('없는단어')
//-1
words.indexOf('th')
//-1
//문자열과는 달리 처음 발견된 지점이 아닌 요소 값을 모두 입력해야됨
words.indexOf('Brown') !== -1 //배열안에 해당 요소 여부 확인
//true
words.includes('Brown') //Internet Explorer에서는 사용불가
//true
words.includes('없는것')
//false
2. 객체
키와 값 쌍(key-value pair)으로 이루어진 의미를 가지는 값
let user = {
firstName : 'Steve',
키 값
lastName : 'Lee',
email : 'steve@codestates.com',
city : 'Seoul'
};
- 키, 값 사이는 콜론(:)으로 구분합니다
- 중괄호(curly bracket)를 이용해서 객체를 만듭니다
- 키-값 쌍은 쉼표로 구분해줍니다
객체를 사용하는 방법 Dot notation과 Bracket notation
let user = {
firstName : 'Steve',
lastName : 'Lee',
email : 'steve@codestates.com',
city : 'Seoul'
};
1.Dot notation
user.firstName;
//'Steve'
user.city;
//'Seoul'
//정해진 값에만 적용
2.Bracket notation
user['firstName'];
//'Steve'
user['city'];
//'Seoul'
//key 값이 변할때 주로 사용
tweet[content]와 tweet['content']의 차이
let tweet = {
writer : 'stevelee',
createdAt : '2019-09-10 12-03:33',
content : '프리코스 재밌어요!'
};
tweet['content']
//'프리코스 재밌어요!'
tweet[content]
//Uncaught ReferenceError : content is not defined
pi
//Uncaught ReferenceError : pi is not defined
adghjrs
//Uncaught ReferenceError : adghjrs is not defined
//[content]를 변수로 취급
let keyName = 'content';
tweet[keyName]
//'프리코스 재밌어요!'
//content를 새 변수에 할당해주면 tweet['content']와 같아진다
let person = {
name : 'Steve',
age : 16
};
function getProperty(obj, propertyName) {
return obj[propertyName]; // []에는 문자열 형식을 넣거나 변수를 넣어야함
}
1.
let output = getProperty(person, 'name');
console.log(output);
//'Steve'
2.
let output2 = getProperty(person, 'age');
console.log(output2);
//16
let tweet = {
writer : 'stevelee',
createdAt : '2019-09-10 12:03:33',
content : '프리코스 재밌어요!'
};
dot/bracket notation을 이용해 값을 추가
tweet['category'] = '잡담';
tweet.isPublic = true;
tweet.tags = ['#코드스테이츠', '#프리코스']; //배열을 넣어도됨
delete 키워드를 이용해 삭제가 가능
delete tweet.createdAt; //createAt 키-값 쌍을 지웁니다
//tweet은 다음과 같게 됩니다
// {writer : 'stevelee', content : '프리코스 재밌어요!'}
in 연산자를 이용해 해당하는 키가 있는지 확인할 수 있습니다
'content' in tweet;
//true
'updateAt' in tweet;
//false
Q. 두 개의 객체를 입력받아 두 번째 객체의 속성들을 첫 번째 객체에 추가해야 합니다.
입출력 예시
const obj1 = { a: 1, b: 2 };
const obj2 = { b: 4, c: 3 };
// 보기의 코드를 여기에 입력하면, 아래의 결과가 나와야 합니다.
extend (obj1, obj2);
console.log(obj1);
//{a:1, b:2, c:3}
console.log(obj2);
//{b:4, c:3}
답
function extend(obj1, obj2) {
for (let key in obj2) {
if (!key in obj1) {
obj1[key] = obj2[key];
}
}
}
3. Primitive, Reference, Rest Parameter, Spread operator
원시 자료형 (primitive data types)
- 객체가 아니면서 method를 가지지않습니다
- string, number, bignit, boolean, underfined, symbol, (null)
- 모두 '하나'의 정보, 즉, 데이터를 담고 있습니다
const num1 = 123;
const num2 = 1234566789;
- 이렇게 변수에는 데이터의 크기와는 관계 없이 하나의 데이터만 담을 수 있습니다
- 원시 자료형은 값 자체에 대한 변경이 불가능(immutable)하지만, 변수에 다른 데이터를 할당할 수 있습니다
- 원시 자료형이 할당될 때에는 변수에 값 자체가 담깁니다
- call stack 메모리에 저장
참조 자료형 (reference type)
- 원시 자료형이 아닌 모든것
- 배열, 객체, 함수가 대표적
- 직접 다루게 되는 변수에는 주소만 저장 (call stack 메모리)
- 참조 자료형이 할당될 때는 보관함의 주소(reference)가 담깁니다
- 참조 자료형의 데이터 자체는 heap 메모리에 저장됩니다
- 원시 자료형처럼 고정된 크기의 보관함이 아니라 동적으로 크기가 변합니다
Q1) 코드가 실행된 후, player.score의 값은 무엇일까요?
let player = {score:3};
function doStuff(obj) {
obj.score = 2;
}
doStuff(player);
//2
Q2) 코드가 실행된 후, myArray의 값은 무엇일까요?
let myArray = [2,3,4,5];
let ourArray = myArray;
ourArray[2] = 25;
ourArray = underfined;
//ourArray에 원시 자료형 underfined가 할당되었기 때문에, myArray에 접근할 수 없습니다
//하지만 myArray는 그대로 유지됩니다
//[2, 3, 25, 5]
Rest 파라미터
Rest 파라미터 구문은 정해지지 않은 수를 배열로 나타낼 수 있도록 합니다
function f(a, b, ...rest) {
// ...
}
- 함수의 마지막 파라미터 앞에 ...를 붙여 (사용자가 제공한) 모든 나머지 인수를 "표준" 자바스크립트 배열로 대체합니다
- 마지막 파라미터만 "Rest 파라미터"가 될 수 있습니다
Q1) 함수가 실행된후, 콘솔에 출력되는 args의 값은 무엇일까요?
function printMaxNums(...args) {
console.log(args)
}
printMaxNums(10, 30, 40)
//[10, 30, 40]
Q2) 코드가 실행된 후, value의 값은 무엇일까요?
function findBiggestArg(...args) {
let biggestArg = 0;
for(let i=0; i<args.length; i++) {
if (biggestArg < args[i] {
biggestArg = args[i]
}
}
return biggestArg
}
let value = findBiggestArg(10, 30, 40, 20)
//40
spread operator
전개 구문은 배열이나 문자열과 같이 반복 가능한 문자를 펼쳐주는 문법을 의미합니다
const arr = [1, 2, 3, 4, 5]
const newArr = [...arr]
//[1, 2, 3, 4, 5]
Q) 코드가 실행된 후, value값은 무엇일까요?
let arr = [10, 30, 40, 20]
let value = Math.max(...arr)
//Math.max(10, 30, 40, 20)
//40
4. scope
변수 접근 규칙에 따른 유효 범위
- 변수는 어떠한 환경 내에서만 사용 가능하며, 프로그래밍 언어는각각의 변수 접근 규칙을 갖고 있습니다
- 변수와 그 값이, 어디서부터 어디까지 유효한지를 판단하는 범위
- JavaScript는 기본적으로, 함수가 선언되는(lecical) 동시에 자신만의 Scope를 가집니다
Local Scope과 Global Scope의 차이
let greeting = 'Hello';
function greetSomeone() {
let firstName = 'Josh';
return greeting + ' ' + firstName;
}
greetSomeone();
//'Hello Josh'
firstName;
//ReferenceError
- 변수 firstName에 접근할 수 있는 범위가 존재
- Local Scope 안쪽에서 선언된 변수는 밖에서 사용할 수 없습니다
- 안쪽 Scope에서 바깥 변수/함수를 접근하는 것은 가능
- 바깥쪽 Scope에서 안쪽 변수/함수를 접근하는 것은 불가능
- Scope는 중첩이 가능합니다
-함수 안에 함수를 넣을 수 있습니다 - Global Scope는 최상단의 Scope로 전역 변수는 어디서든 접근이 가능합니다
let name = "Richard";
function showName() {
let name = "Jack"; //지역 변수, showName 함수 안에서만 접근 가능
console.log(name); //2, Jack
}
console.log(name); //1, Richard
showName();
console.log(name); //3, Richard
let name = "Richard";
function showName() {
name = "Jack"; //전역 변수, showName 함수 안에서만 접근 가능
console.log(name); //2, Jack
}
console.log(name); //1, Richard
showName();
console.log(name); //3, Jack
Function Scope과 Block Scope (= lexical scope)의 차이
Block : 중괄호로 시작하고 끝나는 단위
var
- 함수 단위로 자신만의 Scope를 가집니다
let
- Block단위로 Scope를 구분합니다
function greetSomeone(firstName) {
var time = 'night';
if(time === 'night') {
var greeting = 'Good Night';
}
return greeting + ' ' + firstName;
}
greetSomeone('Steve');
//Good Night Steve
function greetSomeone(firstName) {
let time = 'night';
if(time === 'night') {
var greeting = 'Good Night';
}
return greeting + ' ' + firstName;
}
greetSomeone('Steve');
//안쪽 함수에 접근이 불가능해 안쪽 함수 firstName 값이 undefined
//작동 안됨
변수 | var | let | const |
유효 범위 | Function Scope | Block Scope | Block Scope |
재선언 | o | x | x |
재할당 | o | o | x |
- const
constant(상수)를 뜻합니다. 즉, '항상 같은 수'를 말합니다
상수이기 때문에 const 키워드로 선언하면 변치 않는 값을 갖는 변수를 생성합니다
그렇기에 const로 선언한 변수는 값을 재할당할 수 없습니다
const myName = 'Wo Young';
myName = 'O'; // TypeError: Assignment to constant variable
const 변수는 반드시 값이 할당되어야 합니다
값 없이 선언하면 SyntaxError 메시지가 뜹니다
const name;
console.log(name); //Syntaxerror: Missing initializer in const declaration - let
let은 const와 다르게 다른 값이 재할당될 수 있습니다
let fruit = 'banana';
fruit = 'apple'; - var
let, const와 다르게 변수 재선언이 가능합니다
var age = 30; // 결과 : 30 (초기 변수 선언)
age = 28; // 결과 : 28 (변수값 재할당)
var age = 'old'; // 결과 : 'old' (변수 재선언)
전역변수와 window 객체
- 전역 범위를 대표하는 객체 window
- Global Scope에서 선언된 함수, 그리고 var 키워드를 이용해 선언된 변수는 window 객체와 연결
- 전역 범위에 너무 많은 변수를 선언하지는 말자
선언 없이 초기화된 전역 변수
선언 키워드 (var, let, const)없이 변수를 초기화하지 마세요
function showAge() {
age = 90; //age는 전역 변수로 취급
console.log(age);
}
//age === window.age
//오류가 많이 나게된다 (좋은것이 아님)
showAge();
//90
console.log(age);
//90
Q) 다음의 코드를 실행시킨 후에 result의 값은 무엇이 될까요?
let x = 10;
function outer() {
x = 20;
function inner() {
let x;
x = x + 20;
return x;
}
inner();
}
outer();
let result = x;
//20
5.closure
함수와 함수가 선언된 어휘적 환경의 조합
function outerFn() {
let outerVar = 'outer';
console.log(outerVar);
function innerFn() {
let innerVar = 'inner';
console.log(innerVar);
}
return innerFn;
}
outerFn();
/*function innerFn() {
let innerVar = 'inner';
console.log(innerVar);
} */
outerFn()()
//inner
let innerFn = outerFn();
//outer
innerFn();
//inner
- 외부 함수의 변수에 접근할 수 있는 내부 함수
- 클로저 함수 안에서는 지역 변수 외부 함수의 전역 변수의 접근이 전부 가능합니다
커링
함수 하나가 n개의 인자를 받는 대신, n개의 함수를 만들어 각각 인자를 받는 방법
function adder(x) {
return function(y) {
return x+y;
}
}
adder(2)(3); //5
let add100 = add(100); //x의 값을 고정해놓고 재사용할 수 있다
add100(2); //102
add100(10); //110
외부 함수의 변수가 저장되어 마치 템플릿 함수와 같이 사용 가능
function htmlMaker(tag) {
let startTag = '<' + tag + '>';
let endTag = '</' +tag + '>';
return function(connect) {
return startTag + content + endTag;
}
}
let divMaker = htmlMaker('div');
divMaker('code'); //<div>code</div>
divMaker('states'); //<div>states</div>
let h1Maker = htmlMaker('h1');
h1Maker('Headline'); //<h1>Headline</h1>
클로저 모듈 패던
변수를 스코프 안쪽에 가두어 함수 밖으로 노출시키지 않는 방법
function makeCounter() {
let privateCounter = 0;
return {
increment: function() {
privateCounter++;
},
decrement: function() {
privateCounter--;
},
getValue: function() {
return privateCounter;
}
}
}
위와 동일 코드
/*
function makeCounter() {
let privateCounter = 0;
let obj = {
increment: function() {
privateCounter++;
},
decrement: function() {
privateCounter--;
},
getValue: function() {
return privateCounter;
}
}
return obj;
}
*/
let counter1 = makeCounter();
counter1.increment();
counter1.increment();
counter1.getValue();
//2
let counter2 = makeCounter();
counter2.increment();
counter2.decrement();
counter2.increment();
counter2.getValue();
//1
// privateCounter를 간접적으로 바꿀수가 있다
let counter2 = makeCounter();
counter2.increment()
counter2.increment()
counter2.increment()
counter2.increment()
counter2.increment()
counter1.getValue()
//5
- privateCounter를 각각 독립적으로 가지고 있기때문에
- 두 카운터에 각기 다른 privateCounter를 다루면서, privateCounter를 밖으로 노출시키지 않는다
funtion makePayment() {
let type = '현금'; // 반드시 '현금' 또는 '카드'여야 함
return {
payWithCash: function(amount) {
type = '현금';
console.log(type +'으로' + amount + '만큼 지불합니다.');
},
payWithCard: function(amount) {
type = '카드';
console.log(type +'으로' + amount + '만큼 지불합니다.');
}
}
}
}
- 프로그램 외부에서 type을 마음대로 바꿀수가 없다
- 현금, 카드로만 들어갈수 있도록 간접적으로만 바꿀수 있도록 세팅
클로저의 단점
일반 함수였다면 함수 실행 종료 후 가비지 컬렉션(자동 메모리 관리법) 대상이 되었을 객체가 클로저 패턴에서는 메모리 상에 남아 있게 됩니다. 외부 함수 스코프가 내부함수에 의해 언제든지 참조 될 수 있기 때문입니다. 따라서 클로저를 남발할 경우 퍼포먼스 저하가 발생할 수도 있습니다.
Q) total의 값은 무엇일까요?
let add = function(x) {
let sum = function(y) {
return x + y;
}
return sum;
}
let foo = add(1);
foo(3);
let total = foo(6);
//7
'Language > JavaScript' 카테고리의 다른 글
[JS] OOP (0) | 2021.02.26 |
---|---|
[JS] 구조 분해 (0) | 2021.02.25 |
[JS] 화살표함수 (0) | 2021.02.24 |
[JS] 고차함수 (0) | 2021.02.24 |
[JS] Method (0) | 2021.01.19 |
댓글