最近我看了 You-Dont-Know-JS 的兩個小冊,在看書的過程當中,爲了方便之後索引與更深刻的瞭解,也爲了不遺忘,我對每一冊的較爲複雜的點作了總結,編輯以下前端
本文地址: https://shanyue.tech/post/js-puzzles/git
types & grammer
-
判斷如下結果github
var s = 'abc';
s[1] = 'B';
console.log(s);
var l = new String('abc');
l[1] = 'B';
console.log(l);string
及其包裝對象 (Boxed Object) 是不可變 (immutable) 類型,所以不能改變它自己(modify in place),因此String
的全部方法都是返回一個新的字符串,而不會改變自身。web -
You-Dont-Know-JS [1] -
如何逆序一個字符串?面試
s.split('').reverse().join('')
前端工程化 -
接上,爲何不能直接使用
Array.prototype.reverse.call(s)
逆序字符串?數組當一個數組逆序時
l.reverse()
會改變 l 自己。正如第一題,string
不能改變自身。瀏覽器 -
判斷如下結果,爲何會出現這樣的狀況,如何作出正確的比較?服務器
0.1 + 0.2 === 0.3;
0.8 - 0.6 === 0.2;浮點數根據 IEEE 754 標準存儲 64 bit 雙精度,可以表示 2^64 個數,而浮點數是無窮的,表明有些浮點數必會有精度的損失,0.1,0.2 表示爲二進制會有精度的損失。比較時引入一個很小的數值
Number.EPSILON
容忍偏差,其值爲2^-52
。微信function equal (a, b) {
return Math.abs(a - b) < Number.EPSILON
} -
You-Dont-Know-JS [2] -
知乎 [3] -
如何判斷一個數值爲整數?
// ES6
Number.isInteger(num);
// ES5
if (!Number.isInteger) {
Number.isInteger = function(num) {
return typeof num == "number" && num % 1 == 0;
};
} -
You-Dont-Know-JS#user-content-testing-for-integers [4]
-
如何判斷一個數值爲 +0?
function isPosZero (n) {
return n === 0 && 1 / n === Infinity
} -
'abc'.toUpperCase()
中 'abc' 做爲 primitive value,如何訪問toUpperCase
方法當
primitive value
訪問屬性或者方法時,會自動轉化爲它的包裝對象。另外也可使用Object.prototype.valueOf()
解包裝(Unboxing)。 -
You-Dont-Know-JS#user-content-boxing-wrappers [5] -
判斷如下結果 (Boxing Wrappers)
function foo() {
console.log(this)
}
foo.call(3);Number(3)。理由如上。
-
判斷如下結果
Array.isArray(Array.prototype)
true 內置對象的 prototype 都不是純對象,好比
Date.prototype
是 Date,Set.prototype
是 Set。 -
You-Dont-Know-JS#user-content-native-prototypes [6] -
判斷如下結果
Boolean(new Boolean(false));
Boolean(document.all);
[] == '';
[3] == 3;
[] == false;
42 == true;new Boolean() 返回 object,爲 true document.all,歷史問題,參考這裏[7]Falsy value 指會被強制轉化爲 false 的值,有如下五種。除此以外所有會轉化爲 true
You-Dont-Know-JS#user-content-toboolean[8]
-
undefined -
null -
false -
+0, -0, and NaN -
""
-
找出如下代碼問題 (TDZ)
var a = 3;
let a;這是暫時性死域(Temporal Dead Zone)的問題,let a 聲明以前,不能使用 a。
-
找出如下代碼問題 (TDZ)
var x = 3;
function foo (x=x) {
// ..
}
foo()一樣,在函數默認參數中,也有 TDZ。
scope & closures
-
var a = 2
中,Engine
,Scope
,Compiler
作了什麼工做 -
判斷如下結果 (Lexical Scope)
var scope = 'global scope';
function checkScope () {
var scope = 'local scope';
function f() {
return scope;
}
return f;
}
checkScope()();'local scope'
因爲 js 爲詞法做用域(Lexical Scope),訪問某個變量時,先在當前做用域中查找,若是查找不到則在嵌套做用域中查找,直到找到。若是找不到,則報
ReferenceError
。 -
判斷如下結果 (Hoisting)
console.log(a);
var a = 3;undefined
以上代碼會被編譯器理解爲
var a;
console.log(a);
a = 3; -
判斷如下結果 (Function First)
var foo = 1;
function foo () {
}
console.log(foo);1。函數也會有提高,因此會被賦值覆蓋。
-
判斷如下結果 (IIFE & Function First)
var foo = 1;
(function () {
foo = 2;
function foo () {}
console.log(foo);
})()
console.log(foo);2,1
以上代碼會被編譯器理解爲以下形式
var foo = 1;
(function () {
var foo;
function foo () {
}
foo = 2;
console.log(foo);
})()
console.log(foo); -
判斷如下結果,如何按序輸出 (Closure)
for (var i = 0; i < 10; i++) {
setTimeout(function () {
console.log(i);
}, 1000)
}大約 1s 以後連續輸出 10 個 10。由於沒有塊級做用域,能夠把 var 改爲 let,也能夠給 setTimeout 包裝一層 IIFE。
this & object prototypes
注意:如下均爲瀏覽器環境中
-
判斷如下結果 (Default Binding)
function foo() {
"use strict";
console.log( this.a );
}
var a = 2;
foo();會報錯,在函數的嚴格模式下,默認綁定其中的 this 指向 undefined。
-
判斷如下結果
"use strict";
var a = 2;
let b = 3;
console.log(this.a, this.b);2, undefined
在瀏覽器環境中 this 指向 window,而 var 聲明的變量會被掛在 window 上。而 let 聲明的變量不會掛在 window 上。
-
判斷如下結果 (Strict Mode & Default Binding)
function foo() {
console.log( this.a );
}
var a = 2;
(function(){
"use strict";
foo();
})();2
只有存在 this 的函數中設置嚴格模式,this 爲 undefined。所以會正常輸出。
-
判斷如下結果 (Hard Binding)
function foo () {
console.log(this.a);
}
const o1 = { a: 3 };
const o2 = { a: 4 };
foo.bind(o1).bind(o2)();3
bind 爲硬綁定,第一次綁定後 this 沒法再次綁定。
-
如何實現
Function.prototype.bind
與Function.prototype.softBind
bind 爲硬綁定,softBind 能夠屢次綁定 this。大體實現代碼以下。bind 第二個參數能夠預設函數參數,因此,bind 也是一個偏函數。另外,bind 也須要考慮
new
的狀況。但如下示例主要集中在硬綁定和軟綁定的差別之上。Function.prototype.fakeBind = function (obj) {
var self = this;
return function () {
self.call(obj);
}
}
Function.prototype.softBind = function(obj) {
var self = this;
return function () {
self.call(this === window? obj : this);
}
}; -
new
的過程當中發生了什麼,判斷如下結果 (new)function F () {
this.a = 3;
return {
a: 4;
}
}
const f = new F();
console.log(f.a);4
new
的過程大體爲以下幾個步驟由於函數最後顯式返回了一個對象,因此打印爲 4
-
建立一個新的對象 -
this 指向實例,而且執行函數 -
若是沒有顯式返回,則默認返回這個實例 -
什麼是
data descriptor
和accessor descriptor
二者均經過
Object.defineProperty()
定義,有兩個公有的鍵值數據描述符有如下鍵值
訪問器描述符有如下鍵值
const obj = {
get a() {},
set a(val) {}
}Vue 中
computed
的內部原理即是get
,而watch
的內部原理是set
-
set -
get 另外,也能夠經過字面量的形式表示訪問器描述符 -
writable 該鍵是否能夠更改 -
value -
configurable 設置該鍵是否能夠刪除 -
enumerable 設置是否可被遍歷 -
如何訪問一個對象的屬性?([[Get]])
訪問對象的屬性會觸發 [[Get]] 操做,大體簡述以下
查找過程與 Scope 查找變量很類似,只不過,對象屬性找不到,返回 undefined,而變量找不到報 Reference Error。
-
是否被 Proxy 攔截,若是攔截,查看攔截器的返回值,若是沒攔截,繼續下一步 -
檢查自身屬性,若是沒找到則繼續下一步 -
若是沒被找到,則在原型鏈上查找,若是沒找到,則返回 undefined -
如何對一個對象的屬性賦值 ([[Put]])
對一個對象的屬性賦值會觸發 [[Put]] 操做,大體簡述以下
-
若是屬性是訪問描述符,則調用 setter 函數 -
若是屬性是 data descriptor,則檢查 writable 是否可寫。若是可寫,被自身屬性覆蓋,不然在嚴格模式下將會報錯 -
普通屬性,被自身屬性覆蓋 -
若是屬性是訪問描述符,則調用 setter 函數 -
若是屬性是 data descriptor,則檢查 writable 是否可寫 -
普通屬性,直接賦值 -
檢查是否被 Proxy 攔截 -
若是該對象屬性爲自身屬性 (obj.hasOwnProperty('a') === true) -
若是該對象屬性存在於原型鏈上 -
若是該對象不存在與原型鏈上,直接給自身屬性賦值 -
如何遍歷一個對象 ($$iterator)
給對象設置 Symbol.iterator 屬性
-
如何實現一個繼承 (Object.create & call)
在 ES6 時代能夠簡單的經過 class & extends 實現繼承,ES5 時代用以下方法
function A () {}
function B () {
A.call(this)
}
B.prototype = Object.create(A.prototype)
// B.prototype = new A() 不推薦
-
如何實現
Object.create
至於爲何在繼承的時候不推薦
new
,緣由在於你很難保證 A 是一個純函數,好比它會有自身屬性,有可能操做 DOM 等。如下是一個簡單版本的實現,省略了第二個參數。Object.create = function (o) {
function F() {}
F.prototype = o;
return new F();
}
關注我
點擊閱讀原文,方便訪問本篇文章頁面連接。能夠添加我微信 shanyue94 交流,備註崗位與來源信息。也能夠添加我微信加羣,備註加羣。

參考資料
You-Dont-Know-JS: https://github.com/getify/You-Dont-Know-JS/blob/master/types%20%26%20grammar/ch2.md#user-content-strings
[2]You-Dont-Know-JS: https://github.com/getify/You-Dont-Know-JS/blob/master/types%20%26%20grammar/ch2.md#user-content-small-decimal-values
[3]知乎: https://www.zhihu.com/question/28551135
[4]You-Dont-Know-JS#user-content-testing-for-integers: https://github.com/getify/You-Dont-Know-JS/blob/master/types%20%26%20grammar/ch2.md#user-content-testing-for-integers
[5]You-Dont-Know-JS#user-content-boxing-wrappers: https://github.com/getify/You-Dont-Know-JS/blob/master/types%20%26%20grammar/ch3.md#user-content-boxing-wrappers
[6]You-Dont-Know-JS#user-content-native-prototypes: https://github.com/getify/You-Dont-Know-JS/blob/master/types%20%26%20grammar/ch3.md#user-content-native-prototypes
[7]這裏: https://stackoverflow.com/questions/10350142/why-is-document-all-falsy
[8]You-Dont-Know-JS#user-content-toboolean: https://github.com/getify/You-Dont-Know-JS/blob/master/types%20%26%20grammar/ch4.md#user-content-toboolean
本文分享自微信公衆號 - 壹前端(yiqianduan)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。