출처 : https://chanspark.github.io/2017/07/13/%ED%81%AC%EB%A1%AC-%EC%9E%90%EB%8F%99%EC%99%84%EC%84%B1-%EA%B8%B0%EB%8A%A5-%EC%A0%9C%EA%B1%B0%ED%95%98%EA%B8%B0.html


회원가입, 로그인 폼에는 꼭 패스워드와 아이디, 이름, 주소, 카드번호, 카드 비밀번호 등등 다양한 개인정보를 작성해야하는 페이지가 존재합니다. 구글 크롬은 같은 정보를 여러 사이트에서 쉽게 입력할 수 있도록 자동완성기능(autocomplete)이란것을 지원합니다. 문제는 이 자동완성기능이 사이트에서 필요로 하지 않는 정보까지 포함하고 있기때문에 보안 문제가 많다고 하네요.크롬의 자동완성기능의 보안 문제와 해결법-영문그래서 저는 개인적으로 이 기능을 off 하고 사용하고 있지만 저희 고객분들은 그런 기능을 잘 모르는 분들이 많겠죠!! 그렇다고 일일이 고객분들께 사용하지 말아달라고 부탁할 수도 없는 노릇이고… 하니 사이트에서 자체적으로 이 기능을 block 해버리는 방법을 찾아보게 되었습니다.

방법 1 (X)

input  form autocomplete="off"라는 속성값을 부여합니다.

<input type="text" id="..." name="..." autocomplete="off">

인터넷 익스플로러에서는 이 방법이 먹히지만 크롬과 같은 최신 브라우저에서는 통하지 않습니다. 몇년전에 사용하던 방법이었는데 크롬에서 막아버렸다고 하죠. 사유는 뭐 개발자들이 무분별하게 사용해서 이를 방지하는 차원이라는데… 그냥 없애달라고 ㅠㅠㅠ

방법 2 (X)

방법 1이 안된다면, autocomplete="false" 등으로 바꿔보는 방법도 있습니다.

방법 3 (X)

방법 2도 안된다면… 비밀번호 입력칸에 autocomplete="new-password"라고 입력해서 항상 새로운 비밀번호를 입력받는다는 꼼수를 부릴 수도 있습니다.

방법 4

이쯤 와서 정말 화가나기 시작했습니다. 다른일 해야하는데 이거때문에 시간 질질끌리고…. 몇번 더 구글링한 결과 다른 방법을 찾았습니다!!!

먼저 자동완성 기능이 활성화 되는 시점은 form 이나 전체 페이지에서 처음으로 오는 <input type="password"> 요소를 찾아서 바로 앞에 위치하는 input과 자동완성 기능을 띄워주는 방식입니다. 이를 활용하면, 더미 입력폼을 만들어서 해결할 수 있습니다.

<!--remove autocomplete-->
<input style="display:none" aria-hidden="true">
<input type="password" style="display:none" aria-hidden="true">
<!--remove autocomplete end-->
<!--real input start-->
<input type="text"  autocomplete="false" required>
<input type="password" name="password" autocomplete="new-password">
<!--real input end-->

실제로 저는 이렇게 해서 효과를 봤습니다. 완벽하게는 아니지만 password 전에 위치한 인풋창에는 자동완성 기능이 뜨지 않습니다(제 원래 목표). 여전히 password인풋에는 자동완성 창이 뜨네요… ㅠㅠ

번외 - 모바일 인풋 대문자 입력 방지

모바일(특히 iOS)에서는 인풋에서 입력을 시작하면 첫 글자가 대문자로 입력이 됩니다. 하지만 아이디나 이메일 입력창에서 첫 글자가 대문자인 경우는 드물기 때문에 아래 코드릴 입력하시면 좋습니다. 은근히 UX를 개선하는 방법중 하나랄까..

<input type="email" autocapitalize="off">

참고

관련 문제에 대한 스택오버플로 질문글



출처 : http://webframeworks.kr/tutorials/translate/explanation-of-this-in-javascript-1/

참고 : https://hyunseob.github.io/2016/03/10/javascript-this/

1. this에 대한 미스터리

많은 시간 동안 this 키워드는 자바스크립트 개발자들에게 미스터리의 대상이었다. this는 강력한 기능임에도 불구하고 쉽게 이해하기 힘든 부분이다.

Java, PHP와 같은 언어에서 this는 클래스로부터 생성되는 인스턴스 중 현재 객체를 의미한다. 그 이상 그 이하도 아니다. 대부분 클래스 밖에서는 사용될 수 없으며 이러한 접근 방법으로는 혼란이 생기지 않는다.

자바스크립트에서 this는 함수의 현재 실행 문맥이다. 자바스크립트에는 4가지의 함수 실행 타입이 있기 때문이다.

  • 함수 실행: alert('Hello World!')
  • 메소드 실행: console.log('Hello World!')
  • 생성자 실행: new RegExp('\d')
  • 간접 실행: alert.call(undefined, 'Hello World!')

각각의 타입은 서로 다른 각각의 문맥을 가진다. 이 부분에서 개발자들의 예상과 다른 부분이 생긴다.

더욱이 엄격 모드 역시 실행 문맥에 영향을 미친다.

this 키워드를 이해할 수 있는 방법은 함수 실행, 함수 실행이 문맥에 어떤 영향을 미치는지에 대해 확실히 이해하는 것이다.

이 글은 실행에 대한 설명에 초점을 맞췄다. 그리고 함수 호출이 this에 어떤 영향을 미치는지, 그리고 문맥을 식별하는 과정에서 저지르는 일반적인 실수들을 보여줄 것이다.

시작하기 전에 아래의 용어들과 친숙해지자.

  • 실행이란, 함수 내부에 있는 코드를 수행하는 것이다. (쉽게 말해 함수 호출이라고 보면 됨) 예를 들어 parseInt라는 함수는 parseInt('15', 10)로 실행된다.
  • 실행 시점에서의 문맥은 함수 내부에서의 값이다.
  • 함수의 스코프는 변수, 객체, 내부 함수들의 집합이다.

2. 함수 실행

함수 실행은 함수 객체로 계산 될 표현식이 열림 괄호, 콤마로 구분되는 인자들, 그리고 닫힘 괄호와 함께 수행된다. parseInt('18')가 함수 실행의 예제다.

이 표현식은 myObject.myFunction와 같이 메소드를 실행하기 위해 사용하는 속성 접근자가 될 수 없다. 예를 들어 [1,5].join(',')는 함수 실행이 아니라 메소드 호출이다.

함수 실행에 대한 간단한 예제

function hello(name) {
  return 'Hello ' + name + '!';
}
// 함수 실행
var message = hello('World');
console.log(message); // => 'Hello World!'

hello('World')은 함수 실행이다. hello표현식은 뒤따라 오는 'World' 인자와 함께 함수 객체로 계산 될 것이다.

A more advanced example is the IIFE (immediately-invoked function expression): 심화 예제로 IIFE(즉시실행함수)가 있다.

var message = (function(name) {
   return 'Hello ' + name + '!';
})('World');
console.log(message) // => 'Hello World!'

즉시실행함수 역시 함수 실행의 종류 중 하나다. 앞 부분에 위치한 괄호 한 쌍은 함수 객체로 바뀔 표현식이고, 뒤에 위치한 괄호 한 쌍은 함수에 전달될 인자들이다. 여기에서는 'World'가 인자다.

2.1 함수 실행에서의 this

함수 실행에서의 this는 전역 객체다.

전역 객체는 실행 환경에 따라 결정된다. 웹 브라우저에서는 window 객체가 전역 객체다.

함수 실행에서의 실행 문맥은 전역 객체다.

아래의 함수 예제를 통해 문맥을 체크해보자.

function sum(a, b) {
   console.log(this === window); // => true
   this.myNumber = 20; // 전역 객체에 'myNumber'라는 속성을 추가
   return a + b;
}
// sum()은 함수 호출이다.
// sum()에서의 this는 전역 객체다. (window)
sum(15, 16);     // => 31
window.myNumber; // => 20

sum(15, 16)이 실행되자마자, 자바스크립트는 자동으로 this를 전역 객체로 가진다. 웹 브라우저 환경에서의 this는 window다.

this가 함수 스코프 밖(최상단: 전역 실행 문맥)에서 사용되었을 경우, 여기서의 this 역시 전역 객체를 참조하게 된다.

console.log(this === window); // => true
this.myString = 'Hello World!';
console.log(window.myString); // => 'Hello World!'
<!-- In an html file -->
<script type="text/javascript">
   console.log(this === window); // => true
</script>

2.2 엄격 모드에서 함수 실행에서의 this

엄격 모드에서 함수 실행에서의 this는 undefined다.

엄격 모드는 코드 안정성과 더 나은 오류 검증을 제공하기 위해 ECMA Script 5.1 버전에서 처음 소개 되었다. 엄격 모드로 작성하기 위해 함수 내부의 최상단에 'use strict'라는 예약어를 적는다. 이 모드는 실행 문맥인 this를 undefined로 만든다. 실행 문맥은 더이상 전역 객체로 되지 않고, 위의 2.1 케이스와 반대의 상황이 된다.

엄격 모드로 실행되는 예제 코드

function multiply(a, b) {
  'use strict'; // 엄격 모드
  console.log(this === undefined); // => true
  return a * b;
}
// multiply() 함수는 엄격 모드로 실행됨
// multiply()에서의 this는 undefined
multiply(2, 5); // => 10

multiply(2, 5) 함수가 실행될 때 this는 undefined다.

엄격 모드는 현재 스코프 뿐만 아니라 내부 스코프에서도 적용된다. (내부에 정의된 모든 함수에 적용됨)

function execute() {
   'use strict'; // 엄격 모드
   function concat(str1, str2) {
     // 이곳에서도 마찬가지로 엄격 모드
     console.log(this === undefined); // => true
     return str1 + str2;
   }
   // concat() 함수는 엄격 모드
   // concat() 함수 안에서의 this는 undefined
   concat('Hello', ' World!'); // => "Hello World!"
}
execute();

'use strict'은 최상단에 위치하고, 해당 스코프에 엄격 모드를 실행해준다. concat은 실행 스코프 내에 정의되어있기 때문에 엄격 모드를 상속 받는다. 그리고 concat('Hello', ' World!') 실행은 this를 undefined로 만든다.

자바스크립트 파일에는 엄격 모드, 비 엄격 모드 두 가지 모두 포함되어있다. 그래서 같은 실행 타입에서 서로 다른 모드를 적용할 수 있다.

function nonStrictSum(a, b) {
  // 비 엄격 모드
  console.log(this === window); // => true
  return a + b;
}
function strictSum(a, b) {
  'use strict';
  // 엄격 모드
  console.log(this === undefined); // => true
  return a + b;
}
// nonStrictSum() 함수는 비 엄격 모드로 실행
// nonStrictSum()에서의 this는 window 객체
nonStrictSum(5, 6); // => 11
// strictSum() 함수는 엄격 모드로 실행
// strictSum()에서의 this는 undefined
strictSum(8, 12); // => 20

2.3. 실수: 내부 함수에서의 this를 사용할 때

함수를 실행할 때 흔히 하는 실수가 외부 함수에서의 this와 내부 함수에서의 this를 동일하게 생각하는 것이다.

사실 내부 함수의 문맥은 외부 함수의 문맥에 의존되는 게 아니라 오직 실행 환경에 좌우된다. 기대하는 되로 this가 동작되려면 수정이 필요하다. (call이라 apply 메소드를 사용하는 간접 실행, 또는 바인딩 함수를 적용)

아래의 예제는 두 수의 합계를 계산해준다.

var numbers = {
   numberA: 5,
   numberB: 10,
   sum: function() {
     console.log(this === numbers); // => true
     function calculate() {
       // this는 window, 엄격 모드였으면 undefined
       console.log(this === numbers); // => false
       return this.numberA + this.numberB;
     }
     return calculate();
   }
};
numbers.sum(); // NaN, 엄격 모드였으면 TypeError

numbers.sum()은 객체 내에 있는 메소드를 실행하는 것이다. 그래서 sum 메소드 내의 문맥은 numbers 객체다. calculate 함수는 sum 내부에 정의되어있다. 그래서 아마도 calculate() 역시 this를 numbers 객체로 바라보고 있을 거라고 예상한다. 하지만 calculate()은 메소드 실행이 아닌 함수 실행이다. 그리고 이 함수에서의 this는 전역 객체인 window다. (만약, 엄격 모드였다면 undefined) 비록 외부 함수의 문맥이 numbers 객체지만, calculate 함수에는 영향을 미치지 않는다.

numbers.sum()의 실행 결과는 NaN, 혹은 엄격 모드에서 numberA 속성이 undefined이므로 접근할 수 없어서 TypeError다. calculate 함수는 제대로 실행되지 않았기 때문에 실행 결과는 기대한대로 5 + 10 = 15가 되지 않는다.

이 문제를 해결하기 위해, calculate 함수 역시 sum method와 동일한 문맥으로 되어야 한다. 그래야 numberA와 numberB 속성에 접근할 수 있기 때문이다. 해결책 중 하나로 .call 메소드를 사용하는 것이다.

var numbers = {
   numberA: 5,
   numberB: 10,
   sum: function() {
     console.log(this === numbers); // => true
     function calculate() {
       console.log(this === numbers); // => true
       return this.numberA + this.numberB;
     }
     // 문맥을 수정하기 위해 .call() 메소드를 적용
     return calculate.call(this);
   }
};
numbers.sum(); // => 15

calculate.call(this)는 이전과 동일하게 계산된다. 하지만 추가로, 첫 번째 파라미터로 들어온 인자로 실행 문맥을 수정해준다. 이제 this.numberA + this.numberB의 결과는 numbers.numberA + numbers.numberB와 동일하게 된다. 그래서 결과값도 예상했던대로 5 + 10 = 15가 된다.

3. 메소드 실행

메소드는 객체의 속성으로 있는 함수다. 예를 들어

var myObject = {
  // helloFunction is a method
  helloFunction: function() {
    return 'Hello World!';
  }
};
var message = myObject.helloFunction();

helloFunction is a method in myObject. To get the method, use a property accessor: myObject.helloFunction. 여기서 helloFunction는 myOjbect의 메소드다. 메소드에 접근하기 위해서는 myObject.helloFunction와 같은 속성 접근자를 이용하면 된다.

메소드 실행은 속성 접근자 형태의 표현식이 함수 객체로 계산되면서 실행된다. 이 표현식은 함수와 마찬가지로 열림 괄호, 함수에 전달할 인자, 닫힘 괄호의 구조다. 이전 예제를 활용하면 myObject.helloFunction()은 myObject라는 객체의 helloFunction라는 메소드 실행이다. [1, 2].join(',') 혹은 /\s/.test('beautiful world') 역시 메소드 실행의 종류다.

앞서 설명한 함수 실행과 메소드 실행이 다르다는 점은 중요하다. 왜냐하면 둘은 서로 다른 타입이기 때문이다. 둘의 가장 큰 차이점은 메소드 실행은 속성 접근자를 통해 function (.functionProperty() or 'functionProperty')를 호출한다. 반면에 함수 실행은 속성 접근자를 사용하지 않고, (())와 같이 바로 호출한다

['Hello', 'World'].join(', '); // 메소드 실행
({ ten: function() { return 10; } }).ten(); // 메소드 실행
var obj = {};
obj.myFunction = function() {
  return new Date().toString();
};
obj.myFunction(); // 메소드 실행

var otherFunction = obj.myFunction;
otherFunction();     // 함수 실행
parseFloat('16.60'); // 함수 실행
isNaN(0);            // 함수 실행

3.1 메소드 실행에서의 this

this는 메소드 실행에서 메소드를 소유하고 있는 객체다

객체 내에 있는 메소드를 실행할 때, 여기서의 this는 객체 자신이다.

숫자를 증가하는 메소드를 가진 객체를 하나 만들어보자.

var calc = {
  num: 0,
  increment: function() {
    console.log(this === calc); // => true
    this.num += 1;
    return this.num;
  }
};
// 메소드 실행. 여기서의 this는 calc.
calc.increment(); // => 1
calc.increment(); // => 2

calc.increment() 호출은 increment 함수의 문맥을 calc 객체로 만들어준다. 그래서 this.num 참조로 number 속성을 증가시하는 게 가능하도록 해준다. 자바스크립트 객체는 프로토타입에 있는 메소드를 상속 받는다. 상속 받은 메소드를 객체 내에서 실행한다면 메소드에서의 문맥은 객체 자신을 가리키게 된다.

var myDog = Object.create({
  sayName: function() {
     console.log(this === myDog); // => true
     return this.name;
  }
});
myDog.name = 'Milo';
// 메소드 실행. 여기서의 this는 myDog.
myDog.sayName(); // => 'Milo'

Object.create()는 myDog라는 새로운 객체를 만들고, 프로토타입을 설정한다. myDog 객체는 sayName이라는 메소드를 상속받는다. myDog.sayName()이 실행될 때, myDog가 실행 문맥이다.

ECMAScript 6의 class 예약어에서 메소드 실행 문맥은 위와 마찬가지로 인스턴스 자신을 가리킨다.

class Planet {
  constructor(name) {
    this.name = name;
  }
  getName() {
    console.log(this === earth); // => true
    return this.name;
  }
}
var earth = new Planet('Earth');
// 메소드 실행. 여기서의 this는 earth.
earth.getName(); // => 'Earth'

3.2 실수: 객체로부터 메소드를 분리할 때

객체 내에 있는 메소드는 별도의 변수로 분리할 수 있다. 이 변수를 통해 메소드를 호출할 때, 당신은 아마도 여기서의 this가 메소드가 정의되어있는 객체라고 생각할 것이다.

사실 객체 밖에 있는 메소드를 호출할 경우, 함수 실행을 한 결과와 같다. 함수 실행을 할 경우 this는 전역 객체인 window를 가리킨다. (엄격 모드에서는 undefined) .bind() 바인딩 함수를 사용해서 문맥을 수정할 경우, 메소드를 객체에 포함시킬 수 있다.

아래는 Animal 생성자로 myCat이라는 인스턴스를 생성하는 예제다. 그리고 setTimeout() 함수로 1초 뒤 myCat 객체의 정보를 출력한다.

function Animal(type, legs) {
  this.type = type;
  this.legs = legs;
  this.logInfo = function() {
    console.log(this === myCat); // => false
    console.log('The ' + this.type + ' has ' + this.legs + ' legs');
  }
}
var myCat = new Animal('Cat', 4);
// "The undefined has undefined legs" 출력
// 혹은 엄격모드라면 TypeError 출력
setTimeout(myCat.logInfo, 1000);

아마도 setTimeout으로 myCat.logInfo()를 호출할 때, myCat 객체가 출력될 거라고 예상할 것이다. 하지만 setTimeout의 매개변수로 전달되었기 때문에 메소드는 객체로부터 분리 되어있고, 1초 뒤 함수 실행이 된다. logInfo가 함수로써 실행되기 때문에 여기서의 this는 전역 객체이거나 엄격 모드에서라면 undefined다. 그렇기 때문에 객체의 정보를 기대한 것대로 출력하지 못한다.

함수는 .bind 메소드를 사용해 문맥을 강제로 지정시킬 수 있다. 만약 분리된 메소드가 myCat 객체로 바인딩 된다면 이 문제는 해결된다.

function Animal(type, legs) {
  this.type = type;
  this.legs = legs;
  this.logInfo = function() {
    console.log(this === myCat); // => true
    console.log('The ' + this.type + ' has ' + this.legs + ' legs');
  };
}
var myCat = new Animal('Cat', 4);
// "The Cat has 4 legs" 출력
setTimeout(myCat.logInfo.bind(myCat), 1000);

myCat.logInfo.bind(myCat)는 객체의 메소드가 logInfo라는 새로운 함수로 실행된다. 하지만 바인딩 메소드 덕분에 함수 실행임에도 불구하고, 여기서의 this는 myCat을 가리키게 된다.

4. 생성자 실행

생성자 실행은 표현식 앞에 new라는 키워드가 붙었을 때, 함수 객체로 계산되어 수행된다. 이 표현식은 함수와 마찬가지로 열림 괄호, 함수에 전달할 인자, 닫힘 괄호의 구조다. 예를 들어 new RegExp('\d')와 같다.

아래의 예제는 Country라는 함수가 생성자로 실행되는 내용이다.

function Country(name, traveled) {
   this.name = name ? name : 'United Kingdom';
   this.traveled = Boolean(traveled); // boolean으로 타입 변환
}
Country.prototype.travel = function() {
  this.traveled = true;
};
// Constructor invocation
var france = new Country('France', false);
// Constructor invocation
var unitedKingdom = new Country;

france.travel(); // Travel to France

new Country('France', false)은 Country 함수의 생성자 실행이다. 이것의 실행 결과는 France라는 이름을 가진 새로운 객체다. 만약 생성자에 아무런 매개 변수 없이 실행 된다면, new Country처럼 괄호가 생략되어도 된다.

ECMAScript 6에서는 생성자를 class라는 키워드로 정의할 수 있게 해준다.

class City {
  constructor(name, traveled) {
    this.name = name;
    this.traveled = false;
  }
  travel() {
    this.traveled = true;
  }
}
// 생성자 실행
var paris = new City('Paris', false);
paris.travel();

new City('Paris')은 생성자 실행이다. 이 객체에서 초기 값은 constructor라는 특수 메소드로 설정할 수 있다. 이 메소드 내에서의 this는 새로 만들어지는 객체를 바라보게 된다.

생성자 호출은 생성자의 프로토타입으로부터 속성을 상속받는 새로운 빈 객체를 만든다. 생성자 함수의 역할은 객체를 초기화하는 것이다. 이미 알고 있을지도 모르지만, 이 타입에서 this는 인스턴스를 가리킨다.

myObject.myFunction과 같은 속성 접근자가 new 키워드 뒤에 오게되면, 자바스크립트는 메소드 실행이 아닌 생성자 실행으로 계산한다. 예를 들어 new myObject.myFunction()의 경우, 첫 번째로 extractedFunction = myObject.myFunction과 같이 함수가 추출되고, 그 다음으로 new extractedFunction()와 같이 생성자 실행으로 새로운 객체가 만들어진다.

4.1 생성자 실행에서의 this

생성자 실행에서의 this는 새롭게 만들어진 객체이다.

생성자 실행에서의 문맥은 새롭게 만들어진 객체다. 생성자 실행은 객체에 초기값을 셋팅하기 위해 사용된다. 초기값 셋팅의 예로는 생성자 함수의 매개 변수로 받은 데이터, 속성을 위한 환경 변수, 이벤드 핸들러 등이 있다.

아래 예제를 통해 문맥을 체크해보자.

function Foo () {
  console.log(this instanceof Foo); // => true
  this.property = 'Default Value';
}
// 생성자 실행
var fooInstance = new Foo();
fooInstance.property; // => 'Default Value'

new Foo()은 생성자 실행이다. 여기서의 문맥은 Foo의 인스턴스가 된다. Foo의 내부에서는 초기값이 셋팅되었다. this.property는 default value라는 값을 가진다.

ES6에서 사용 가능한 class 문법 역시 같은 형식이다. 초기값 셋팅은 오직 생성자 메소드에서 할 수 있다.

class Bar {
  constructor() {
    console.log(this instanceof Bar); // => true
    this.property = 'Default Value';
  }
}
// Constructor invocation
var barInstance = new Bar();
barInstance.property; // => 'Default Value'

new Bar()가 실행되면서, 자바스크립트는 생성자 메소드를 통해 문맥을 설정한 빈 객체를 만든다. this.property = 'Default Value'와 같이 this 키워드를 통해 객체에 속성 값을 추가할 수 있다.

4.2 실수: new 깜빡할 때

몇몇 자바스크립트 함수는 생성자 실행 형태로 실행됐을 때 뿐만 아니라 함수 실행으로도 인스턴스를 생성한다. 예를 들어 RegExp가 있다.

var reg1 = new RegExp('\\w+');
var reg2 = RegExp('\\w+');

reg1 instanceof RegExp;      // => true
reg2 instanceof RegExp;      // => true
reg1.source === reg2.source; // => true

new RegExp('\w+')와 RegExp('\w+')가 실행될 때, 자바스크립트는 동일한 정규식 객체를 생성한다.

객체 생성을 위해 함수 실행을 사용하는 것(팩토리 패턴을 제외)은 잠재적 문제를 만들게 된다. 왜냐하면 new 키워드가 생략되었을 때 생성자 함수는 객체를 초기화하는 로직을 생략할지도 모른다.

아래는 해당 문제점이 나타나는 예제다.

function Vehicle(type, wheelsCount) {
  this.type = type;
  this.wheelsCount = wheelsCount;
  return this;
}
// Function invocation
var car = Vehicle('Car', 4);
car.type;       // => 'Car'
car.wheelsCount // => 4
car === window  // => true

Vehicle은 타입과 바퀴개수 속성을 가지는 객체를 만들어주는 함수다.

Vehicle('Car', 4)를 실행하게 되면 객체가 반환된다. 이 객체는 올바른 속성을 가지고 있다. car.type으로 'Car'를, car.wheelCount로 4를 나타낸다. 아마도 초기값을 가진 새로운 객체가 잘 생성되었으리라 예상할 것이다.

하지만 여기서의 this는 함수 실행이 되므로 window 객체를 가리키게 된다. 그리고 Vehicle('Car', 4)은 속성을 window 객체에 추가한다. 잘못된 사용이다. 새로운 객체가 만들어지지 않았다.

생성자를 호출할 때에는 꼭 new 연산자를 사용해야 한다.

function Vehicle(type, wheelsCount) {
  if (!(this instanceof Vehicle)) {
    throw Error('Error: Incorrect invocation');
  }
  this.type = type;
  this.wheelsCount = wheelsCount;
  return this;
}
// 생성자 실행
var car = new Vehicle('Car', 4);
car.type               // => 'Car'
car.wheelsCount        // => 4
car instanceof Vehicle // => true

// 함수 실행. 잘못된 방식.
var brokenCar = Vehicle('Broken Car', 3);

new Vehicle('Car', 4)은 정상 동작한다. 초기 값을 가진 새로운 객체가 생성되었다. 왜냐하면 생성자 실행 앞에 new 키워드를 썼기 때문이다.

검증하는 방법은 생성자 함수에 추가되어 있다. this instanceof Vehicle로 실행 문맥으로 올바른 객체 타입이 맞는지 체크한다. 만약 여기서의 this가 Vehicle이 아니라면 에러가 발생한다. 이와 같은 경우 만약 Vehicle('Broken Car', 3)가 new 키워드 없이 실행된다면, 올바른 실행이 아니라는 에러 메세지를 반환하게 된다.

+ Recent posts