poiemaweb / javascript스터디 4
1. 타입체크
자바스크립트는 동적 타입(dynamic typed) 언어이므로 변수에 어떤 값이 할당될 지 예측하기 어렵다.
이와 같은 이유로 자바스크립트는 타입 체크가 필요하다.
typeof
- 타입 연산자(Type Operator) typeof는 피연산자의 데이터 타입을 문자열로 반환한다.
- null을 제외환 원시 타입은 구별을 하지만 그 외의 경우 function을 제외하고 구분을 못 한다.
typeof ''; // string
typeof 1; // number
typeof NaN; // number
typeof true; // boolean
typeof []; // object
typeof {}; // object
typeof new String(); // object
typeof new Date(); // object
typeof /test/gi; // object
typeof function () {}; // function
typeof undefined; // undefined
typeof null; // object (설계적 결함)
typeof undeclared; // undefined (설계적 결함)
Object.prototype.toString
- Object.prototype.toString 메소드는 객체를 나타내는 문자열을 반환한다.
var obj = new Object();
obj.toString(); // [object Object]
- Function.prototype.call 메소드를 사용하면 모든 타입의 값의 타입을 알아낼 수 있다.
Object.prototype.toString.call(''); // [object String]
Object.prototype.toString.call(new String()); // [object String]
Object.prototype.toString.call(1); // [object Number]
Object.prototype.toString.call(new Number()); // [object Number]
Object.prototype.toString.call(NaN); // [object Number]
Object.prototype.toString.call(Infinity); // [object Number]
Object.prototype.toString.call(true); // [object Boolean]
Object.prototype.toString.call(undefined); // [object Undefined]
Object.prototype.toString.call(); // [object Undefined]
Object.prototype.toString.call(null); // [object Null]
Object.prototype.toString.call([]); // [object Array]
Object.prototype.toString.call({}); // [object Object]
Object.prototype.toString.call(new Date()); // [object Date]
Object.prototype.toString.call(Math); // [object Math]
Object.prototype.toString.call(/test/i); // [object RegExp]
Object.prototype.toString.call(function () {}); // [object Function]
Object.prototype.toString.call(document); // [object HTMLDocument]
Object.prototype.toString.call(argument); // [object Arguments]
Object.prototype.toString.call(undeclared); // ReferenceError
- 이 것을 통하여 type을 체크하는 함수를 만들 수 있다.
function getType(target) {
return Object.prototype.toString.call(target).slice(8, -1);
}
- 위에서 만든 getType을 응용하여 타입별로 체크하는 함수들을 만들 수도 있다.
instanceof
- 타입 연산자(Type Operator)이다.
- instanceof 연산자는 피연산자인 객체가 우항에 명시한 타입의 인스턴스인지 여부를 알려준다. 이때 타입이란 constructor를 말하며 프로토타입 체인에 존재하는 모든 constructor를 검색하여 일치하는 constructor가 있다면 true를 반환한다.
target instanceof HTMLElement; //true or false
유사 배열 객체
- 배열인지 체크하기 위해서는 Array.isArray 메소드를 사용한다.
- 유사 배열 객체(array-like object)은 length 프로퍼티를 갖는 객체로 문자열, arguments, HTMLCollection, NodeList 등은 유사 배열이다.
- 유사 배열 객체는 length 프로퍼티가 있으므로 순회할 수 있으며 call, apply 함수를 사용하여 배열의 메소드를 사용할 수도 있다.
- 어떤 객체가 유사 배열인지 체크하려면 우선 length 프로퍼티를 갖는지 length 프로퍼티의 값이 정상적인 값인지 체크한다.
const isArrayLike = function (collection) {
// 배열 인덱스: 32bit 정수(2의 32제곱 - 1)
// 유사 배열 인덱스: 자바스크립트로 표현할 수 있는 양의 정수(2의 53제곱 - 1)
const MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
// 빈문자열은 유사배열이다. undefined == null => true
const length = collection == null ? undefined : collection.length;
return typeof length === 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
};
2. 프로토타입
프로토타입 객체
- 자바스크립트는 프로토타입 기반 객체지향 프로그래밍 언어이다.
- 클래스 기반 객체지향 프로그래밍 언어는 객체 생성 이전에 클래스를 정의하고 이를 통해 객체(인스턴스)를 생성한다.
- 프로토타입 기반 객체지향 프로그래밍 언어는 클래스 없이(Class-less)도 (ECMAScript 6에서 클래스가 추가되었다) 객체를 생성할 수 있다.
- 자바스크립트의 모든 객체는 자신의 부모 역할을 담당하는 객체와 연결되어 있고 객체 지향의 상속 개념과 같이 부모 객체의 프로퍼티 또는 메소드를 상속받아 사용할 수 있다. 이러한 부모 객체를 Prototype(프로토타입) 객체 또는 줄여서 Prototype(프로토타입)이라 한다.
- Prototype 객체는 생성자 함수에 의해 생성된 각각의 객체에 공유 프로퍼티를 제공하기 위해 사용한다.
var student = {
name: 'Lee',
score: 90
};
// student에는 hasOwnProperty 메소드가 없지만 아래 구문은 동작한다.
console.log(student.hasOwnProperty('name')); // true
- ECMAScript spec에서는 자바스크립트의 모든 객체는 [[Prototype]]이라는 인터널 슬롯(internal slot)를 가진다고 나와 있다.
- [[Prototype]]의 값은 null 또는 객체이며 상속을 구현하는데 사용된다.
- [[Prototype]] 객체의 데이터 프로퍼티는 get 액세스를 위해 상속되어 자식 객체의 프로퍼티처럼 사용할 수 있다. 하지만 set 액세스는 허용되지 않는다.
- [[Prototype]]의 값은 Prototype(프로토타입) 객체이며 __proto__ accessor property로 접근할 수 있다. __proto__ 프로퍼티에 접근하면 내부적으로 Object.getPrototypeOf가 호출되어 프로토타입 객체를 반환한다.
var student = {
name: 'Lee',
score: 90
}
console.log(student.__proto__ === Object.prototype); // true
- 프로토타입 객체는 다른 임의의 객체로 변경할 수 있다. 이것은 부모 객체인 프로토타입을 동적으로 변경할 수 있다는 것을 의미한다. 이러한 특징을 활용하여 객체의 상속을 구현할 수 있다.
[[Prototype]] vs prototype 프로퍼티
-
모든 객체는 자신의 프로토타입 객체를 가리키는 [[Prototype]] 인터널 슬롯(internal slot) 을 갖으며 상속을 위해 사용된다.
-
함수도 객체이므로 [[Prototype]] 인터널 슬롯을 갖는다. 그런데 함수 객체는 일반 객체와는 달리 prototype 프로퍼티도 소유하게 된다.
-
prototype 프로퍼티와 [[Prototype]]은 모두 프로토타입 객체를 가리키지만 관점의 차이가 있다.
-
[[Prototype]]
- 함수를 포함한 모든 객체가 가지고 있는 인터널 슬롯이다.
- 객체의 입장에서 자신의 부모 역할을 하는 프로토타입 객체를 가리킨다.
- 함수 객체의 경우 Function.prototype을 가리킨다.
console.log(Person.__proto__ === Function.prototype);
-
prototype 프로퍼티
- 함수 객체만 가지고 있는 프로퍼티이다.
- 함수 객체가 생성자로 사용될 때 이 함수를 통해 생성 될 객체의 부모 역할을 하는 객체(프로토타입 객체)를 가리킨다.
console.log(Person.prototype === foo.__proto__);
constructor 프로퍼티
- 프로토타입 객체는 constructor 프로퍼티를 갖는다.
- constructor 프로퍼티는 자식 객체의 입장에서 자신을 생성한 객체를 가리킨다.
- Person() 생성자 함수에 의해 생성된 객체를 foo라 하자. 이 foo 객체를 생성한 객체는 Person() 생성자 함수이다. foo 객체의 프로토타입 객체는 Person.prototype이며 person.prototype의 constructor 프로퍼티는 Person() 생성자 함수이다.
function Person(name) {
this.name = name;
}
var foo = new Person('Lee');
// Person() 생성자 함수에 의해 생성된 객체를 생성한 객체는 Person() 생성자 함수이다.
console.log(Person.prototype.constructor === Person);
// foo 객체를 생성한 객체는 Person() 생성자 함수이다.
console.log(foo.constructor === Person);
// Person() 생성자 함수를 생성한 객체는 Function() 생성자 함수이다.
console.log(Person.constructor === Function);
Prototype chain
- 자바스크립트는 특정 객체의 프로퍼티나 메소드에 접근하려고 할 때 해당 객체에 접근하려는 프로퍼티 또는 메소드가 없다면 [[Prototype]]이 가리키는 링크를 따라 자신의 부모 역할을 하는 프로토타입 객체의 프로퍼티나 메소드를 차례대로 검색한다. 이것을 프로토타입 체인이라 한다.
var student = {
name: 'Lee',
score: 90
}
// Object.prototype.hasOwnProperty()
console.log(student.hasOwnProperty('name')); // true
- 객체 리터럴 방식으로 생성된 객체의 프로토타입 체인
- 객체 리터럴 방식으로 생성된 객체는 결국 내장 함수(Built-in)인 Object() 생성자 함수로 객체를 생성하는 것을 단순화시킨 것이다.
- 함수 객체인 Object() 생성자 함수는 일반 객체와 달리 prototype 프로퍼티가 있다.
- prototype 프로퍼티는 함수 객체가 생성자로 사용될 때 이 함수를 통해 생성된 객체의 부모 역할을 하는 객체, 즉 프로토타입 객체를 가리킨다.
var person = {
name: 'Lee',
gender: 'male',
sayHello: function(){
console.log('Hi! my name is ' + this.name);
}
};
console.dir(person);
console.log(person.__proto__ === Object.prototype); // ① true
console.log(Object.prototype.constructor === Object); // ② true
console.log(Object.__proto__ === Function.prototype); // ③ true
console.log(Function.prototype.__proto__ === Object.prototype); // ④ true
Object literal Prototype chaining
var student = {
name: 'Lee',
score: 90
};
// student에는 hasOwnProperty 메소드가 없지만 아래 구문은 동작한다.
console.log(student.hasOwnProperty('name')); // true
- [[Prototype]]의 값은 null 또는 객체이며 상속을 구현하는데 사용된다.
- [[Prototype]] 객체의 데이터 프로퍼티는 get 액세스를 위해 상속되어 자식 객체의 프로퍼티처럼 사용할 수 있다. 하지만 set 액세스는 허용되지 않는다.
- [[Prototype]]의 값은 Prototype(프로토타입) 객체이며 __proto__ accessor property로 접근할 수 있다. __proto__ 프로퍼티에 접근하면 내부적으로 Object.getPrototypeOf가 호출되어 프로토타입 객체를 반환한다.
var student = {
name: 'Lee',
score: 90
}
console.log(student.__proto__ === Object.prototype); // true
- 프로토타입 객체는 다른 임의의 객체로 변경할 수 있다. 이것은 부모 객체인 프로토타입을 동적으로 변경할 수 있다는 것을 의미한다. 이러한 특징을 활용하여 객체의 상속을 구현할 수 있다.
모든 객체는 자신의 프로토타입 객체를 가리키는 [[Prototype]] 인터널 슬롯(internal slot) 을 갖으며 상속을 위해 사용된다.
함수도 객체이므로 [[Prototype]] 인터널 슬롯을 갖는다. 그런데 함수 객체는 일반 객체와는 달리 prototype 프로퍼티도 소유하게 된다.
prototype 프로퍼티와 [[Prototype]]은 모두 프로토타입 객체를 가리키지만 관점의 차이가 있다.
[[Prototype]]
- 함수를 포함한 모든 객체가 가지고 있는 인터널 슬롯이다.
- 객체의 입장에서 자신의 부모 역할을 하는 프로토타입 객체를 가리킨다.
- 함수 객체의 경우 Function.prototype을 가리킨다.
console.log(Person.__proto__ === Function.prototype);
prototype 프로퍼티
- 함수 객체만 가지고 있는 프로퍼티이다.
- 함수 객체가 생성자로 사용될 때 이 함수를 통해 생성 될 객체의 부모 역할을 하는 객체(프로토타입 객체)를 가리킨다.
console.log(Person.prototype === foo.__proto__);
- Person() 생성자 함수에 의해 생성된 객체를 foo라 하자. 이 foo 객체를 생성한 객체는 Person() 생성자 함수이다. foo 객체의 프로토타입 객체는 Person.prototype이며 person.prototype의 constructor 프로퍼티는 Person() 생성자 함수이다.
function Person(name) {
this.name = name;
}
var foo = new Person('Lee');
// Person() 생성자 함수에 의해 생성된 객체를 생성한 객체는 Person() 생성자 함수이다.
console.log(Person.prototype.constructor === Person);
// foo 객체를 생성한 객체는 Person() 생성자 함수이다.
console.log(foo.constructor === Person);
// Person() 생성자 함수를 생성한 객체는 Function() 생성자 함수이다.
console.log(Person.constructor === Function);
var student = {
name: 'Lee',
score: 90
}
// Object.prototype.hasOwnProperty()
console.log(student.hasOwnProperty('name')); // true
- 객체 리터럴 방식으로 생성된 객체는 결국 내장 함수(Built-in)인 Object() 생성자 함수로 객체를 생성하는 것을 단순화시킨 것이다.
- 함수 객체인 Object() 생성자 함수는 일반 객체와 달리 prototype 프로퍼티가 있다.
- prototype 프로퍼티는 함수 객체가 생성자로 사용될 때 이 함수를 통해 생성된 객체의 부모 역할을 하는 객체, 즉 프로토타입 객체를 가리킨다.
var person = {
name: 'Lee',
gender: 'male',
sayHello: function(){
console.log('Hi! my name is ' + this.name);
}
};
console.dir(person);
console.log(person.__proto__ === Object.prototype); // ① true
console.log(Object.prototype.constructor === Object); // ② true
console.log(Object.__proto__ === Function.prototype); // ③ true
console.log(Function.prototype.__proto__ === Object.prototype); // ④ true
Object literal Prototype chaining
-
생성자 함수로 생성된 객체의 프로토타입 체인
- 생성자 함수로 객체를 생성하기 위해서는 우선 생성자 함수를 정의하여야 한다.
- 함수 선언식과 함수표현식은 함수 리터럴 방식을 사용하고 함수 리터럴 방식은 Function()생성자 함수를 사용하는 방식을 단순화 시킨 것이다.
- 모든 함수 객체의 [[prototype]]는 Function.prototype이다. 생성자 함수도 함수 객체이므로 생성자 함수의 [[prototype]] 객체는 Function.prototype이다.
function Person(name, gender) { this.name = name; this.gender = gender; this.sayHello = function(){ console.log('Hi! my name is ' + this.name); }; } var foo = new Person('Lee', 'male'); console.dir(Person); console.dir(foo); console.log(foo.__proto__ === Person.prototype); // ① true console.log(Person.prototype.__proto__ === Object.prototype); // ② true console.log(Person.prototype.constructor === Person); // ③ true console.log(Person.__proto__ === Function.prototype); // ④ true console.log(Function.prototype.__proto__ === Object.prototype); // ⑤ true
- 객체 리터럴 방식이나 생성자 함수 방식이나 결국은 모든 객체의 부모 객체인 Object.prototype 객체에서 프로토타입 체인이 끝난다. Object.prototype 객체를 프로토타입 체인의 종점(End of prototype chain)이라 한다.
프로토타입 객체의 확장
- 프로토타입 객체도 객체이므로 일반 객체와 같이 프로퍼티를 추가/삭제할 수 있다. 그리고 이렇게 추가/삭제된 프로퍼티는 즉시 프로토타입 체인에 반영된다.
function Person(name) {
this.name = name;
}
var foo = new Person('Lee');
Person.prototype.sayHello = function(){
console.log('Hi! my name is ' + this.name);
};
foo.sayHello();
원시 타입(Primitive data type)의 확장
- 자바스크립트에서 원시 타입(숫자, 문자열, boolean, null, undefined)을 제외한 모든것은 객체이다. 그런데 원시 타입인 문자열이 객체와 유사하게 동작한다.
var str = 'test';
console.log(typeof str); // string
console.log(str.constructor === String); // true
console.dir(str); // test
var strObj = new String('test');
console.log(typeof strObj); // object
console.log(strObj.constructor === String); // true
console.dir(strObj);
// {0: "t", 1: "e", 2: "s", 3: "t", length: 4, __proto__: String, [[PrimitiveValue]]: "test" }
console.log(str.toUpperCase()); // TEST
console.log(strObj.toUpperCase()); // TEST
- 원시 타입으로 프로퍼티나 메소드를 호출할 때 원시 타입과 연관된 객체로 일시적으로 변환되어 프로토타입 객체를 공유하게 된다.
- 원시 타입은 객체가 아니므로 프로퍼티나 메소드를 직접 추가할 수 없다.
- String 객체의 String.prototype에 메소드를 추가하면 원시 타입, 객체 모두 메소드를 사용할 수 있다.
프로토타입 객체의 변경
- 부모 객체의 프로토타입 객체는 다른 임의의 객체로 변경 할 수 있다.
- 프로토타입 객체 변경 이전에 해당 프로토타입 객체를 부모로 둔 객체는 기존 프로토타입 객체에 연결되어있다.
- 새롭게 변경한 프로토타입 객체는 변경 이후 생성된 객체에 연결된다.
function Person(name) {
this.name = name;
}
var foo = new Person('Lee');
// 프로토타입 객체의 변경
Person.prototype = { gender: 'male' };
var bar = new Person('Kim');
console.log(foo.gender); // undefined
console.log(bar.gender); // 'male'
console.log(foo.constructor); // ① Person(name)
console.log(bar.constructor); // ② Object()
changing prototype
프로토타입 체인 동작 조건
- 객체의 프로퍼티를 참조하는 경우
- 해당 객체에 프로퍼티가 없는 경우
- 객체의 프로퍼티에 값을 할당하는 경우, 프로토타입 체인이 동작하지 않는다.
3. 스코프
스코프란?
- 스코프는 참조 대상 식별자(identifier, 변수, 함수의 이름과 같이 어떤 대상을 다른 대상과 구분하여 식별할 수 있는 유일한 이름)를 찾아내기 위한 규칙이다. 자바스크립트는 이 규칙대로 식별자를 찾는다.
스코프의 구분
- 전역 스코프 (Global scope)
- 코드 어디에서든지 참조할 수 있다.
- 지역 스코프 (Local scope or Function-level scope)
- 함수 코드 블록이 만든 스코프로 함수 자신과 하위 함수에서만 참조할 수 있다.
모든 변수는 스코프를 갖는다.
- 변수는 선언 위치(전역 또는 지역)에 의해 스코프를 가지게 된다.
- 전역 변수 (Global variable)
- 전역 스코프를 갖는 변수로 전역에서 선언된 함수
- 지역 변수 (Local variable)
- 지역(함수) 내에서 선언된 변수이며 그 지역과 그 지역의 하부 지역에서만 참조할 수 있다.
자바스크립트 스코프의 특징
- 자바스크립트는 함수 레벨 스코프(function-level scope)를 따른다. 함수 레벨 스코프란 함수 코드 블록 내에서 선언된 변수는 함수 코드 블록 내에서만 유효하고 함수 외부에서는 유효하지 않다(참조할 수 없다)는 것이다.
- 단, ECMAScript 6에서 도입된 let keyword를 사용하면 블록 레벨 스코프를 사용할 수 있다.
전역 스코프(Global scope)
- 전역에 변수를 선언하면 이 변수는 어디서든지 참조할 수 있는 전역 스코프를 갖는 전역 변수가 된다. - var 키워드로 선언한 전역 변수는 전역 객체(Global Object) window의 프로퍼티이다.
- 전역 변수의 사용은 변수 이름이 중복될 수 있고, 의도치 않은 재할당에 의한 상태 변화로 코드를 예측하기 어렵게 만드므로 사용을 억제하여야 한다.
비 블록 레벨 스코프(Non block-level scope)
- var을 통해 코드 블록 내에 선언된 변수는 전역 스코프를 갖는다.
함수 레벨 스코프(Function-level scope)
- 함수 내에서 선언된 매개변수와 변수는 함수 외부에서는 유효하지 않다.
- 전역변수와 지역변수의 이름의 동일한 경우 지역변수를 우선하여 참조한다.
- 함수 레벨 스코프에서 외부 함수에서 선언된 변수나 전역 변수를 접근 및 변경 가능하다.
렉시컬 스코프
- 동적 스코프(Dynamic scope)
- 함수를 어디서 호출하였는지에 따라 함수의 상위 스코프를 결정하는 방식
- 렉시컬 스코프(Lexical scope)
- 함수를 어디서 선언하였느냐에 따라 함수의 상위 스코프를 결정하는 방식
- 자바스크립트는 렉시컬 스코프이다.
암묵적 전역
- 식별자를 var등 예약어 없이 선언 하면 해당 식별자를 전역 객체의 프로퍼티에 추가한다.
- 하지만 해당 식별자에 대해 변수 호이스팅이 발생하지는 않는다.
- 또한 해당 식별자는 변수와 달리 delete 연산자로 삭제할 수 있다.
최소한의 전역변수 사용
- 전역변수 사용을 최소화하는 방법 중 하나는 애플리케이션에서 전역변수 사용을 위해 다음과 같이 전역변수 객체 하나를 만들어 사용하는 것이다. (더글라스 크락포드의 제안)
var MYAPP = {};
MYAPP.student = {
name: 'Lee',
gender: 'male'
};
console.log(MYAPP.student.name);
즉시실행함수를 이용한 전역변수 사용 억제
- 전역변수 사용을 억제하기 위해, 즉시 실행 함수(IIFE, Immediately-Invoked Function Expression)를 사용할 수 있다.
- 라이브러리 등에 자주 사용된다. 즉시 실행 함수는 즉시 실행되고 그 후 전역에서 바로 사라진다.
(function () {
var MYAPP = {};
MYAPP.student = {
name: 'Lee',
gender: 'male'
};
console.log(MYAPP.student.name);
}());
console.log(MYAPP.student.name);
Author And Source
이 문제에 관하여(poiemaweb / javascript스터디 4), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://velog.io/@candyroom136/poiemaweb-javascript스터디-4
저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
- 코드 어디에서든지 참조할 수 있다.
- 함수 코드 블록이 만든 스코프로 함수 자신과 하위 함수에서만 참조할 수 있다.
모든 변수는 스코프를 갖는다.
- 전역 변수 (Global variable)
- 전역 스코프를 갖는 변수로 전역에서 선언된 함수
- 지역 변수 (Local variable)
- 지역(함수) 내에서 선언된 변수이며 그 지역과 그 지역의 하부 지역에서만 참조할 수 있다.
- 단, ECMAScript 6에서 도입된 let keyword를 사용하면 블록 레벨 스코프를 사용할 수 있다.
- 함수를 어디서 호출하였는지에 따라 함수의 상위 스코프를 결정하는 방식
- 함수를 어디서 선언하였느냐에 따라 함수의 상위 스코프를 결정하는 방식
- 자바스크립트는 렉시컬 스코프이다.
var MYAPP = {};
MYAPP.student = {
name: 'Lee',
gender: 'male'
};
console.log(MYAPP.student.name);
(function () {
var MYAPP = {};
MYAPP.student = {
name: 'Lee',
gender: 'male'
};
console.log(MYAPP.student.name);
}());
console.log(MYAPP.student.name);
Author And Source
이 문제에 관하여(poiemaweb / javascript스터디 4), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@candyroom136/poiemaweb-javascript스터디-4저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)