typeof, instanceof
• typeof 연산자는 타입을 확인하기 위한 연산자이다.
• instanceof 는 객체의 타입이 특정 타입인지를 판단하기 위한 연산자이며
instanceof 왼쪽에 객체를, 오른쪽에 타입을 명시해서 왼쪽 객체의 타입이
오른쪽에 명시한 타입인지를 판단하는 연산자이고 연산의 최종 결과는 true/false 이다.
instanceof 는 생성자를 가지고 판단하는 연산자이기도 한다.
▶ 다른 함수의 프로토타입을 그대로 자신의 프로토타입으로 지정하는 경우의 instanceof
▶ 상위 객체를 생성해서 하위 프로토타입으로 지정하는 경우의 instanceof
"use strict"
function User(){}
let user1 = new User()
//typeof
console.log(typeof 10)//number
console.log(typeof 'hello')//string
console.log(typeof true)//boolean
console.log(typeof User)//function
console.log(typeof [10, 20])//object
console.log(typeof user1)//object
//instanceof
//instanceof 는 객체의 타입을 판단하기 위한 연산자이다.
//내부적으로 오른쪽의 생성자 함수의 prototype 을 판단한다.
console.log(10 instanceof Number)//false
console.log(new Number(10) instanceof Number)//true
function Car(){}
console.log(user1 instanceof User)//true
console.log(user1 instanceof Car)//false
function Shape(){}
function Rectangle(){}
//두 생성자 함수의 prototype 이 동일
Rectangle.prototype = Shape.prototype
let shape = new Shape()
let rect = new Rectangle()
console.log(shape instanceof Shape)//true
console.log(shape instanceof Rectangle)//true
console.log(rect instanceof Shape)//true
console.log(rect instanceof Rectangle)//true
Rectangle.prototype = new Shape()
let shape1 = new Shape()
let rect1 = new Rectangle()
console.log(shape1 instanceof Shape)//true
console.log(shape1 instanceof Rectangle)//false
console.log(rect1 instanceof Shape)//true
console.log(rect1 instanceof Rectangle)//true
프로퍼티 Descriptor
• 객체에 프로퍼티에는 설명자(Descriptor)라는 정보가 있다.
설명자를 이용해 객체의 프로퍼티 값이 변경되지 않게 하거나 열거로 사용되지 않게 하는 등
원하는데로 사용되게 할 수 있다. 객체를 선언하면서 프로퍼티의 설명자를 따로 지정하지 않는다고 하더라도
기본으로 프로퍼티에 설명자가 추가 된다.
프로퍼티의 설명자를 확인하려면 Object.getOwnPropertyDescriptor() 를 이용하면 된다.
△ 설명자의 각 속성의 의미
• value : 프로퍼티에 대입된 값
• writable: 프로퍼티 값을 수정할 수 있는지에 대한 여부
• enumerable : 프로퍼티가 열거형으로 이용이 가능한지에 대한 여부
• configurable : 프로퍼티의 설명자를 변경할 수 있는지에 대한 여부
▼ writable
• writable 은 프로퍼티의 값을 변경하지 못하게 할 때 이용한다.
객체를 선언할 때 지정한 값으로만 사용되어야 하거나 특정 업무가 진행되는 동안
값 변경이 불가능하게 하고자 할 때 이용한다.
만약 프로퍼티의 설명자 값을 변경하고 싶다면 Object.defineProperty() 를 이용한다.
▼ enumerable
• enumerable 은 프로퍼티가 열거로 이용이 가능한지를 설정하기 위해서 사용한다.
열거라는 것은 프로퍼티 명을 명시하지 않고 어떤 객체에 등록된 프로퍼티들을 순서대로 나열해서 사용하는 것을 의미하며 특정 프로퍼티가 이 열거로 이용되지 않게 하고자 할 때 enumerable 을 false 로 지정한다.
▼ configurable
• configurable 은 프로퍼티의 설명자를 재설정 할 수 있는지에 대한 정보이며
false 로 지정하면 재설정이 불가능하다.
"use strict"
let obj = {
name: '홍길동',
age: 10,
address: 'seoul'
}
//특정 객체의 property 의 descriptor 확인
console.log(Object.getOwnPropertyDescriptor(obj, 'name'))
//{value: '홍길동', writable: true, enumerable: true, configurable: true}
//writable - 값 변경 못하게 하고자 할때
Object.defineProperty(obj, 'age', {writable: false})
obj.name = '김길동'
// obj.age = 20 //error
//enumerable
console.log(Object.keys(obj))//['name', 'age', 'address']
console.log(Object.values(obj))//['김길동', 10, 'seoul']
console.log(Object.entries(obj))
//0:(2) ['name', '김길동']
//1: (2) ['age', 10]
//2: (2) ['address', 'seoul']
//in.. 열거 예약어..obj 의 entry 갯수만큼 for 반복.
for(let property in obj){
console.log(property)
}
//name
//age
//address
Object.defineProperty(obj, 'age', {enumerable: false})
console.log(Object.entries(obj))
//0: (2) ['name', '김길동']
//1: (2) ['address', 'seoul']
for(let property in obj){
console.log(property)
}
//name
//address
console.log(obj.age)//10
//configurable
//wriable 을 false 로 지정했다고 하더라도 누군가가 true 로 변경해서
//값 변경을 할수도있다.
Object.defineProperty(obj, 'age', {writable:true})
obj.age = 20
//descriptor 를 조정한 후에 다시 descriptor 가 조정되지 못하게함
Object.defineProperty(obj, 'age', {writable:false, configurable:false})
Object.defineProperty(obj, 'age', {writable:true})
new Object()
• 함수나 클래스 같은 객체의 모형을 선언하고 이 모형을 이용해 객체를 생성하지 않고
간단하게 객체를 선언하는 방법은 객체 리터럴이다.
또한 객체 리터럴 방법을 이용하지 않고 new Object() 로 객체를 선언할 수도 있다.
Object.create()
• 자바스크립트의 모든 객체는 그 객체를 만드는 프로토타입이 있어야 한다.
• 객체 리터럴로 객체를 생성하는 것은 new Object() 방법으로 객체를 생성하는 것과 동일함으로
객체 리터럴로 만든 객체의 프로토타입은 Object 의 프로토타입이다.
그런데 경우에 따라 객체를 생성하면서 Object의 프로토타입이 아닌
다른 프로토타입을 지정하고 싶은 경우가 있는데 이렇게 하고자 하는 주된 이유는
어떤 프로토타입을 지정해 객체를 생성해서 그 프로토타입에 등록된 멤버를 객체에서 이용하고자 한다.
일종의 상속개념으로 프로토타입을 지정해 그 프로토타입의 코드를 재사용하는 개념이며
이때 Object.create() 로 객체를 생성한다.
• Object.create() 로 객체를 생성할 때 첫번째 매개변수로 생성되는 객체의 프로토타입을 지정해 주어야 한다.
그리고 두번째 매개변수는 옵셔널로 생략가능한데 등록한다면 생성되는 객체의 프로퍼티이다.
객체의 프로퍼티를 지정하고 있는데 name: { } 형태로 프로퍼티를 등록해야 한다
name 은 프로퍼티 명이며 { } 은 프로퍼티의 설명자이다.
즉 value, writable, enumerable, configurable 등의 정보로 프로퍼티의 설명자를 등록해야 한다.
• Object.create() 를 이용하는 것은 등록하는 프로퍼티에 설명자를 객체를 생성하면서 지정하고자 하는 경우이다.
또다른 경우는 특정 프로토타입을 지정해서 코드 재사용을 한다.
"use strict"
//간단하게 객체를 생성해서 사용하는 방법 - object literal
let user1 = {
name: '홍길동',
age: 20
}
console.log(user1)//{name: '홍길동', age: 20}
console.dir(user1)
//this - name, age
//prototype - Object
//==>모든 객체는 prototype이 지정된다.
//==>위 object literal 기법으로 만든 객체는 아래처럼 만든것과 동일하다.
//prototype - 객체 정보
let user2 = Object.create(Object.prototype, {
name: {value: '홍길동'},
age: {value: 20}
})
console.log(user2)//{name: '홍길동', age: 20}
console.dir(user2)
//==>
//생성자 함수를 이용해 객체를 만들면 그 함수에 prototype 이 자동으로 만들어진다.
//그런데 object literal 기법으로 만들면 prototype 지정?
//Object.create() 로 객체를 생성하면서 원하는 prototype 을 지정해서 사용한다.
function Shape(name){
this.name = name
}
Shape.prototype.draw = function(){
console.log(`${this.name}을 그립니다.`)
}
let rect1 = Object.create(Shape.prototype, {
name: {value: 'rect1'},
width: {value:100},
height: {value:200}
})
rect1.draw()
console.dir(rect1)
this
• this 예약어는 코드를 실행시키는 객체를 의미하고 주로 함수 내에서 이용되며
함수를 호출한객체를 지칭하기 위해서 this 가 사용된다.
그런데 자바스크립트에서는 함수를 호출한 객체가 정적이지 않고
자바스크립트에서는 실행단계에서 동적으로 this 가 결정된다.
동일한 함수의 this 라고 하더라도 함수를 어떻게 호출했는지에 따라 다른 객체를 지칭하게 된다.
▼ 전역위치에 선언된 함수에서의 this
• 전역위치 함수 호출의 경우,
자바스크립트 코드를 엄격모드에서 작성하고 있는지에 따라 this 가 다르게 나올 수 있다.
• 일반모드에서는 전역위치를 실행시키는 객체는 별도로 명시하지 않아도 window 가 지정된다.
• 또한 엄격모드에서 전역위치 함수를 객체를 지정하지 않고 호출하게 되면
this 는 window 가 아니라 undefined상태이다.
▼ 함수 내에 선언된 함수에서의 this
• 함수 내에 선언된 함수를 호출하는 경우도 전역 위치의 함수 호출과 동일하다.
▼ 객체 메서드 호출시의 this
• 객체를 생성하고 그 객체의 메서드를 호출했을 때 메서드를 실행시키는 this는 메서드를 호출한 객체가 된다.
//객체 메서드의 this
function User2(arg1, arg2){
this.name = arg1
this.age = arg2
this.sayHello = function() {
console.log(`Hello ${this.name}, ${this.age}`)
}
}
let user2 = new User2('홍길동', 20)
let user3 = new User2('김길동', 30)
//현 순간의 sayHello 의 this 는 user2 로 호출하는 것임으로 user2
user2.sayHello()//Hello 홍길동, 20
//현 순간의 sayHello 의 this 는 user3 로 호출하는 것임으로 user3
user3.sayHello()//Hello 김길동, 30
▼ 생성자 함수에서의 this
• new 연산자에 의해 호출되는 시점의 this 는 새로 만들어지는 객체를 의미한다.
//생성자 함수내에서의 this
function User1(arg1){
//new 로 호출이 됨으로 호출하자 마자 빈 상태의 객체가 만들어지고.
//생성자 함수가 호출이 되는 동안의 this 는 그 객체
console.log(this)//User1 {}
this.data = arg1
console.log(this)//User1 {data: '홍길동'}
}
let user1 = new User1('홍길동')
console.log(user1)//User1 {data: '홍길동'}
▼ 화살표 함수
• 자바스크립트에서 함수가 실행될때의 this는
호출하는 시점에 결정되는 동적 바인딩이지만 화살표 함수에한해서만 정적 바인딩이 된다.
• 화살표 함수내의 this 는 호출하는 시점에 결정되는 것이 아니라 코드를 작성하는 시점,
즉 선언 시점에 결정된다.
이를 전문 용어로 렉시컬(Lexical) this 혹은 렉시컬 바인딩이라 부르며
화살표 함수의 this 는 선언되는 시점에 지정되며 화살표 함수의 상위 스코프에 있는 this가 지정된다.
▷ 화살표 함수 – 전역 위치에 선언
• 화살표 함수는 선언된 위치의 상위 스코프의 this 에 바인딩이 됨으로
전역 위치에 선언된 화살표 함수의 상위 스코프는 window 이다.
▷ 화살표 함수 – 생성자 함수내에 선언
• 화살표 함수는 선언한 순간 this 가 결정되며 상위 스코프의 this 에 등록되며
결국 this.fun3 = () => { } 로 등록했음으로 생성자 함수로 생성되는 객체에 대입된다.
▷ 화살표 함수 – 객체 리터럴에 선언
• 객체 리터럴 내의 함수를 화살표 함수로 선언하면 화살표 함수내에서의 this 는 생성되는 객체를 지칭하지 못한다.
//화살표 함수, 생성자 함수내에 선언될 때 this
function User3() {
this.data = 20
this.fun1 = function() {
console.log(this.data)
}
//화살표 함수, 선언 시점에 상위 스코프의 this 가 지정
this.fun2 = () => {
console.log(this.data)
}
}
let user4 = new User3()
user4.fun1()//20
user4.fun2()//20
//object literal 로 만든 객체의 함수, this
let obj = {
data: 30,
fun1: function(){
console.log(this.data)
},
fun2: () => {
console.log(this.data)
}
}
//function 예약어의 함수에서 this 는 동적 결정임으로 객체가 만들어 진후 함수가
//호출.
obj.fun1()//30
//화살표 함수는 lexical this, 작성 시점에 상위 스코프가 실행되어야 객체가 만들어진다.
//선언 시점에는 {} 는 객체를 만들기 위한 정보에 지나지 않는다. 스코프 아님
//위 예에서의 상위 scope 는 window
obj.fun2()//undefined
//==>화살표 함수는 함수를 간단하게 선언하고 싶을때 자주 이용.
//==>this 를 함수내에서 사용하지 않는 경우에 쓸 것을 권장하고 있다.
this 동적 바인딩
• 함수에서의 this 는 그 함수를 호출한 객체를 의미한다.
자바스크립트에서는 함수의 this 를 동적 바인딩이 가능하며 동적 바인딩이란
실행시점에 함수의 this 를 지정할 수 있다는 의미이다.
또한 이 동적 바인딩 기법을 이용해 함수의 this 역할을 하는 객체를 바꿔서 이용할 수 있다
▼ bind()
• bind() 함수는 함수에 this 역할을 하는 객체를 바인딩하여 새로운 함수를 반환하는 역할이다.
• bind() 는 함수를 호출하는 것이 아니라 새로운 함수를 만드는 역할이다..
• bind 되는 함수에 매개변수를 지정하고 새로 만들어지는 함수를 호출하면서 매개변수 값을 대입할 수 있다.
• bind() 로 함수에 객체를 바인딩 시키면서 매개변수 값을 지정할 수 있으며
또한 함수를 호출하면서 지정한 매개변수 값이 모두 같이 이용되게 할 수 있다.
let obj1 = {
name: '홍길동'
}
//생성자 함수가 아닌이상 혹은 어떤 객체에 동작 바인딩 되어 실행될
//함수가 아닌 이상 함수내에 this 는 안쓰는게 좋다.
let sayHello = function(){
console.log(`Hello, ${this.name}`)
}
// sayHello() //error
//==>위 객체와 함수는 별개이다
//의도하에 함수를 obj1 객체 안에 선언된 것처럼 돌리고자 할때
let newFun = sayHello.bind(obj1)
newFun()//Hello, 홍길동
▼ call(), apply()
bind()는 새로운 함수를 만드는 역할이지 함수를 호출하는 역할은 아니다.
• call(), apply() 를 이용하면 함수에 객체를 바인딩 시키고 그 함수를 호출까지 해주며
결국 call(), apply() 의 반환 값은 새로운 함수가 아니라 함수를 호출한 결과 값이다.
• call() 로 객체를 바인딩 시켜 함수를 호출할 수 있는데 이때 원한다면 매개변수 값을 전달 할 수 있다.
• apply() 함수도 call() 와 마찮가지로 함수에 객체를 바인딩 하면서 함수를 호출하는 역할을 한다.
apply() 함수가 call() 과 차이가 있는 것은 함수를 호출하면서 전달하는 매개변수 값을 지정하는 방법이다.
또한 apply() 는 전달하는 매개변수를 배열로 지정해야 한다.
"use strict"
let obj1 = {
name: '홍길동'
}
//생성자 함수가 아닌이상 혹은 어떤 객체에 동작 바인딩 되어 실행될
//함수가 아닌 이상 함수내에 this 는 안쓰는게 좋다.
let sayHello = function(){
console.log(`Hello, ${this.name}`)
}
// sayHello() //error
//==>위 객체와 함수는 별개이다
//의도하에 함수를 obj1 객체 안에 선언된 것처럼 돌리고자 할때
let newFun = sayHello.bind(obj1)
newFun()//Hello, 홍길동
//apply, call
//함수를 만들자 마자 호출까지..
let sayHello1 = function(arg1, arg2){
console.log(`Hello, ${this.name}, ${arg1}, ${arg2}`)
}
sayHello1.call(obj1, 10, 20)//Hello, 홍길동, 10, 20
sayHello1.apply(obj1, [10, 20])//Hello, 홍길동, 10, 20
※참고용 - 유사배열 객체
//사례 //어떤 함수가 있고, 객체가 있고. //함수가 객체의 멤버로 준비되지 않은 별개의 함수이지만 //마치 자신의 멤버로 등록된 함수처럼 사용하고 싶을때 //배열 let array = ['orange','yellow', 'green'] array.push('black') array.push('white') console.log(array.shift())//orange console.log(array.pop())//white console.log(array)//['yellow', 'green', 'black'] // 유사 배열 객체 생성 - 배열처럼 보임 let myArray = { 0: 'orange', 1: 'yellow', 2: 'green', length:3, push: function(e){ //마지막 index 에 e 값을 추가 this[this.length] = e this.length++ }, pop: function(){ //마지막 index 데이터 제거, 반환 let last = this[this.length - 1] this.length-- delete this[this.length] return last }, shift: function(){ //맨 앞에꺼 제거, 반환 let first = this[0] for(let i=0; i<this.length-1; i++){ this[i] = this[i+1] } this.length-- delete this[this.length] return first } } myArray.push('black') myArray.push('white') console.log(myArray.shift())//orange console.log(myArray.pop())//white console.log(myArray)//{0: 'yellow', 1: 'green', 2: 'black' //유사 배열 객체를 만들면서 배열에 있는 필요한 함수를 직접 알고리즘으로 구현했다 //Array 에 push, pop, shift 함수가 있지 않나? Array 에 선언된 함수이지만. //마치 나의 객체에 있는 함수처럼 연결해서 사용하면 된다. let myArray2 = { 0: 'orange', 1: 'yellow', 2: 'green', length:3, push: function(e){ Array.prototype.push.call(this, e) }, pop: function(){ return Array.prototype.pop.call(this) }, shift: function(){ return Array.prototype.shift.call(this) } } myArray2.push('black') myArray2.push('white') console.log(myArray2.shift())//orange console.log(myArray2.pop())//white console.log(myArray2)//{0: 'yellow', 1: 'green', 2: 'black'
getter/setter
• 소프트웨어 언어에서 흔히 사용하는 용어로 어떤 변수가 있고
그 변수의 값을 획득하기 위한 함수를 getter함수라고 부르고,
그 변수의 값을 변경하기 위한 함수를 setter 라고 부르며이 둘을 합쳐서 흔히 getter/setter 함수라고 한다
소프트웨어 언어별로 getter/setter 함수를 선언하는 문법의 차이는 있으며
자바스크립트에서는 get, set 예약어를 통해 변수의 getter/setter 를 추가한다.
• getter/setter은 함수 스타일로 프로퍼티를 정의할 수도 있으며 흔히 getter/setter 함수라고 부른다.
함수 스타일의 프로퍼티란 객체내에 선언된 함수인데 외부에서는 객체의 변수처럼 사용되는 프로퍼티를 의미한다.
일반적으로 어떤 객체가 가지고 있는 값을 위한 프로퍼티인데 이 프로퍼티에 값이 대입될 때 로직이 실행되어야 하거나 아니면 이 값이 참조될 때 로직이 실행되어야 하는 경우에 이용한다.
로직이 실행되어야 함으로 함수로 작성이 되고 그 함수가 호출이 되어야 하는데 외부에서는 이 함수를 변수처럼 이용한다는 개념이다.
• 자바스크립트에서 프로퍼티로 이용되는 getter/setter 함수를 만들기 위해서는
get, set 이라는 예약어로 함수가 선언되어 있어야 한다.
get 예약어로 선언된 함수를 getter 라고 부르며 프로퍼티 값을 참조할 때 호출 되고
set 예약어로 선언된 함수는 매개변수를 가지고 있어야 하며 이 프로퍼티 값을 변경할 때 호출된다.
• get 함수만 선언하면 함수를 프로퍼티로 활용할 수 있지만 get 만 있는 프로퍼티가 됨으로 값 참조만 되고변경은 불가능한 프로퍼티가 된다.
또한 set 만 선언된 함수를 만들 수도 있습니다. 이렇게 되면 값 대입만 되는 프로퍼티가 된다.
"use strict"
// let obj = {
// //이 값이 변경되는 순간 운용 로그를 남겨야 한다는 유지보수 사항이 발생했다면??
// setNum: (value) => {
// console.log(`어디선가 값 변경을 시도합니다.`)
// //~~~~
// }
// }
// obj.setNum(20)
// console.log(obj.num)
let obj = {
_num: 0,
get num(){
return this._num
},
set num(value){
console.log('운용로그를 남깁니다.')
this._num = value
}
}
obj.num = 20
console.log(obj.num)//20
'BootCamp Review' 카테고리의 다른 글
2024.10.01 (Day 18) - JavaScript OOP / 클로저(Closure) (0) | 2024.10.01 |
---|---|
2024.10.01 (Day 18) - JavaScript OOP / 클래스(class) (0) | 2024.10.01 |
2024.09.25-26 (Day 14,15) - JavaScript OOP / 프로토 타입 (0) | 2024.09.26 |
2024.09.24 (Day 13) - JavaScript OOP / 생성자 함수 (0) | 2024.09.24 |
2024.09.23 (Day 12) - JavaScript OOP / Object Literal (0) | 2024.09.24 |