值類型:undefined, Number, Boolean, String,nullhtml
引用類型:Objecthtml5
值類型存放在棧中windows
引用類型將地址存放在棧中,將數據實體存放在堆中數組
not defined是未聲明,當使用未聲明變量時瀏覽器會拋出這個錯誤瀏覽器
undefined是已聲明未賦值,typeof undefined是undefined閉包
null相似於空對象,是一個已定義,定義爲空的值,typeof null 是 objectapp
若是是值類型,直接用typeof判斷異步
若是是引用類型,使用instanceof判斷,instanceof基於原型鏈,通常用於判斷自定義對象函數
constructor是prototype上的一個屬性,他容易被重寫覆蓋,因此不可信賴this
Object.prototype.toString.call,調用Object原型上的toString方法能夠獲得當前調用者的具體類型
Object.prototype.toString.call().slice(8, -1); // Object|Array|Number|String|Boolean...
每個函數上都有一個prototype屬性,稱爲原型對象
函數實例化產生對象
每個對象都有一個__proto__
(隱匿原型)屬性,指向構造它的原型對象。
原型對象自己也是對象,也有一個隱匿原型,指向它的原型對象。
沿着隱匿原型鏈最終會指向Object.prototype,它的原型對象是null
這就構成一個原型鏈
PS. 將原子類型賦給 prototype 的操做將會被忽略
function Foo() {} Foo.prototype = 1; // 無效
instanceof的原理
A instanceof B
A的原型鏈是否會到達B.prototype
經過原型鏈實現繼承,原型對象上能夠定義屬性和方法。
當要在一個對象上尋找某個屬性,先在對象自己找,沒有的話,再沿着原型鏈向上找原型對象裏有沒有,向上查找找到爲止,到達頂部仍未找到,返回undefined
PS.判斷對象上是否有某個屬性,而非其原型鏈上有,使用
hasOwnProperty
函數
在函數調用時或者是全局代碼開始運行時產生,處理的事情:變量聲明,函數聲明,函數聲明形式的定義賦值,定義this,在函數內還有定義arguments的操做
PS.
arguments
變量不是一個數組(Array
)。 儘管在語法上它有數組相關的屬性length
,但它不從Array.prototype
繼承,實際上它是一個對象(Object
)。所以,沒法對
arguments
變量使用標準的數組方法,好比push
,pop
或者slice
。 雖然使用for
循環遍歷也是能夠的,可是爲了更好的使用數組方法,最好把它轉化爲一個真正的數組。Array.prototype.slice.call(arguments);
全局代碼開始執行時,產生一個全局的執行上下文,壓棧
代碼執行到函數A調用時,產生一個函數A的執行上下文,壓棧
函數A中調用函數B,產生一個函數B的執行上下文,壓棧
函數B,執行完畢,出棧銷燬執行上下文
函數A,執行完畢,出棧並銷燬執行上下文
this存在於執行上下文中
PS. 一些誤解
// 1. 嚴格按照規範 Foo.method = function() { // 在這,this是Foo的實例化對象 function test() { // this 將會被設置爲全局對象 } test(); } // 2. 函數別名 var test = someObject.methodTest; test(); // this設置爲全局對象
PS. apply和call的用法
function.apply(null, arguments);
function.call(null, arg1, arg2);
ES5中只有函數做用域的概念,做用域是一個虛擬概念,沒有具體的數據類型或者結構。
一個函數的做用域在函數定義時肯定,建立函數的做用域成爲該函數的上級做用域
在函數中尋找變量,先找到函數做用域對應的執行上下文,在執行上下文中找變量。
沒有找到的話,看上級函數做用域,向上查找到,找到爲止。
若是找不到,則會拋出 ReferenceError
異常。
PS. 好比,當訪問函數內的
foo
變量時,JavaScript 會按照下面順序查找:
- 當前做用域內是否有
var foo
的定義。- 函數形式參數是否有使用
foo
名稱的。- 函數自身是否叫作
foo
。- 回溯到上一級做用域,而後從 #1 從新開始。
什麼是閉包?
一個函數中有依賴外部變量,函數在建立它的做用域以外被調用。
將會在執行上下文棧中保留上級做用域的執行上下文。
若在閉包使用完畢以後不手動解除引用,相關執行上下文將會一直保留於執行上下文棧中,佔據內存空間,若持續積累,容易形成內存泄漏。
常見應用,函數做爲返回值,函數做爲參數。
經典問題
for (var i = 0; i < 5; i++) { setTimeout(function() { console.log(i); }, 1000); } // 5,5,5,5,5 for (let i = 0; i < 5; i++) { setTimeout(function() { console.log(i); }, 1000); } // 0,1,2,3,4 for (var i = 0; i < 5; i++) { (function (i) { setTimeout(function() { console.log(i); }, 1000); })(i) } // 0, 1, 2, 3, 4 for(var i = 0; i < 5; i++) { setTimeout((function(e) { return function() { console.log(e); } })(i), 1000) } // 0, 1, 2, 3, 4
基礎繼承
var Bar = function () {}; Bar.prototype = { greet: function (name) { console.log(name); } } var Foo = function () {} Foo.prototype.__proto__ = Bar.prototype;
等價於
var Bar = function () {}; var Foo = function () {} Foo.prototype = new Bar();
原理:實現原型鏈
缺點:屬性不獨立
組合繼承
var Bar = function (name) { this.name = name; } Bar.prototype = { greet: function () { console.log(this.name); } } var Foo = function (name) { Bar.apply(this, arguments); } Foo.prototype = new Bar();
原理:把this屬性賦值在子類的做用域執行一次,方法經過原型鏈繼承
缺點:this屬性賦值進行了兩次
寄生組合式繼承
var Bar = function (name) { this.name = name; } Bar.prototype = { greet: function () { console.log(this.name); } } var Foo = function (name) { Bar.apply(this, arguments); } Foo.prototype = Object.create(Bar.prototype); Foo.prototype.constructor = Foo;
原理: 把this屬性賦值在子類的做用域執行一次,手動鏈接原型對象的拷貝
優勢:解決組合繼承的缺點
extends方法
class Point { constructor(x, y) { this.x = x; this.y = y; } toString () { return this.x + ' ' + this.y; } } class ColorPoint extends Point { constructor(x, y, color) { super(x, y); // 調用父類的constructor(x, y) this.color = color; } toString() { return this.color + ' ' + super.toString(); // 調用父類的toString() } }
子類本身的this
對象,必須先經過父類的構造函數完成塑造,獲得與父類一樣的實例屬性和方法,而後再對其進行加工,加上子類本身的實例屬性和方法。若是不調用super
方法,子類就得不到this
對象。
PS. new 運算符作了什麼
// 1. 首先建立一個空對象 var o = new Object(); // 2. 將空對象的原型賦值爲構造器函數的原型 o.__proto__ = A.prototype; // 3. 更改構造器函數內部this,將其指向新建立的空對象 A.call(o);
轉爲數值:Number針對全部類型,parseInt和parseFloat針對字符串
字符串轉換爲數字的經常使用方法:
+'010' === 10 Number('010') === 10 parseInt('010', 10) === 10 // 用來轉換爲整數 +'010.2' === 10.2 Number('010.2') === 10.2 parseInt('010.2', 10) === 10 parseFloat('10.1.2') === 10.1 // 字符轉換爲浮點數 Number(undefined) // NaN Number嚴格轉換,只要有一個字符沒法轉爲數值輸出NaN parseInt原理爲從左往右讀字符串,讀到非數值字符爲止 parseFloat原理爲從左往右讀字符串,讀到第二個小數點或者非數值非小數點字符爲止
'5' + 1 === '51'
主線程運行時產生堆和執行棧
主線程以外,還存在一個"任務隊列"。只要異步任務有了運行結果,就在"任務隊列"之中放置一個事件。
一旦"執行棧"中的全部同步任務執行完畢,系統就會讀取"任務隊列",看看裏面有哪些事件。對應的異步任務,結束等待狀態,進入執行棧,開始執行
任務隊列分爲宏任務隊列和微任務隊列
執行同步任務 -> 處理微任務隊列 -> 處理宏任務隊列裏隊首任務 -> 處理微任務隊列
Promise.resolve().then(()=>{ console.log('Promise1') setTimeout(()=>{ console.log('setTimeout1') },0) }) setTimeout(()=>{ console.log('setTimeout2') Promise.resolve().then(()=>{ console.log('Promise2') }) Promise.resolve().then(()=>{ console.log('Promise3') }) },0) setTimeout(()=>{ console.log('setTimeout4') Promise.resolve().then(()=>{ console.log('Promise4') }) },0) Output: Promise1 setTimout2 Promise2 Promise3 setTimeout4 Promise4 setTimeout1
setTimeout:產生一個宏任務,在指定時間以後加入任務隊列。
setInterval:循環產生宏任務,但存在問題,若任務執行時間長於指定時間間隔,會產生堆疊執行效果。