/* .active 클래스는 포함하지 않는 a태그 hover시 효과 */ lia:hover:not(.active) { background-color: #555; color: white; }
2. 고정된 세로 Navigation Bar
sticky side 네비게이션 바를 만드는 방법이다.
1 2 3 4 5 6 7 8 9 10
ul { list-style-type: none; margin: 0; padding: 0; width: 25%; background-color: #f1f1f1; height: 100%; /* Full height */ position: fixed; /* Make it stick, even on scroll */ overflow: auto; /* Enable scrolling if the sidenav has too much content */ }
수평 Navigation Bar
1. li 요소를 display:inline 속성을 준다.
1 2 3
li { display: inline; }
2. floating li
1 2 3 4 5 6 7 8 9
li { float: left; }
a { display: block; padding: 8px; background-color: #dddddd; }
메서드로 프로퍼티를 참조하고 변경하기 위해서는 우선 자신이 속한 객체를 가리키는 식별자를 참조해야만 가능한 일이다.
1 2 3 4 5 6 7 8 9 10 11 12
const circle = { // 프로퍼티: 객체 고유의 상태 데이터 radius: 5, // 메서드: 상태 데이터를 참조하고 조작하는 동작 getDiameter() { // 이 메서드가 자신이 속한 객체의 프로퍼티나 다른 메서드를 참조하려면 // 자신이 속한 객체인 circle을 참조할 수 있어야 한다. return2 * circle.radius; }, };
console.log(circle.getDiameter()); // 10
객체 리터럴은 circle 변수에 할당되기 직전에 평가된다? = 할당 연산자에 의해서 피연산자를 할당해주기 위해서는 우측의 객체 리터럴이 평가된 값으로 존재해야 할당을 해줄 수 있기 때문이다.
하지만 위처럼 재귀적으로 자신이 속한 객체를 참조하는 것은 바람직하지 않다.
그 예시를 생성자 함수를 통해 설명해보자
1 2 3 4 5 6 7 8 9 10 11 12 13
// 생성자 함수 functionCircle(radius) { // 이 시점에는 생성자 함수 자신이 생성할 인스턴스를 가리키는 식별자를 알 수 없다. ????.radius = radius; }
Circle.prototype.getDiameter = function () { // 이 시점에는 생성자 함수 자신이 생성할 인스턴스를 가리키는 식별자를 알 수 없다. return2 * ????.radius; };
// 생성자 함수로 인스턴스를 생성하려면 먼저 생성자 함수를 정의해야 한다. const circle = newCircle(5);
생성자 함수 내부에서 프로퍼티나 메서드를 추가하기 위해서는 자신이 생성할 인스턴스를 참조할 수 있어야 하는데, 인스턴스를 생성하려면 생성자 함수가 존재해야한다.
따라서 자신이 속한 객체, 자신이 생성할 인스턴스를 가리킬 특별한 식별자가 필요하다.
this란, 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수이다. this를 통해 자신이 속한 객체나 자신이 생성할 인스턴스의 프로퍼티나 메서드를 참조할 수 있다.
this는 코드 어디서든 참조할 수 있다. (전역에서도 가능)
this는 객체의 프로퍼티나 메소드를 참조하기 위한 자기 참조 변수이므로 객체의 메서드 내부 또는 생성자 함수 내부에서만 의미가 있다. 따라서 strict mode가 선언된 일반 함수 내부의 this는 undefined가 바인딩된다. (일반함수에선 필요 없다)
하지만 메서드 내의 중첩함수와 콜백함수는 외부함수를 돕는 헬퍼 함수의 역할을 하는데 외부함수인 메서드와 중첩함수 또는 콜백함수의 this가 일치하지 않는다는 것은 중첩함수 또는 콜백함수가 헬퍼 함수로 동작하는 것을 어렵게 만든다.
화살표함수 내부에서 this
1 2 3 4 5 6 7 8 9 10 11
var value = 1;
const obj = { value: 100, foo() { // 화살표 함수 내부의 this는 상위 스코프의 this를 가리킨다. setTimeout(() =>console.log(this.value), 100); // 100 }, };
obj.foo();
2. 메서드 호출
메서드 내부의 this는 메서드를 소유한 객체가 아닌 메서드를 호출한 객체에 바인딩된다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
const person = { name: "Lee", getName() { // 메서드 내부의 this는 메서드를 호출한 객체에 바인딩된다. returnthis.name; }, }; const people = { name: "Kim", getName() { returnthis.name; }, };
console.log(person.getName()); // Lee console.log(people.getName()); // Kim
person 객체의 getName 프로퍼티가 가리키는 함수 객체는 person 객체에 포함된 것이 아니라 독립적으로 존재하는 별도의 객체이다? 내 생각에는 this가 가리키는 것이 메서드를 소유한 객체라고 생각해도 맞지 않나? person 객체에 getName 프로퍼티 키가 가리키는 함수 객체를 소유하고 있는 것이 아니라 참조값을 가지므로 독립적으로 존재하는 객체를 가리키고 있는 것이 맞다.
arguments 객체는 배열이 아니므로 배열 메서드를 사용할 수 없지만 apply, call 메서드를 사용하면 가능하다.
새로 나온 Array.from() 정적 메서드를 사용할 수 있다. 하지만 arguments 객체를 잘 안쓴다.
bind
1 2 3 4 5 6 7 8 9 10 11 12
functiongetThisBinding() { returnthis; }
// this로 사용할 객체 const thisArg = { a: 1 };
// bind 메서드는 첫 번째 인수로 전달한 thisArg로 this 바인딩이 교체된 // getThisBinding 함수를 새롭게 생성해 반환한다. console.log(getThisBinding.bind(thisArg)); // getThisBinding // bind 메서드는 함수를 호출하지는 않으므로 명시적으로 호출해야 한다. console.log(getThisBinding.bind(thisArg)()); // {a: 1}
bind 메서드는 함수를 호출하지 않고 인수로 전달받은 객체로 this 바인딩이 교체된 함수를 새롭게 생성하여 반환한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
const person = { name: "Lee", foo(callback) { // ① setTimeout(callback, 100); }, };
person.foo(function () { console.log(`Hi! my name is ${this.name}.`); // ② Hi! my name is . // 일반 함수로 호출된 콜백 함수 내부의 this.name은 브라우저 환경에서 window.name과 같다. // 브라우저 환경에서 window.name은 브라우저 창의 이름을 나타내는 빌트인 프로퍼티이며 기본값은 ''이다. // Node.js 환경에서 this.name은 undefined다. });
person.foo의 콜백함수가 호출되기 전 1의 시점에서 this는 foo 메서드를 호출한 객체(person)를 가리킨다.
그러나 person.foo의 콜백함수가 일반 함수로서 호출된 2의 시점에서 this는 전역객체 windows를 가리킨다.
person.foo의 콜백함수는 헬퍼함수로 person.foo를 돕는 역할을 하기 때문에 서로의 this가 같아야한다.
이 때, bind 메서드를 사용하여 this를 일치시킨다.
1 2 3 4 5 6 7 8 9 10 11
const person = { name: "Lee", foo(callback) { // bind 메서드로 callback 함수 내부의 this 바인딩을 전달 setTimeout(callback.bind(this), 100); }, };
person.foo(function () { console.log(`Hi! my name is ${this.name}.`); // Hi! my name is Lee. });
callback 함수에 this가 바인딩된 새로운 함수를 반환
코드해설
즉, foo안의 this는 person 객체를가리키는데, 콜백함수 호출하면 this가 window를 가리킨다. 그러므로 bind함수를 사용하여 foo 메서드가 가리키는 this를 callback 함수에 바인딩해줘서 콜백함수가 가리키는 this와 일치 시켜준다.
bind, call, apply, that으로 this 바인딩을 일치 시켜주는 것 보다 화살표 함수를 사용하는 것이 간편하다. 하지만 여러 가지 방식에 대해서도 알아두자.