const strObj = new String("aaa");
console.log(typeof strObj) // object
const numObj = new Number("aaa");
console.log(typeof numObj) // number
.
.
.
문자열이나 숫자, 불리언 등의 원시값이 있는데도 문자열, 숫자, 불리언 객체를 생성하는 String
, Number
, Boolean
등의 빌트인 생성자 함수가 존재하는 이유가 뭘까?
const str = "Hello"
console.log(str.length); // 5
console.log(str.toUpperCase()); // HELLO
위의 코드를 보면 원시타입인 str 변수는 객체도 아닌데 원시값이 문자열이 마치 객체처럼 동작한다.
이는 원시값인 문자열, 숫자, 불리언 값의 경우 이들 원시값에 대해 마치 객체처럼 마침표 표기법으로 접근하면 자바스크립트 엔진이 일시적으로 원시값을 연관된 객체로 변환해 주기 때문이다.
const str = "Hello"
// 원시 타입인 문자열이 래퍼 객체인 String 인스턴스로 변환된다.
console.log(str.length); // 5
console.log(str.toUpperCase()); // HELLO
// 래퍼 객체로 프로퍼티에 접근하거나 메서드를 호출한 후, 다시 원시값으로 되돌린다.
console.log(typeof str); // string
즉, 원시값을 객체처럼 사용하면 자바스크립트 엔진은 암묵적으로 연관된 객체를 생성하여 생성된 객체로 프로퍼티에 접근하거나 메서드를 호출하고 다시 원시값으로 되돌린다. 이때 임시 생성되는 객체를 래퍼 객체(wrapper object)
라고 한다.
const str = "hello"
// 식별자 str은 암묵적으로 생성된 래퍼 객체를 가리킨다.
// 래퍼 객체에 name 프로퍼티가 동적 추가된다.
str.name = "Lee"
// str은 다시 원래의 원시값으로 되돌려진다.
// 이때 래퍼 객체는 가비지 컬렉션의 대상이 된다.
// 식별자 str은 새롭게 생성된 또다른 래퍼 객체를 가리키지만
// 해당 래퍼객체에는 name 프로퍼티가 존재하지 않는다.
console.log(str.name) // undefined
원시값을 객체처럼 사용하고 난 이후, 원시값은 원래의 원시값으로 돌아가고 해당 래퍼객체는 가비지 컬렉션의 대상이 된다. 따라서 다시 원시값을 객체처럼 사용하면 이전 래퍼 객체는 사라졌기 때문에 이전에 할당하였던 프로퍼티들은 존재하지 않는다.
Javascript
는 최상위에 전역 객체를 가진다. 우리가 값을 선언하면 Javascript
의 모든 객체와 값들은 이 전역 객체 밑으로 들어가며, 오직 하나만 생성 가능하다.
브라우저에서는 window
로 전역객체에 접근이 가능하고
Node.js global
로 전역객체에 접근이 가능하다.
둘다 또한 globalThis
로 접근 가능하다. 브라우저에서 globalThis
는 window
이고, Node.js에서 globalThis
는 global
이 되는 것이다.
var foo = 1;
console.log(window.foo); // 1
bar = 2;
console.log(window.bar); // 2
var
키워드로 선언한 전역 변수와 선언하지 않은 변수에 값을 할당하면 암묵적 전역
즉, 전역 객체의 프로퍼티가 된다.
let foo = 123;
console.log(window.foo) // undefined
const bar = 456;
console.log(window.bar) // undefined
하지만 let
이나 const
키워드로 선언한 전역 변수는 전역 객체의 프로퍼티가 아니다. let
이나 const
키워드로 선언한 전역 변수는 보이지 않는 개념적인 블록내에 존재하게 된다.
var x = 10;
function foo() {
y = 20; // window.y = 20;
console.log(x + y);
}
foo() // 30
foo() 함수 안의 선언하지 않은 식별자 y는 마치 선언된 전역 변수 처럼 동작한다.
자바스크립트 엔진은 y = 20을 window.y = 20으로 해석하여 전역 객체에 프로퍼티를 동적생성한다.
이렇게 선언하지 않은 식별자에 값을 할당하면 전역 객체의 프로퍼티가 된다.
console.log(window.Infinity === Infinity)
console.log(window.NaN === NaN)
console.log(window.undefined === undefined)
isNaN(1O) // false
isNaN(NaN) // true
parseInt(10.123) // 10
encodeURIComponent(url);
decodeURIComponent(env);
전역객체는 Object, String, Number, Booelean… 등등 모든 표준 빌트인 객체를 프로퍼티로 가지고있다.
우리가 사용하는 여러 프로퍼티나, 함수들은 전역 객체의 프로퍼티, 전역 함수이다.
Javascript에서 this
는 나를 부른 대상
즉 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기참조 변수이다. this
의 대상은 런타임에 결정된다.
아무것도 없이 this를 호출하는 경우엔 글로벌 객체
를 가리킨다.
브라우저에서 호출하는 경우 window
, nodejs에서는 global
이 된다.
function User() {
return this;
}
console.log(User()); // window Or global
함수 안에서 this는 함수의 주인에게 바인딩된다. 함수는 주인은 글로벌 객체이기 때문에 window
나 global
이 호출 된다.
다만 this는 객체의 프로퍼티나 메서드를 참조하기 위한 자기 참조 변수이므로 객체를 생성하지 않는 일반 함수에서 this는 의미가 없다. 따라서 strict mode
의 일반함수의 this는 undefined
가 바인딩 된다.
var user = {
name: "minwoo"
myOwner: function() {
return this;
}
};
user.myOwner();
그렇다면 객체안의 메서드에서는 어떨까?
메서드 호출 시 메서드 내부 코드에서 사용된 this
는 해당 메서드를 호출한 객체로 바인딩된다.
var btn = document.querySelector('#btn')
btn.addEventListener('click', function () {
console.log(this); // #btn
});
이벤트 핸들러에서 this
는 이벤트를 받는 HTML 요소를 가리킨다.
function User(name) = {
this.name = name;
};
var user1 = new User("minwoo");
console.log(user1.name); // minwoo
생성자 함수에서도 마찬가지로 생성자 함수가 생성하는 객체로 this
가 바인딩 된다. 이는 class에서도 동일하게 작용한다.
var name = "whoAreYou";
function User(name) = {
this.name = name;
};
var user1 = User("minwoo");
console.log(window.name); // minwoo
하지만 new
키워드를 빼먹는 순간 일반 함수 호출과 같아지기 때문에, 이 경우는 this
가 window
에 바인딩되고, 맨 윗줄의 전역 객체의 변수인 name이 변경된 것이다.
function User(name) {
this.name = name;
this.whoAreYou = function() {
console.log(this); // User { name: "minwoo" }
(function() {
console.log(this); // window or global
console.log("my name is.." + this.name);
})();
}
}
const user = new User("minwoo");
console.log(user.whoAreYou()); // my name is.. undefined
함수 안에서 this
는 함수의 주인에게 바인딩되고, 함수는 주인은 글로벌 객체이기 때문에 window나 global이 호출 된 다고 위에서 말했다.
여기서 whoAreYou의 메서드 주인은 User객체이지만, 밑의 즉시 실행함수의 주인은 전역 객체가 된다. 따라서 즉시실행함수안의 this.name은 undefined가 되는 것이다.
function User(name) {
this.name = name;
this.whoAreYou = function() {
console.log(this); // User { name: "minwoo" }
(() => {
console.log(this); // User { name: "minwoo" }
console.log("my name is.." + this.name);
})();
}
}
const user = new User("minwoo");
console.log(user.whoAreYou()); // my name is.. minwoo
이럴때는 화살표 함수를 사용하면 된다. 화살표 함수는 전역 컨텍스트에서 실행되더라도 this
를 새로 정의하지 않고, 바로 바깥 함수나 클래스의 this
를 쓰기 때문이다. 따라서 화살표 함수로 정의된 즉시 실행함수의 this
는 바로 바깥 함수의 this
인 User객체가 된다.