JS基礎總結

(以下內容,參考自阮一峰es5)

  1. console.log(對象)時,頁面彈出的就是[object,Object] 它代表什么?
alert會調用變量的toString方法,toString會生成[object classname]的結果,
第一個object表示變量是對象,不是值類型,classname表示該對象的類名。
[object Object]前面一個object表示他是對象,后面一個Object表示它是Object類的。
  1. 函數執行時所在的作用域,是定義時的作用域,而不是調用時所在的作用域
  2. 函數參數如果是原始類型的值(數值、字符串、布爾值),傳遞方式是傳值傳遞(passes by value)。這意味著,在函數體內修改參數值,不會影響到函數外部。
var p = 2;

function f(p) {
  p = 3;
}
f(p);

p // 2

如果函數參數是復合類型的值(數組、對象、其他函數),傳遞方式是傳址傳遞(pass by reference)。也就是說,傳入函數的原始值的地址,因此在函數內部修改參數,將會影響到原始值。

var obj = { p: 1 };

function f(o) {
  o.p = 2;
}
f(obj);

obj.p // 2

注意,如果函數內部修改的,不是參數對象的某個屬性,而是替換掉整個參數,這時不會影響到原始值。(本質上是替換了地址)

var obj = [1, 2, 3];

function f(o) {
  o = [2, 3, 4];
}
f(obj);

obj // [1, 2, 3]
  1. Object.keys
  • Object.keys方法的參數是一個對象,返回一個數組。該數組的成員都是該對象自身的(而不是繼承的)所有屬性名。
var obj = {
  p1: 123,
  p2: 456
};
Object.keys(obj) // ["p1", "p2"]
  • Object.keys方法用于數組時,返回數組的所有鍵名。可以看到數組的鍵名就是整數0、1、2。
var arr = ['a', 'b', 'c'];

Object.keys(arr)
// ["0", "1", "2"]
  • 區別的原因:由于數組成員的鍵名是固定的(默認總是0、1、2...),因此數組不用為每個元素指定鍵名,而對象的每個成員都必須指定鍵名。JavaScript 語言規定,對象的鍵名一律為字符串,所以,數組的鍵名其實也是字符串。之所以可以用數值讀取,是因為非字符串的鍵名會被轉為字符串。
var arr = ['a', 'b', 'c'];

arr['0'] // 'a'
arr[0] // 'a'
  1. 對象有兩種讀取成員的方法:點結構(object.key)和方括號結構(object[key])。但是,對于數值的鍵名,不能使用點結構。
var arr = [1, 2, 3];
arr.0 // SyntaxError
  1. 數組遍歷方法:
  • for
  • while
  • forEach
var a = [1, 2, 3];

// for循環
for(var i = 0; i < a.length; i++) {
  console.log(a[i]);
}

// while循環
var i = 0;
while (i < a.length) {
  console.log(a[i]);
  i++;
}

// 反向遍歷,從最后一位開始
var l = a.length;
while (l--) {
  console.log(a[l]);
}
var colors = ['red', 'green', 'blue'];
colors.forEach(function (color) {
  console.log(color);
});
// red
// green
// blue
  • 不使用for...in,原因是for...in不僅會遍歷數組所有的數字鍵,還會遍歷非數字鍵。
var a = [1, 2, 3];
a.foo = true;

for (var key in a) {
  console.log(key);
}
// 0
// 1
// 2
// foo
  1. 數組的空位
  • 使用delete命令刪除一個數組成員,會形成空位,并且不會影響length屬性。
var a = [1, 2, 3];
delete a[1];

a[1] // undefined
a.length // 3
  • 數組的某個位置是空位,與某個位置是undefined,是不一樣的。如果是空位,使用數組的forEach方法、for...in結構、以及Object.keys方法進行遍歷,空位都會被跳過。
var a = [, , ,];

a.forEach(function (x, i) {
  console.log(i + '. ' + x);
})
// 不產生任何輸出

for (var i in a) {
  console.log(i);
}
// 不產生任何輸出

Object.keys(a)
// []
  • 如果某個位置是undefined,遍歷的時候就不會被跳過。
var a = [undefined, undefined, undefined];

a.forEach(function (x, i) {
  console.log(i + '. ' + x);
});
// 0. undefined
// 1. undefined
// 2. undefined

for (var i in a) {
  console.log(i);
}
// 0
// 1
// 2

Object.keys(a)
// ['0', '1', '2']
  1. 類數組對象:
  • “類似數組的對象”的根本特征,就是具有length屬性。只要有length屬性,就可以認為這個對象類似于數組。但是有一個問題,這種length屬性不是動態值,不會隨著成員的變化而變化。
  • 典型的“類似數組的對象”是函數的arguments對象,以及大多數DOM 元素集,還有字符串
// arguments對象
function args() { return arguments }
var arrayLike = args('a', 'b');

arrayLike[0] // 'a'
arrayLike.length // 2
arrayLike instanceof Array // false

// DOM元素集
var elts = document.getElementsByTagName('h3');
elts.length // 3
elts instanceof Array // false

// 字符串
'abc'[1] // 'b'
'abc'.length // 3
'abc' instanceof Array // false
  • 將類數組對象變成數組的方法:
    1. 使用數組的slice方法,變成真正的數組:Array.prototype.slice.call()
     // arguments對象
     function args() { return arguments }
     var arrayLike = args('a', 'b');
     var arr = Array.prototype.slice.call(arrayLike);
    
    1. 使用數組的forEach方法,通過call()把數組的方法放到對象上面:Array.prototype.forEach.call()
    // arguments對象
    function args() { return arguments }
    var arrayLike = args('a', 'b');
    
    function print(value, index) {
    console.log(index + ' : ' + value);
    }
    
    Array.prototype.forEach.call(arrayLike, print);
    
    • arrayLike代表一個類似數組的對象,本來是不可以使用數組的forEach()方法的,但是通過call(),可以把forEach()嫁接到arrayLike上面調用。

    • 注意,這種方法比直接使用數組原生的forEach要慢,所以最好還是先將“類似數組的對象”轉為真正的數組,然后再直接調用數組的forEach方法。

    var arr = Array.prototype.slice.call('abc');
    arr.forEach(function (chr) {
      console.log(chr);
    });
    // a
    // b
    // c
    
  1. 加法運算符是在運行時決定,到底是執行相加,還是執行連接。也就是說,運算子的不同,導致了不同的語法行為,這種現象稱為“重載”(overload)。由于加法運算符存在重載,可能執行兩種運算,使用的時候必須很小心。
'3' + 4 + 5 // "345"
 3 + 4 + '5' // "75"
  1. 對象的相加:必須先轉成原始類型的值,然后再相加。
var obj = { p: 1 };
obj + 2 // "[object Object]2"

操作順序:
1.首先,自動調用對象的valueOf方法,返回對象自身

 var obj = { p: 1 };
 obj.valueOf() // { p: 1 }

2.再自動調用對象的toString方法,將其轉為字符串。

var obj = { p: 1 };
obj.valueOf().toString() // "[object Object]"
  • 所以,可以通過自定義對象的valueOf()和toString()方法
var obj = {
  valueOf: function () {
    return 1;
  },
  toString: function () {
    return 'hello';
  }
};

obj + 2 // 3

由于valueOf方法直接返回一個原始類型的值,所以不再調用toString方法。

  • 這里有一個特例,如果運算子是一個Date對象的實例,那么會優先執行toString方法。
  1. 強制轉換_Number()方法,參數是對象時轉型過程:
    (1). 調用對象自身的valueOf方法。如果返回原始類型的值,則直接對該值使用Number函數,不再進行后續步驟。
    (2). 如果valueOf方法返回的還是對象,則改為調用對象自身的toString方法。如果toString方法返回原始類型的值,則對該值使用Number函數,不再進行后續步驟。
    (3). 如果toString方法返回的是對象,就報錯。

    var obj = {x: 1};
    Number(obj) // NaN
    
    // 等同于
    if (typeof obj.valueOf() === 'object') {
      Number(obj.toString());
    } else {
      Number(obj.valueOf());
    }
    

    上面代碼中,Number函數將obj對象轉為數值。背后發生了一連串的操作,首先調用obj.valueOf方法, 結果返回對象本身;于是,繼續調用obj.toString方法,這時返回字符串[object Object],對這個字符串使用Number函數,得到NaN。

    默認情況下,對象的valueOf方法返回對象本身,所以一般總是會調用toString方法,而toString方法返回對象的類型字符串(比如[object Object])。所以,會有下面的結果。

    Number({}) // NaN
    

    如果toString方法返回的不是原始類型的值,結果就會報錯。

    var obj = {
      valueOf: function () {
        return {};
      },
      toString: function () {
        return {};
      }
    };
    
    
    Number(obj)
    // TypeError: Cannot convert object to primitive value
    

    valueOf和toString方法,都是可以自定義的。

    Number({
      valueOf: function () {
        return 2;
      },
      toString: function () {
        return 3;
      }
    })
    // 2
    
  2. parseIntparseFloatNumber的區別:parseInt、parseFloat是對字符換轉換;Number可以對任意類型轉換

  3. Number()+運算符轉換數值時,null轉為數值時為0,而undefined轉為數值時為NaN。

  4. Object.prototype.toString()

  • toString方法的作用是返回一個對象的字符串形式,默認情況下返回類型字符串。
    var o1 = new Object();
    o1.toString() // "[object Object]"
    
    var o2 = {a:1};
    o2.toString() // "[object Object]"
    
  • 字符串[object Object]本身沒有太大的用處,但是通過自定義toString方法,可以讓對象在自動類型轉換時,得到想要的字符串形式。
    var obj = new Object();
    
    obj.toString = function () {
      return 'hello';
    };
    
    obj + ' ' + 'world' // "hello world"
    
    上面代碼表示,當對象用于字符串加法時,會自動調用toString方法。由于自定義了toString方法,所以返回字符串hello world
  • 數組字符串函數Date 對象都分別部署了自定義的toString方法,覆蓋了Object.prototype.toString方法。
    [1, 2, 3].toString() // "1,2,3"
    
    '123'.toString() // "123"
    
    (function () {
      return 123;
    }).toString()
    // "function () {
    //   return 123;
    // }"
    
    (new Date()).toString()
    // "Tue May 10 2016 09:11:31 GMT+0800 (CST)"
    
  • toString() 的應用:判斷數據類型
    Object.prototype.toString方法返回對象的類型字符串,因此可以用來判斷一個值的類型。
    Object.prototype.toString.call(value)
    

    利用這個特性,可以寫出一個比typeof運算符更準確的類型判斷函數。

    var type = function (o){
    var s = Object.prototype.toString.call(o);
    return s.match(/\[object (.*?)\]/)[1].toLowerCase();
    };
    
    type({}); // "object"
    type([]); // "array"
    type(5); // "number"
    type(null); // "null"
    type(); // "undefined"
    type(/abcd/); // "regex"
    type(new Date()); // "date"
    
  1. Array.prototype.slice()方法:
    返回數組的深拷貝
    var arr = ['djf', {a:1,b:2,c:[4,5,6]}, 112];
    var copy = arr.slice();
    console.dir(copy);
    copy[0] = 0;
    console.dir(arr);
    
    slice方法的一個重要應用,是將類似數組的對象轉為真正的數組。
    Array.prototype.slice.call({ 0: 'a', 1: 'b', length: 2 })
    // ['a', 'b']
    
    Array.prototype.slice.call(document.querySelectorAll("div"));
    Array.prototype.slice.call(arguments);
    
  2. Array.prototype.concat()方法:
  • concat方法用于多個數組的合并。它將新數組的成員,添加到原數組成員的后部,然后返回一個新數組,原數組不變。
  • 如果數組成員包括對象,concat方法返回當前數組的一個淺拷貝。所謂“淺拷貝”,指的是新數組拷貝的是對象的引用。
    var obj = { a: 1 };
    var oldArray = [obj];
    
    var newArray = oldArray.concat();
    
    obj.a = 2;
    newArray[0].a // 2
    
    上面代碼中,原數組包含一個對象,concat方法生成的新數組包含這個對象的引用。所以,改變原對象以后,新數組跟著改變。
  1. String.prototype.concat()方法:
  • concat方法用于連接兩個字符串,返回一個新字符串,不改變原字符串。
    var s1 = 'abc';
    var s2 = 'def';
    
    s1.concat(s2) // "abcdef"
    s1 // "abc"
    
  • 如果參數不是字符串,concat方法會將其先轉為字符串,然后再連接。
    var one = 1;
    var two = 2;
    var three = '3';
    
    ''.concat(one, two, three) // "123"
    one + two + three // "33"
    
    上面代碼中,concat方法將參數先轉成字符串再連接,所以返回的是一個三個字符的字符串。作為對比,加號運算符在兩個運算數都是數值時,不會轉換類型,所以返回的是一個兩個字符的字符串。
  1. String.prototype.slice()方法:
  • slice方法用于從原字符串取出子字符串并返回,不改變原字符串。它的第一個參數是子字符串的開始位置,第二個參數是子字符串的結束位置(不含該位置)。
    'JavaScript'.slice(0, 4) // "Java"
    
  • String.prototype.substring()
    substring方法用于從原字符串取出子字符串并返回,不改變原字符串,跟slice方法很相像。它的第一個參數表示子字符串的開始位置,第二個位置表示結束位置(返回結果不含該位置)。
    'JavaScript'.substring(0, 4) // "Java"
    
  • String.prototype.substr()
    substr方法用于從原字符串取出子字符串并返回,不改變原字符串。
    substr方法的第一個參數是子字符串的開始位置(從0開始計算),第二個參數是子字符串的長度
    'JavaScript'.substr(0, 4) // "Java"
    
  1. String.prototype.trim()方法:
    trim方法用于去除字符串兩端的空格,返回一個新字符串,不改變原字符串。
     '  hello world  '.trim()
     // "hello world"
    
  2. 事件循環
  • JavaScript 運行時,除了一個正在運行的主線程,引擎還提供多個任務隊列(task queue),里面是各種需要當前程序處理的異步任務。
  • 引擎檢查異步任務隊列的任務,執行完的任務會重新進入到主線程,這種機制叫:事件循環
  1. 定時器運行機制:
    setTimeoutsetInterval指定的回調函數,必須等到本輪事件循環的所有同步任務都執行完,才會開始執行。
  • 由于前面的任務到底需要多少時間執行完,是不確定的,所以沒有辦法保證,setTimeoutsetInterval指定的任務,一定會按照預定時間執行。
    setTimeout(someTask, 100);
    veryLongTask();
    
    上面代碼的setTimeout,指定100毫秒以后運行一個任務。但是,如果后面的veryLongTask函數(同步任務)運行時間非常長,過了100毫秒還無法結束,那么被推遲運行的someTask就只有等著,等到veryLongTask運行結束,才輪到它執行。
  • 再看一個setInterval的例子。
    setInterval(function () {
    console.log(2);
    }, 1000);
    
    sleep(3000);
    
    function sleep(ms) {
      var start = Date.now();
      while ((Date.now() - start) < ms) {
      }
    }
    
    上面代碼中,setInterval要求每隔1000毫秒,就輸出一個2。但是,緊接著的sleep語句需要3000毫秒才能完成,那么setInterval就必須推遲到3000毫秒之后才開始生效。注意,生效后setInterval不會產生累積效應,即不會一下子輸出三個2,而是只會輸出一個2。
  • setTimeout(f, 0)會在下一輪事件循環一開始就執行。
    setTimeout(function () {
      console.log(1);
    }, 0);
    console.log(2);
    // 2
    // 1
    
  1. Promise對象:
  1. 除了時間對象Date。其他對象求值都是先調用valueOf()方法,再調用toString()方法
  2. 頁面重流和重繪性能優化
  • 讀取 DOM 或者寫入 DOM,盡量寫在一起,不要混雜。不要讀取一個 DOM 節點,然后立刻寫入,接著再讀取一個 DOM 節點。
  • 緩存 DOM 信息。
  • 不要一項一項地改變樣式,而是使用 CSS class 一次性改變樣式。
  • 使用documentFragment操作 DOM
  • 動畫使用absolute定位或fixed定位,這樣可以減少對其他元素的影響。
  • 只在必要時才顯示隱藏元素。
  • 使用window.requestAnimationFrame(),因為它可以把代碼推遲到下一次重流時執行,而不是立即要求頁面重流。
  • 使用虛擬 DOM(virtual DOM)庫。
  1. History對象:
  • 屬性:
    1. History.length:當前窗口訪問過的網址數量(包括當前網頁)
    2. History.state:History 堆棧最上層的狀態值
  • 方法:
    1. History.back()
    2. History.forward()
    3. History.go(): history.go(0)相當于刷新當前頁面。
    4. History.pushState:pushState()方法不會觸發頁面刷新,只是導致 History 對象發生變化,地址欄會有反應。
      window.history.pushState(state, title, url)
      state:一個與添加的記錄相關聯的狀態對象,主要用于popstate事件。
      該事件觸發時,該對象會傳入回調函數。
      也就是說,瀏覽器會將這個對象序列化以后保留在本地,重新載入這個頁              
      面的時候,可以拿到這個對象。如果不需要這個對象,此處可以填null。
      title:新頁面的標題。但是,現在所有瀏覽器都忽視這個參數,所以這里可      
      以填空字符串。
      url:新的網址,必須與當前頁面處在同一個域。瀏覽器的地址欄將顯示這    
      個網址。
      
      如果pushState的 URL 參數設置了一個新的錨點值(即hash),并不會觸發hashchange事件。反過來,如果 URL 的錨點值變了,則會在 History 對象創建一條瀏覽記錄。
      如果pushState()方法設置了一個跨域網址,則會報錯。
    5. History.replaceState(): 方法用來修改 History 對象的當前記錄
  • 事件popState: 每當同一個文檔的瀏覽歷史(即history對象)出現變化時,就會觸發popstate事件。
    注意:
    1. 僅僅調用pushState()方法或replaceState()方法,并不會觸發該事件,只有用戶點擊瀏覽器倒退按鈕和前進按鈕,或者使用 JavaScript 調用History.back()、History.forward()、History.go()方法時才會觸發。

    2. 該事件只針對同一個文檔,如果瀏覽歷史的切換,導致加載不同的文檔,該事件也不會觸發。

    3. 頁面第一次加載的時候,瀏覽器不會觸發popstate事件。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,247評論 6 543
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,520評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,362評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,805評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,541評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,896評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,887評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,062評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,608評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,356評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,555評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,077評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,769評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,175評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,489評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,289評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,516評論 2 379

推薦閱讀更多精彩內容