当客户端再次请求资源时,会将上次请求时服务器返回的Last-Modified值放在请求头中的If-Modified-Since字段中,服务器会根据这个值判断资源是否发生了变化。如果资源没有发生变化,服务器会返回304 Not Modified状态码,表示资源没有发生变化,客户端可以使用缓存。
ETag
ETag是一个HTTP响应头部字段,用于指定资源的唯一标识。它的值是一个字符串。服务器会根据资源的内容生成一个唯一的字符串(注意,生成算法因服务器而异),然后将这个字符串作为ETag的值。当客户端再次请求资源时,会将上次请求时服务器返回的ETag值放在请求头中的If-None-Match字段中,服务器会根据这个值判断资源是否发生了变化。如果资源没有发生变化,服务器会返回304 Not Modified状态码,表示资源没有发生变化,客户端可以使用缓存。
console.log(name); // undefined var name = 'Philip';
以上代码等价于:
1 2 3
var name; // `var`声明的变量会被提升到其作用域顶部。 console.log(name); // undefined name = 'Philip';
如果var是在函数作用域声明的,那么它会被提升到函数作用域的顶部。
1 2 3 4 5
functionprintName() { console.log(name); // undefined var name = 'Philip'; } printName();
以上代码等价于:
1 2 3 4 5 6
functionprintName() { var name; // `var`声明的变量会被提升到其作用域顶部。 console.log(name); // undefined name = 'Philip'; } printName();
let和const声明的变量不会被提升。
对于let和const,它们不会被提升,所以下面代码会报错。
1 2
console.log(num); // ReferenceError: Cannot access 'num' before initialization const num = 1;
前面说过,关于let和const是否被提升有争议。
一种说法是let和const不会被提升,所以在声明之前访问会报错。
另一种说法是let和const会被提升,但是在声明之前访问会抛出Temporal Dead Zone错误。
比如下面的代码:
1 2 3 4 5
const x = 1; { console.log(x); // ReferenceError: Cannot access 'x' before initialization const x = 2; }
这段代码会报错,但是如果我们把{}内的const x = 2;注释掉,那么代码就不会报错。如果const x = 2没有被提升的话,那么console.log(x)应该可以访问到全局的const x = 1,而不会报错。换句话说:因为const x = 2被提升了,所以console.log(x)访问的是提升后的x,而此时x还没有被初始化,所以报错。
functionfoo() { var a; // value of a is `undefined` if (a) { a = 10; // never executed. } console.log(a); }
foo();
第三题
1 2 3 4 5 6 7 8 9 10 11 12
functionfn() { console.log(typeof foo); var foo = 'variable';
functionfoo() { return'function'; }
console.log(typeof foo); }
fn();
还是那句话,此类题目的解法就是按照提升规则把代码重新写一遍,以上代码提升后等价于如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
functionfn() { var foo;
functionfoo() { return'function'; }
console.log(typeof foo); foo = 'variable';
console.log(typeof foo); }
fn();
所以输出结果是function和string。
变量的作用域
var声明的变量有只两种作用域:全局作用域和函数作用域。(没有块级作用域)
let和const声明的变量有三种作用域:全局作用域,函数作用域和块级作用域。
var声明的全局变量会挂载到window对象上,而let和const不会。
let和const有临时性死区,而var没有。
面试题
第一题
以下代码输出什么?
1 2 3 4 5 6
let x = 1; { let x = 2; }
console.log(x);
答案:1,因为let有块级作用域,所以let x = 2只在{}内有效。
第二题
以下代码输出什么?
1 2 3 4 5 6
var x = 1; { var x = 2; }
console.log(x);
答案:2,因为var没有块级作用域,所以var x = 2会覆盖外部的var x = 1。
第三题
以下代码输出什么?
1 2 3 4 5
let name = 'zdd'; { console.log(name); let name = 'Philip'; }
答案:ReferenceError: Cannot access ‘name’ before initialization。因为let有块级作用域,所以console.log(name);访问的是let name = 'Philip';之前的name,而此时name还没有被初始化,处于暂时性死区中,所以报错。
第四题
以下代码输出什么?
1 2 3 4 5 6 7 8 9
'use strict';
{ functionfoo() { console.log('foo'); } }
foo();
答案:ReferenceError: foo is not defined。因为foo是在块级作用域内声明的,所以在外部无法访问。但是如果我们把'use strict';去掉,那么代码就可以正常运行。因为在非严格模式下,函数声明会被提升到全局作用域。
第五题
以下代码输出什么?
1 2 3 4 5 6 7 8 9 10 11 12 13
(() => { let x; let y; try { thrownewError(); } catch (x) { x = 1; y = 2; console.log(x); } console.log(x); console.log(y); })();
Posted onEdited onIninterview Symbols count in article: 596Reading time ≈2 mins.
Introduction
There is a very famous question, “What happened when you input a URL and press Enter in the browser?”
Answer
1. URL parsing
When you input a URL in the browser, the browser will parse the URL into several parts:
Protocol: http or https
Host/Domain: www.example.com
Port: 80 or 443
Path: /page.html
Query: ?name=Philip
Fragment: #section1
2. DNS lookup
The browser will check the cache to see if the DNS lookup result is cached. If not, it will send a DNS query to the DNS server to get the IP address of the host.
Check browser cache.
Check OS cache.
Check router cache.
Check ISP(Internet Service Provider) cache.
Make request to the DNS server.(Only if all cache above failed!)
3. TCP connection
The browser will establish a TCP connection with the server using the IP address and port number. This including the TCP handshake process. 关于TCP的连接与关闭,可以看这篇
4. HTTP request
The browser will send an HTTP request to the server. The request includes the following information:
The server will process the request and generate a response. The response includes the following information:
Status code: 200, 404, 500, etc.
Response headers: Content-Type, Content-Length, etc.
Response body: HTML, CSS, JavaScript, etc.
Cookies: Set-Cookie header.
6. Browser rendering
The browser will render the response HTML, CSS, and JavaScript to display the web page to the user. When browser parse html page, it may download js file embed in html, this process might block the parsing, see here for details.
Parse HTML: The browser will parse the HTML to create the DOM tree.
The file was transferred from the server to the browser as binary data.
The browser will parse the binary data to the .html file.
The browser constructs the DOM tree based on the parsed HTML.
If the html contains external CSS files, the browser will download the css in parallel, this won’t block the DOM construction.
If the html contains external JavaScript files, the browser will download the js in parallel, this will/won’t block the DOM construction. see here for details.
Parse CSS: The browser will parse the CSS to create the CSSOM tree.
Render tree: The browser will combine the DOM tree and CSSOM tree to create the render tree.
Layout: The browser will calculate the position and size of each element in the render tree.
Paint: The browser will paint the pixels on the screen.
Composite: The browser will composite the layers to display the final web page.
JavaScript execution: The browser will execute the JavaScript code to add interactivity to the web page.
Event handling: The browser will handle user events like click, scroll, etc.
Conclusion
External CSS doesn’t block the DOM construction, but might block the render tree construction since the browser needs to wait for the CSSOM tree to be constructed.
External JavaScript might block the DOM construction if and only if:
The Scripts is in <head> tag, it will block the DOM construction.
And the script doesn’t set async or defer.
If the script is at the end of the <body> tag, it won’t block the DOM construction even when it doesn’t set async or defer.
Posted onEdited onInjavascript Symbols count in article: 353Reading time ≈1 mins.
Introduction
reduce() is a very import method in JavaScript, it executes a reducer function (that you provide) on each element of the array, resulting in a single output value.
Most of the time, reduce() is used to sum up the values in an array, but it can be used for many other things as well.
1 2 3
const nums = [1, 2, 3]; const sum = nums.reduce((a, c) => a + c); console.log(sum);
Why we use a and c for the parameters of the reducer function, because a = accumulator, c = currentValue.
Each call to the reducer produces a new value, and this value is passed to the next call of the reducer as the accumulator(first argument). Otherwise, a will become undefined in the next call.
Note that you must return value from the callbackFn, otherwise the result will be undefined.
1 2 3
const nums = [1, 2, 3]; const sum = nums.reduce((a, c) => {a + c}); // won't work since we didn't return the value console.log(sum); // undefined.
I found this when I working on the following work, group the following inventory by type property:
const person = Object.create(null); person.name = 'Philip'; person.age = 18;
console.log(person.toString()); // TypeError: person.toString is not a function console.log(person.hasOwnProperty('name')); // TypeError: person.hasOwnProperty is not a function console.log('name'in person); // true
Posted onEdited onInjavascript Symbols count in article: 338Reading time ≈1 mins.
TypeError: Cannot read properties of undefined (reading ‘xxx’)
This error occurs when you try to access a property of an object that is undefined.
1 2
let person = undefined; console.log(person.age); // Uncaught TypeError: Cannot read properties of undefined (reading 'age')
ReferenceError: a is not defined
This is the most common error in JavaScript. It means that you are trying to use a variable that has not been declared.
1
console.log(a); // Uncaught ReferenceError: a is not defined
ReferenceError: Cannot access ‘a’ before initialization
This error occurs when you try to access a variable before it is declared. After ES6, this means you touch the (TDZ)temporal dead zone. temporal dead zone is the zone between the start of the block scope and the actual declaration of the variable.
1 2
console.log(a); // Uncaught ReferenceError: Cannot access 'a' before initialization const a = 1;
TypeError: Assignment to constant variable.
This error occurs when you try to reassign a value to a constant variable.
1 2
const a = 1; a = 2; // Uncaught TypeError: Assignment to constant variable.
RangeError: Maximum call stack size exceeded
See the following code, why it cause the RangeError: Maximum call stack size exceeded error?
Posted onEdited onInjavascript Symbols count in article: 2kReading time ≈7 mins.
Introduction
What is a closure in JavaScript? This is really a hard question, and I see lots of different answers on the internet. I list some of them below:
A closure is a function that has access to the outer function’s variables.
A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives a function access to its outer scope. In JavaScript, closures are created every time a function is created, at function creation time. - from MDN
In the follow example, the inner function increase has access to the outer function’s variable count. The variable count is private and cannot be accessed from outside the function createCounter. But you can still change count by invoking the increase function.
We can also return the function increase directly.(This is not work when you have multiple functions to return, you must use the object literal in that case.)
1 2 3 4 5 6 7 8
functioncreateCounter() { let count = 0;
returnfunction () { count++; return count; } }
And you should also change the way to invoke the function since you return an anonymous function. so there is no increase property in the counter object.
In the following example, we can simulate a module by using closure. The private variables and methods are defined inside the outer function MyModule. The public variables and methods are returned as an object literal. So the private variables and methods are encapsulated and cannot be accessed from outside the function. The public variables and methods can be accessed by invoking the returned object.
// If you want to expose the variables/methods, return them. return { publicVariable, publicMethod, }; }
const myModule = MyModule(); console.log(myModule.publicVariable); // public variable myModule.publicMethod(); // public method
Event handler(or callback)
Some event handler or callback functions use closure to access the outer function’s variables. In the following example, the event handler for click event is an anymous function. The function has access to the outer function’s variable count.
In the following code, we use a local variable instance to check whether the instance has been created or not. The instance variable must memorize its state during different calls to the getInstance function. So we use closure to achieve this.
document.querySelector("#input").addEventListener( "input", debounce(function (e) { document.querySelector("#output").innerHTML = "You have input: " + e.target.value; // You can call and server side api here, and debounce make sure the api is not called very often. }, 500) );