那些被遺忘的javascript細節

Author: bugall
Wechat: bugallF
Email: 769088641@qq.com
Github: https://github.com/bugalljavascript

第一章-變量

1.內置類型
空值(null)
 未定義(undefined)
 布爾值(boolean)
 數字(number)
 字符串(string)
 對象(object)
 符號(symbol,ES6新增)
2.null==='object'
typeof null === 'object' //true

    正確的返回結果應該是"null",但這個bug由來已久,
在javascript中已經存在了將近20年,也許永遠也不會修復
由於這涉及太多現有的web系統,修復它會產生更多的bug
3.null是基本類型中惟一的假值
var a = null;
(!a && typeof a === "object"); //true
4.function(函數)是javascript的一個內置函數
實際上function是object的一個「子類型」,
函數是"可調用對象",它有一個內部屬性[[Call]],
該屬性使其能夠被調用
5.數組是對象,是object的一個子類型
typeof [1,2,3] === "object"; //true
6.javascript中的變量沒有類型,只有值纔有類型
變量能夠隨時持有任何類型的值
7.undefined與undeclared
變量在做用域中聲明可是沒有複製爲"undefined",
變量在做用域中未聲明"undeclared"

第二章-值

1.數組能夠容納任意類型的值
2.delete操做後不會影響數組的長度
使用delete運算符能夠將單元從數組中刪除,
可是單元刪除後數組的length屬性並不會發生變化
3.稀疏數組的length受最大值影響
var a = [];
a[0] = 1;
a[2] = 3;
a[1]; //undefined
a.length; //3
4.數組中數字和字符做爲索引時,數組長度只統計數字部分
var a = [];
a[0] = 1;
a["bugall"] = 2;
a.length; //1
a["bugall"] = 2;
a.bugall = 2;
5.若是字符串鍵值可以被強制類型轉換爲十進制數字的話,
它會被當作數組索引處理
var a = [];
a["13"] = 42;
a.length; //14
6.javascript只有一種數值類型:number
javascript中的數字類型是基於IEEE 754標準實現的,
該標準一般也被稱爲「浮點數」,javascript使用的是
"雙精度"格式64位二進制
7.42.toFixed(2) //SyntaxError
42..toFixed(2) = 42.00,42.toFixed(2)
是無效語法,由於.被視爲常量42.的一部分
8.0.1+0.2 === 0.3 //false
二進制浮點數中0.1和0.2並非十分精確,它們相加的結果並不是恰好等於0.3,而是一個
很是接近的數字0.3000000000000000002
9. 如何判斷兩個小數是否相等
function numbersCloseEnoughToEqual(n1,n2){
    return Math.abs(n1-n2)<Number.EPSILON
}
numbersCloseEnoughToEqual(0.3,0.3) //true
10. 整數的安全範圍
數字的呈現方式決定了「整數」的安全值範圍遠遠小於
Number.MAX_VALUE,最大是2^53-1
11. 整形檢測
Number.isInterger(42) //true
Number.isInterger(42.000) //true
Number.isInterger(42.001) //false
12.不是值的值
undefined類型只有一個值,即undefined,
null類型也只有一個值,即null,它們的名稱是類型也是值
13.不是數字的數字
若是數學運算的操做不是數字類型,就沒法返回一
個有效的數字,這種狀況下返回值爲NaN
var a = 2 / "foo"  //NaN
typeof a === "number"  //true
NaN是執行數學運算沒有成功返回的結果,
NaN是一個特殊值,它和自身不相等,是惟一一個非自反的值
NaN != NaN //true
14. isNaN理解爲is not a number || is NaN
前者一直是我錯誤的理解,正確的理解應該是is NaN,
判斷數學運算是否錯誤,返回錯誤的值
var a = 2 / "foo"
var b = "foo"
a; //NaN
b; //"foo"
isNaN(a) //true
isNaN(b) //true
這是不一樣的結果,一個是運算出錯,一個是字符串
代碼中儘量得少出現isNaN()
15. 簡單值都是經過值類型傳遞
null,undefined,string,number,boolean
16. 複合類型-對象都是經過引用傳遞
咱們沒法更改使用值傳遞仍是引用傳遞,一切根據值的類型肯定

第三章 對象

1. 匿名仍是在堆棧追蹤中不會顯示出有意義的函數名,使的調試很困難
2. 函數聲明,與函數表達式
function a(){...} //函數聲明
    var a = function(){...} //函數表達式
2. 當即執行函數表達式
(function foo(){
    var a=3;
    console.log(a) //3
})()
因爲函數被包含在一對()括號內部,所以成爲一個函數表達式,
經過在末尾加上另外一個()能夠當即執行這個函數。
第一個()將函數變成表達式
3. 變量聲明應該距離使用的地方越近越好
4. 最小力度的做用域,最小的做用域氣泡原則
5. 變量提高
console.log(a) //undefined
var a = 3;
console.log(a) //3
6. 使用var定義變量的時候,它寫在哪裏都是同樣的,由於它們最終都會屬於外部做用域
console.log(a) //undefined
if(false){
    var a = 3;
}
7. 使用let進行聲明不會在塊做用域中進行提高
{
    console.log(a) //ReferenceError
    var a = 3
    console.log(a) //3
}
8. 任何聲明在某個做用域內的變量,都將附屬與這個做用域
9. 函數聲明會被提高,可是函數表達式不會被提高
foo() //bugall
function foo(){
    console.log('bugall')
}

a() // ReferenceError
var a = foo(){
    console.log('bugall')
}
10. 函數聲明和變量聲明都會被提高,可是函數會首先被提高,而後纔是變量
11. 閉包能夠組織GC回收(GC的回收機制採用引用計數)
function a(){
    var n = 'bugall'
    function b(){
        console.log(n)
    }
    return b
}
foo() //'bugall'
覺得b()覆蓋了a()裏的變量n,因此a的資源就沒法釋放,而這個引用就叫作閉包
12. 做用域是基於調用棧的,而不是代碼中得做用域嵌套
13. 箭頭函數用當前的詞法做用域覆蓋了this得原本結構
var obj = {
    name = 'bugall',
    sayName = function(){
        console.log(this.name)
    })
}
var name = 'not bugall'
obj.cool() //bugall ,this的做用域是obj對象
setTimeount(obj.cool,100) //not bugall,this變成了當前做用域

用this的形式
var obj = {
    name = 'bugall',
    sayName = function(()=>{
        console.log(this.name)
    })
}
var name = 'not bugall'
obj.cool() //bugall ,this的詞法做用域是obj
setTimeount(obj.cool,100) //bugall, this的詞法做用域沒有變
14. 匿名函數沒法指向自身
function foo(){
    foo.count = 4; //foo指向自身
}

setTimeout(function(){
    //匿名函數由於沒有名字,沒法指定在堆棧上得肯定位置,因此不能引用自身
})
15. 函數的this默認綁定是全局
function foo(){
    console.log(this.a)
}
var a = 2
foo() //2
16. 使用嚴格模式下,不能使用全局對象用於默認綁定
function foo(){
    'use strict'
    console.log(this.a)
}
var a = 2
foo() //TypeError: this is undefined
17. 對象引用鏈中,只有最後一層在調用位置中起做用
function foo(){
    console.log(this.a)
}
var obj2 = {
    a:42,
    foo:foo
}
var obj1 = {
    a:2,
    obj2:obj2
}
obj1.obj2.foo() //42
18. call || apply實現this的顯示綁定
function foo(){
    console.log(this.a)
}
var obj = {
    a:2
}
foo.call(obj) //2
咱們在調用foo的時候強制把它的this綁定到obj上
19. bind 返回一個硬編碼的新函數
function foo(something){
    console.log(this.a,somethis)
    return this.a + something
}
var obj = {
    a:2
}
var bar = foo.bind(obj)
var b = bar(3) // 2 3
console.log(b) //5
bind(...)會返回一個硬編碼的新函數,
它會把你指定的參數設置爲this的上下文並調用原始函數
20. javascript中的new
在javascript中,構造函數只是一些使用new操做符
時被調用的函數,他們並不會屬於某個類,也不會實例化一個類,
實際上它們甚至都不能說是一個特殊的函數類型,他們只是
被new操做符調用的普通函數而已。
使用new來調用函數的時候,會自動執行下面的操做
1. 建立(或者說構造)一個全新的對象
2. 這個新對象會被執行[[prototype]]鏈接
3. 這個新對象會被綁定到函數調用的this
4. 若是函數沒有返回其餘對象,那麼new表達式中的函數調用會自動返回這個新對象
function foo(a){
    this.a=a
}
var bar = new foo(2)
console.log(bar.a) //2
21. 隱式綁定與顯示綁定,顯示綁定優先級最高
function foo(){
    console.log(this.a)
}
var obj1 = {
    a:2,
    foo:foo
}
var obj2 = {
    a:3,
    foo:foo
}
obj1.foo() //2
obj2.foo() //3
obj1.foo.call(obj2) //3
obj2.foo.call(obj1) //2
22. new綁定與隱式綁定,new綁定優先級高於隱式綁定
function foo(somethis){
    this.a = somethis
}
var obj1 = {
    foo:foo
}
var obj2 = {}
obj1.foo(2)
console.log(obj1.a)//2

obj1.foo.call(obj2,3)
console.log(obj2.a) //3

var bar = new obj1.foo(4)
console.log(obj1.a) //2
console.log(bar.a) //4
bar被綁定到obj1上,可是new bar(3)並無像咱們預期的那樣把obj1.a修改成3
相反,new修改了硬綁定(到obj1的)調用bar(...)中的this,由於使用了new綁定獲得
一個名字爲baz的新對象,而且baz.a的值是3
23. call apply bind的區別
call,apply屬於顯示綁定
bind屬於硬綁定,硬綁定屬於顯示綁定的一種
24. 硬綁定的缺點
硬綁定會下降函數的靈活性,使用硬綁定以後就
沒法使用隱式綁定或是顯式綁定來修改this
25. 若是判斷this綁定
1. 函數是否在new中調用(new綁定),若是是的話this綁定的是新建立的對象
var bar = new foo()

2. 函數是否經過call,apply(顯示綁定)或者硬綁定調用,
若是是的話,this綁定的是指定的對象
var bar = foo.call(obj2)

3. 函數是否在某個上下文對象中調用(隱式綁定),若是是的話,this綁定的是那個上下文對象
var var = obj1.foo()

4. 若是都不是的話,使用默認綁定,若是在嚴格模式下,就綁定到undefined,不然綁定到全局對象
var bar = foo()
25. 當null,undefined做爲this的綁定對象傳入call,apply,bind的時候,實際應用的是默認綁定
相關文章
相關標籤/搜索