Js 3일차

위범석·2022년 7월 5일
0

배열의 메소드 Sort,reduce

  • let arr = [1,5,4,2,3];

arr.sort();

console.log(arr); // 1,2,3,4,5

주의! 배열 자체가 변경 됨. 꼭 기억할것. 문자열도 알파벳 순서대로 정렬이된다.

let arr = [27,8,5,13];
arr.sort();
console.log(arr); // 13,27,5,8

위와 같이 나오는 이유는 배열안에 있는 숫자를 문자열로 취급하기 때문에 1(3),2(7),5,8
이런식으로 배열이 정렬이 된다.

값을 정확하게 비교하기 위해선 sort에서 함수를 전달 받아야 한다.

let arr = [27,8,5,13];
arr.sort((a,b)=>{
console.log(a,b);
return a-b;
});
console.log(arr); //5,8,1,3,27
내부 로직 원리는 2개씩 콘솔에 찍힌 값끼리 서로 비교하면서 자리를 찾아간다.

실무에서는 보통 Lodash라는 라이브러리를 다운받아서 _.sortBy(arr)을 사용하여 sort에 함수 전달 없이 사용한다.

reduce

let arr = [1,2,3,4,5];

let result = 0;
arr.forEach(num=>{
result += num;
});
console.log(result); //15

이 작업을 한번에 처리할 수 있는게 reduce 이다.

  • arr.reduce()
  • 인수로 함수를 받음
  • (누적 계산값, 현재값) => {return 계산값};

// 배열의 모든 수 합치기

  • let arr = [1,2,3,4,5];

  • const result = arr.reduce((prev,cur)=>{
    return prev +cur
    },0); 함수값 다음에 오는 인자는 초기값을 나타내는데, defult는 배열의 첫번째 요소가 들어가게 된다. 배열 자체의 합을 구할 때는 0은 안적어줘도 된다는 뜻.

  • ex) reduce의 유용한 쓰임

let userList = [
{name:"Mike",age:30},
{name:"Tom",age:10},
{name:"Jane",age:27},
{name:"Sue",age:26},
{name:"Harry",age:43},
{name:"Steve",age:60},
];

let result = userList.reduce((prev,cur)=>{
if(cur.age>19) {
prev.push(cur.name);
}
return prev;
},[]);

console.log

상황에 맞게 filter, map, reduce, forEach << 이런 메소드들을 언제 사용할지 생각 하는게 중요.

구조 분해 할당

  • let users = ['1','2','3'];
  • let [u1,u2,u3] = users;
    // u1 = users[0]
    // u2 = users[1]
    // u3 = users[2]

let str = "Mike-Tom-Jane";
let [user1,user2,user3] = str.split('-') << 이것도 위에거와 마찬가지로 배열 구조가 '-' 단위로 분해되어 배열에 저장이 된다.

if 해당 하는 값이 없다면??

let [a=3, b=4,c=5] = [1,2];

c에 기본값을 설정 함으로써 undefined가 되는 상황을 방지 하기위해 기본값을 설정해놓으면 미연에 방지 할 수 있다.

let[u1, ,u2] = ['1','2',3,] 이런식으로 중간을 비우게 되면 1이랑 3이 들어가게 된다.

  • 이미 할당된 변수를 변경 하려면 새로운 변수를 생성해서 바꿔줘야 했지만,
    let a = 1;
    let b = 2;
    [a,b] = [b,a]; 이런식으로 바로 변수 값 변경이 가능하다.

객체 구조 분해

  • let user ={name:'Mike',age : 30};
  • let {name,age} = user;
  • // == let name = user.name;
  • // == let age = user.age;
  • 여기서 name과 age는 순서가 변경되어도 상관이없다.
  • 객체 내의 변수를 바꾸고 싶을떈??
  • let {name:userName, age:userAge}=user;
  • console.log(userName,userAge); << 변수 이름 변경
  • 배열과 마찬가지로 객체에도 기본값 사용이 가능하다.
    let user={name:'Mike',age:30,gender:'male'};
    let {name,age,gender:'female'};
    console.log(name,age,gender); // Mike, 30 ,male
    기본값으로 female을 등록 했지만, 객체에 gender의 값이 이미 등록이 되어 있기 때문에 변경이 되지 않는다.

나머지 매개변수, 전개구문

arguments

function showName(name){
console.log(arguments.length);
console.log(arguments[0]);
console.log(arguments[1]);
}
showName('Mike','Tom');
// 2
// 'Mike'
// 'Tom'

  • 함수로 넘어온 모든 인수에 접근
  • 함수내에서 이용가능한 지역변수
  • length/index
  • Array 형태의 객체
  • 배열의 내장 메서드 없음
    (forEach, map) 이런 메서드들은 사용할 수가 없다.
    권장 하는 방식은 ...사용
    정해지지 않은 변수의 개수를 적을때 유용하다

function showName(...names){
console.log(names);
}

showName('Mike','Tom') // 'Mike', 'Tom'

앞에서와 다르게 reduce, forEach를 ...방식은 모두 사용이 가능하다.

function add(...numbers) {
let result = numbers.reduce((prev,cur) => prev +cur);
console.log(result)
}

add(1,2,3) // 6
add(1,2,3,4,5,6,7,8,9) //55

##// 화살표 함수의 유일한 문장이 'return'일 때 'return'과
// 중괄호({})를 생략할 수 있다.
elements.map(element => element.length); // [8, 6, 7, 9]

유용한 예제

  • 나머지 매개변수
  • user 객체를 만들어 주는 생성자 함수를 만들어줍니다.

function User(name,age, ...skills){
this.name = name;
this.age = age;
this.skill = skills;
}

const user1 = new User('Mike',30,'html','css');
const user2 = new User('Tom',20,'JS','React');
const user3 = new User('Jane',10,'English');

이런식으로 나머지 매개변수는 항상 마지막에 위치해야 한다.

전개구문 복제

let user = {name:'Mike', age:30};
let user2 = {...user};

user2.name = "Tom"
console.log(user2.name); // Tom
// .unshift() : 배열의 맨 앞에 값을 추가한다.

전개구문의 쓰임으로 만약에

let arr1 = [1,2,3];
let arr2 = [4,5,6];

arr2.reverse().forEach((num)=>{
arr1.unshift(num);
});
console.log(arr1); // [4,5,6,1,2,3]
이런식으로 값을 얻어야 하는 부분을

arr1 = [...arr2,...arr1]

이런식으로 같은 값을 얻을 수 있다.

클로저

let one = 1;

function addOne(num) {
// let one = 2
console.log(one + num)
}

addOne(5);

처음에 전역 Lexical 환경에서 one : 1 , addOne:function 이 두개의 값을 가지고 있다가, 내부 Lexical 환경에서는 num:5를 가지고있음, 내부 Lexical이 전역 Lexical을 참조 > num에 대한 정보는 있지만 one은 없기 때문에 전역에서 값을 찾은후 대입, 내부에서 만약 one을 선언 해줄 시에 우선적으로 one의 값은 내부에서 지정해준 값으로 사용된다.

어휘적 환경(Lexical Envionment)

function makeAdder(x) {
return function (y) {
return x + y;
}
}

const add3 = makeAdder(3);
console.log(add3(2)); // 5, add3 함수가 생성된 이후에도 상위함수인 makeAdder의 x에 접근 가능

Closure = 함수와 렉시컬 환경의 조합 , 함수가 생성될 당시의 외부 변수를 기억 생성 이후에도 계속 접근 가능

상속,프로토타입

객체에는 자신이 property로 가지고 있는지 확인하는 메소드가 있는데,
hasOwnProperty() 이다.
hasOwnProperty의 위치는 proto:라는 객체가 있는데, 이것을 프로토타입이라고 한다. 일단 객체에서 property를 읽으려고 하는데 없으면
프로토타입에서 property를 찾는다.
만약 객체 내에서 property가 존재한다면 탐색을 종료한다.

const car = {
wheels :4,
drive() {
console.log("drive..");
},
};

const bmw = {
color : "red",
navigator: 1,
};
const benz = {
color : "black",
};

const audi = {
color:'blue',
};

bmw.proto = car; // 이렇게 되면 car가 bmw의 프로토타입이 되는것, 다시말해, bmw는 car를 상속 받은것이다.
benz.proto = car;
audi.proto = car;

  • 여기서 상속은 계속 이어질 수 있다.

const x5 = {
color:"white",
name:"x5",
};

x5.proto=bmw;

여기서 console.log(x5.drive()) // drive..
가 나오게 되는데 이 메소드는 car객체에 있는 내용인데, 사용이 가능한 이유가 prototype chain관계에 의해서 사용이 가능하다.

  • 생성자 함수 사용

const Bmw = function (color) {
this.color = color;
};

const x5 = new Bmw("red");
const z4 = new Bmw("blue");

x5.proto=car;
z4.proto=car;

// 생성자 함수를 사용하는건 간편해서 이니 다른방법으로는

const Bmw = function (color) {
this.color=color
};

Bmw.prototype.wheels = 4;
Bmw.prototype.drive = function(){
console.log("drive..")
};

const x5 = new Bmw("red");
const z4 = new Bmw("blue");

생성자함수가 새로운 객체를 만들어 낼때, 그 객체는 생성자의 인스턴스라고 부른다. instanceof로 인해서 객체와 생성자를 확인할 수 있다.

ex) z4 instanceof Bmw << z4 Bmw로 인해서 만들어진 인스턴스 이기 때문에, true값을 반환한다.

  • 생성자로 만들어진 객체에는 constructor라는 property가 존재한다.
  • 즉 이 constructor는 생성자(Bmw)를 가리킨다.
  • z4.constructor === Bmw; // true
  • 위의 예제에서 생성자 함수의 prototype을 추가할때,
    Bmw.prototype = {
    constructor:Bmw,
    wheels : 4,
    drive() {
    console.log("drive...");
    },
    navigation :1,
    shop {
    console.log("stop!!")
    },
    };
    이런식으로 추가가 가능하다.
    그런데, 이렇게 하면 z4.constructor === Bmw; 를 하면 false가 반환된다.
    이런 현상을 방지하기 위해서 property를 덮어 쓰지말고 하나씩 추가하는게 좋다.
    혹은, 덮어 쓸시에 constructor:Bmw, 로 추가로 명시를 해주면 true값이 반환되게 된다.

const Bmw = function(color){
this.color = color;
};

const x5 = new Bmw("red"); // 지금 만들어진 자동차의 색상은 마음대로 변경이 가능하다. 이럴때는 클로저를 이용하면 된다.

const Bmw = function (color){
const c = color;
this.getColor = function () {
console.log(c);
}
};

const h4 = new Bmw("red");

이렇게 쓰게되면 초기에 세팅했던 컬러값을 얻을 수는 있고 바꿀수는 없다.

Class

  • ES6에 추가된 스펙
  • class에 들어가기전에 js에서 프로토타입의 이해(prototype)=
    javascript에서는 객체를 상속하기 위해서 프로토타입이라는 방식을 사용합니다.
    JavaScript는 흔히 프로토 타입 기반 언어(prototype-basd language)라 불립니다.

이처럼 우리는 함수라는 객체를 생성했을 때 그 객체에는 prototype이라는 object가 자동으로 생성된다. 그 프로토타입에는 역시 constructor라는 객체가 자동으로 생성되어 서로를 참조할 수 있게 된다.

Person.prototype.sum = function(){}
위의 코드를 통해서 prototype에 함수를 추가함으로써 여러 객체를 생성하였을 때 해당함수를 사용할 수 있게 한다.

그림에서의 객체에서

kim.sum()
lee.sum()
둘 다 사용이 가능하다.

Q. 그렇다면 이렇게 두 객체가 sum함수를 사용할 수 있는 이유는 무엇일까?
그건 바로 Person의 인스턴스를 새로 생성했을 때 해당 인스턴스의 Person의 prototype링크인 proto object를 새로 부여받기 때문이다.

따라서 kim객체에는 없는 sum함수도 사용할 수 있게 된다.

const User = function (name,age) {
this.name = name;
this.age = age;
this.showName = function (){
console.log(this.name);
};
};

const mike = new User('kevin',30);
console.log(mike);
class User2 {
constructor(name,age) {
this.name=name;
this.age = age;
}
showName() {
console.log(this.name);
}
}
const tom = new User2('lisa',29);
console.log(tom);

밑에있는 class에서는 객체 안에 showName이 존재 하지 않고, proto내에 showName이 존재한다.

첫번쨰 생성자 메소드 의 코드에서, 아래와 똑같이 prototype에 함수를 추가하기 위해선, this.showName ... 을 지우고, User.prototype.showName = function(){console.log(this.name)}; 을 추가하면 된다.

  • class는 new연산자 없이 사용할 수 없다.
  • for in 문은 prototype이 포함된 property들을 다 보여주고,
  • 객체가 가지고 있는 property들만 판단하기 위해서 hasOwnproperty를 사용, 클레스인 메소드는 for in 문에서 제외된다.

상속

class Car {
constructor(color) {
this.color = color;
this.wheels = 4;
}
drive() {
console.log("drive..")
}
stop() {
console.log("stop!")
}
}

class Bmw extends Car {
park() {
console.log("PARK");
}
}

const z4 = new Bmw('blue');
z4.stop();

  • 동일한 이름으로 메소드를 정의 하게 되면 class 내부에 있는 함수로 덮어 쓰게 된다.

오버라이딩

  • 부모가 가지고 있는 키워드를 그대로 사용하고 싶을때는 super를 사용하면 된다.
    class Car {
    constructor(color) {
    this.color = color;
    this.wheels = 4;
    }
    drive() {
    console.log("drive..")
    }
    stop() {
    console.log("stop!")
    }
    }

class Bmw extends Car {
park() {
console.log("PARK");
}
stop(){
super.stop() // 메소드 오버라이딩.
console.log("OFF!")
}
}

const z4 = new Bmw('blue');
z4.stop();

생성자 오버라이딩

  • class에 constructor는 빈객체를 만들게되고, this로 그 객체를 가리키게 된다.
  • 반면 extends로 만든 클레스는 빈객체를 만들고, this로 객체를 가리키게 되는 작업을 건너 뛰게 되기 때문에 그래서 항상 super 키워드를 사용해야한다.

class Car {
constructor(color) {
this.color = color;
this.wheels = 4;
}
drive() {
console.log("drive..")
}
stop() {
console.log("stop!")
}
}

class Bmw extends Car {
constructor(color) {
super(color);
this.navigation =1;
}
park() {
console.log("PARK");
}
stop() {
super.stop()
console.log("OFF!")
}
}

const z4 = new Bmw('blue');
console.log(z4);

profile
코린이

0개의 댓글