자네가 자바스크립트를 안다고 생각하나?
JS에는 사람들이 종종 그것을 경시하고 때로는 넘어질 수도 있다는 개념이 있다.프로토타입, 클로즈업, 이벤트 순환 등의 개념은 여전히 대부분의 JS 개발자들이 우회하는 모호한 영역 중 하나이다.우리가 알고 있는 바와 같이, 지식이 적은 것은 위험한 일이다. 그것은 실수를 초래할 수도 있다.
우리 작은 게임을 하자. 나는 너에게 몇 가지 문제를 물어볼 테니, 너는 반드시 모든 문제에 대답해 보아야 한다.설령 네가 답을 모른다 하더라도, 혹은 네가 답을 모른다 하더라도, 추측을 해야 한다.당신의 답안을 기록한 후에 아래의 상응하는 답안을 검사하세요.정답마다 자신에게 1점을 주다.시작했어.
Disclaimer: I’ve used ‘var’ purposely in some of the following code snippets so that you guys can copy paste them into your browser’s console without running into SyntaxError problem.
But I don’t condone the usage of ‘var’ declared variables. ‘let’ and ‘const’ declared variables make your code robust and less error-prone.
질문 1: 브라우저 콘솔에서 인쇄할 내용은 무엇입니까?
var a = 10;
function foo() {
console.log(a); // ??
var a = 20;
}
foo();
문제2: 만약 우리가 var 대신 let이나 const를 사용한다면 출력은 같습니까?var a = 10;
function foo() {
console.log(a); // ??
let a = 20;
}
foo();
질문 3: "새 그룹"에는 어떤 요소가 포함됩니까?var array = [];
for(var i = 0; i <3; i++) {
array.push(() => i);
}
var newArray = array.map(el => el());
console.log(newArray); // ??
문제4: 만약 우리가 브라우저 컨트롤러에서'foo'함수를 실행한다면, 이것은 창고에 오류가 발생할 수 있습니까?function foo() {
setTimeout(foo, 0); // will there by any stack overflow error?
};
질문 5: 콘솔에서 다음 기능을 실행하면 페이지(탭)의 UI가 계속 응답합니까?function foo() {
return Promise.resolve().then(foo);
};
문제6: 우리는 유형 오류를 일으키지 않는 상황에서 아래의 문장에 확장 문법을 사용할 수 있습니까?var obj = { x: 1, y: 2, z: 3 };
[...obj]; // TypeError
문제7: 아래 코드 세그먼트를 실행할 때 컨트롤러에 무엇을 출력합니까?var obj = { a: 1, b: 2 };
Object.setPrototypeOf(obj, {c: 3});
Object.defineProperty(obj, 'd', { value: 4, enumerable: false });
// what properties will be printed when we run the for-in loop?
for(let prop in obj) {
console.log(prop);
}
질문 8: xGetter()는 어떤 값을 인쇄합니까?
var x = 10;
var foo = {
x: 90,
getX: function() {
return this.x;
}
};
foo.getX(); // prints 90
var xGetter = foo.getX;
xGetter(); // prints ??
답안
이제 모든 질문에 위에서 아래로 대답해 봅시다.나는 너에게 간단한 설명을 하면서 이 행위들을 신비화하고 참고 자료를 만들어 보겠다.
정답 1: 정의되지 않았습니다.
설명: var 키워드로 선언된 변수는 JavaScript에서 hoisted이고 메모리에 정의되지 않은 값이 할당됩니다.그러나 초기화는 코드에 입력한 곳에서 발생합니다.그 밖에 var성명의 변수는 function-scoped이고let과const는 블록 작용역을 가진다.그래서 이 과정은 다음과 같다.
var a = 10; // global scope
function foo() {
// Declaration of var a will be hoisted to the top of function.
// Something like: var a;
console.log(a); // prints undefined
// actual initialisation of value 20 only happens here
var a = 20; // local scope
}
대답 2: Reference Error:a가 정의되지 않았습니다.설명:let과 const는 변수를 설명할 수 있습니다. 이 변수의 작용역은 블록, 문장, 표현식만 사용할 수 있습니다.var과 달리 이 변수들은 향상되지 않았고 이른바 temporal dead zone (TDZ) 가 있었다.TDZ에서 이러한 변수에 액세스하려면 선언에 도달하기 전에만 액세스할 수 있으므로 Reference Error가 제거됩니다.lexical scoping 및 Execution Context & Stack에 대한 JavaScript를 자세히 읽습니다.
var a = 10; // global scope
function foo() { // enter new scope, TDZ starts
// Uninitialised binding for 'a' is created
console.log(a); // ReferenceError
// TDZ ends, 'a' is initialised with value of 20 here only
let a = 20;
}
다음 표에서는 JavaScript에서 사용되는 다양한 키워드와 관련된 향상된 동작과 범위(credit:'s blogpost에 대해 간략하게 설명합니다.답안3:[3,3]
설명: forloop의 머리에 var 키워드 설명 변수를 사용하면 이 변수에 귀속(저장 공간)을 만들 수 있습니다.closures에 대한 더 많은 정보를 읽으십시오.for 순환을 다시 한 번 봅시다
// Misunderstanding scope:thinking that block-level scope exist here
var array = [];
for (var i = 0; i < 3; i++) {
// Every 'i' in the bodies of the three arrow functions
// referes to the same binding, which is why they all
// return the same value of '3' at the end of the loop.
array.push(() => i);
}
var newArray = array.map(el => el());
console.log(newArray); // [3, 3, 3]
에서 블록 레벨 역할 영역을 가진 변수를 성명하면 매 순환마다 새 귀속을 생성합니다
// Using ES6 block-scoped binding
var array = [];
for (let i = 0; i < 3; i++) {
// This time, each 'i' refers to the binding of one specific iteration
// and preserves the value that was current at that time.
// Therefore, each arrow function returns a different value.
array.push(() => i);
}
var newArray = array.map(el => el());
console.log(newArray); // [0, 1, 2]
이 괴벽을 해결하는 또 다른 방법은 사용closures
// After understanding static scoping and thus closures.
// Without static scoping, there's no concept of closures.
let array = [];
for (var i = 0; i < 3; i++) {
// invoking the function to capture (closure) the variable's current value in the loop.
array[i] = (function(x) {
return function() {
return x;
};
})(i);
}
const newArray = array.map(el => el());
console.log(newArray); // [0, 1, 2]
응답 4:아니오
설명: JavaScript 동시 모델은 이벤트 루프를 기반으로 합니다.내가 "브라우저는 JS의 집"이라고 말했을 때, 나의 진정한 뜻은 브라우저가 실행할 때 환경을 제공하여 우리의 자바스크립트 코드를 실행하는 것이다.브라우저의 주요 구성 요소는 호출 스택, 이벤트 주기, 작업 대기열 및 웹 API입니다.setTimeout, setInterval과 Promise 같은 전역 함수는 자바스크립트의 일부분이 아니라 웹 API입니다.JavaScript 환경의 시각적 표현은 다음과 같습니다.
JS 호출 스택은 LIFO입니다.엔진은 매번 창고에서 함수를 가져와 위에서 아래로 코드를 실행합니다.setTimeout 같은 비동기 코드를 만날 때마다 웹 API ( 화살표 1 ) 에 전달합니다.따라서 이벤트를 트리거할 때마다 리셋이 작업 대기열로 전송됩니다( 화살표 2)
이벤트 순환은 작업 대기열을 계속 감시하고 대기열 순서대로 리셋을 처리합니다.호출된 창고가 비어 있을 때마다 순환은 리셋을 픽업하여 창고에 넣습니다. ( 화살표 3 )호출된 창고가 비어 있지 않으면 이벤트 순환이 리셋을 창고로 전송하지 않는다는 것을 기억하십시오
JavaScript에서 이벤트가 순환하는 방식을 더 자세히 이해하려면Philip Roberts의 이 글을 보시기를 강력히 권장합니다.또한 이 멋진 tool를 통해 호출 창고를 시각화하고 이해할 수 있습니다."foo"함수를 계속 실행하여 무슨 일이 일어날지 보십시오
이제 이런 지식이 생겨서 앞에서 언급한 질문에 대답해 봅시다:
층계
- 호출 foo()은 foo 함수를 호출 창고에 넣습니다.
- 내부 코드를 처리할 때 JS 엔진이 setTimeout을 만났습니다.
- 그리고 foo 리셋을 WebAPI (화살표 1) 에게 건네주고 함수에서 되돌려줍니다.호출 창고가 다시 비어 있습니다.
- 타이머가 0으로 설정되어 있기 때문에foo는 작업 대기열(화살표 2)에 전송됩니다.
- 호출 창고가 비어 있기 때문에 이벤트 순환은 foo 리셋을 선택하여 호출 창고로 전송하여 처리합니다.
- 이 과정이 다시 반복되어 창고가 영원히 넘치지 않습니다.
응답 5:아니오
설명: 대부분의 경우 개발자가 이벤트 순환도에 하나의 작업 대기열만 있다고 가정하는 것을 볼 수 있다.하지만 사실은 그렇지 않다.우리는 여러 개의 임무 대열을 가질 수 있다.브라우저에서 대기열을 선택하고 내부 콜백 처리
보다 높은 수준에서 JavaScript에는 매크로 작업과 마이크로 작업이 있습니다.setTimeout 리셋은 매크로 작업이고 Promise 리셋은 마이크로 작업입니다.주요 차이는 그들의 행형 의식에 있다.매크로 작업은 하나의 순환 주기에 한 번씩 창고에 밀어 넣지만, 이벤트 순환 (추가 줄 서기 항목 포함) 으로 되돌아오기 전에, 마이크로 작업 대기열은 항상 비워집니다.따라서 프로젝트를 처리하는 속도로 이 대기열에 항목을 추가하면 마이크로 작업을 영원히 처리할 수 있습니다.자세한 내용은 이 또는 article를 참조하십시오.
Microtask queue is always emptied before execution returns to the event loop
현재 컨트롤에서 다음 코드 세그먼트를 실행할 때:
function foo() {
return Promise.resolve().then(foo);
};
"foo"를 호출할 때마다 마이크로 작업 대기열에 "foo"리셋을 추가합니다. 따라서 이 대기열이 완전히 비워지기 전에 이벤트 순환은 다른 이벤트를 처리할 수 없습니다. (스크롤, 클릭 등)따라서 렌더링이 차단됩니다
대답6:네, 제작 대상iterables을 통해
설명: spread syntax와 for-of 문장 교체iterable 대상이 교체할 데이터를 정의합니다.배열 또는 매핑은 기본 반복 동작이 있는 내장 iTerable입니다.대상은 이식 가능하지 않지만 iterable 및 iterator 프로토콜을 사용하여 이식 가능
Mozilla 문서에서 대상이 @iterator 방법을 실현하면 이것은iterable라고 불린다. 이것은 이 대상(또는 그 위의 대상prototype chain)이 @iterator 키가 있는 속성을 가져야 한다는 것을 의미한다. 이 키는 상수 기호를 통해 얻을 수 있다.교체기
상술한 견해는 좀 지루할 수 있지만, 아래의 예는 더욱 의미가 있다.
var obj = { x: 1, y: 2, z: 3 };
obj[Symbol.iterator] = function() {
// An iterator is an object which has a next method,
// which also returns an object with atleast
// one of two properties: value & done.
// returning an iterator object
return {
next: function() {
if (this._countDown === 3) {
return { value: this._countDown, done: true };
}
this._countDown = this._countDown + 1;
return { value: this._countDown, done: false };
},
_countDown: 0
};
};
[...obj]; // will print [1, 2, 3]
generator 함수 사용자 정의 대상의 교체 행위도 사용할 수 있습니다:
var obj = { x: 1, y: 2, z: 3 };
obj[Symbol.iterator] = function*() {
yield 1;
yield 2;
yield 3;
};
[...obj]; // print [1, 2, 3]
정답 7:a, b, c
설명: for in 순환 교체 대상 자체enumerable properties와 대상이 원형에서 계승된 대상.열거 가능한 속성은 for-in 순환에 포함되고 for-in 순환 기간에 접근할 수 있는 속성입니다
var obj = { a: 1, b: 2 };
var descriptor = Object.getOwnPropertyDescriptor(obj, "a");
console.log(descriptor.enumerable); // true
console.log(descriptor);
// { value: 1, writable: true, enumerable: true, configurable: true }
현재 당신의 가방에 이러한 지식이 있습니다. 왜 우리의 코드가 이런 특정 속성을 인쇄하는지 쉽게 이해할 수 있을 것입니다.
var obj = { a: 1, b: 2 }; // a, b are both enumerables properties
// setting {c: 3} as the prototype of 'obj', and as we know
// for-in loop also iterates over the properties obj inherits
// from its prototype, 'c' will also be visited.
Object.setPrototypeOf(obj, { c: 3 });
// we are defining one more property 'd' into our 'obj', but
// we are setting the 'enumerable' to false. It means 'd' will be ignored.
Object.defineProperty(obj, "d", { value: 4, enumerable: false });
for (let prop in obj) {
console.log(prop);
}
// it will print
// a
// b
// c
정답 8:10
설명: x을 전역 범위로 초기화하면 창 대상의 속성이 됩니다strict 모드가 아닌 브라우저 환경이라고 가정).다음 코드 보기:
var x = 10; // global scope
var foo = {
x: 90,
getX: function() {
return this.x;
}
};
foo.getX(); // prints 90
let xGetter = foo.getX;
xGetter(); // prints 10
우리는 단언할 수 있다:
window.x === 10; // true
이것은 항상 이 방법을 사용하는 대상을 가리킬 것입니다.그래서 부유한 예에서.getX (), 이것은 foo 대상을 가리키며, 되돌아오는 값은 90입니다.xGetter()의 경우 창 대상을 가리키며 이 대상은 10의 값을 되돌려줍니다
foo의 값을 검색합니다.x, 우리는 Function.prototype.bind를 사용하여 그 값을 foo 대상에 귀속시켜 새로운 함수를 만들 수 있습니다
let getFooX = foo.getX.bind(foo);
getFooX(); // prints 90
이게 다야!만약 네 모든 답안이 정확하다면, 잘 할 수 있을 것이다.우리는 모두 실수를 통해 배운다.이 모든 것은 배후를 이해하는 원인에 관한 것이다.당신의 도구를 이해하고 그것들을 더 잘 이해하세요.하면, 만약, 만약...❤️ 꼭 웃게 해줄게.😀.
당신의 점수는 얼마입니까😃?
Reference
이 문제에 관하여(자네가 자바스크립트를 안다고 생각하나?), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/aman_singh/so-you-think-you-know-javascript-5c26텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)