基本數據類型是按值訪問的,由於能夠操做保存在變量中的實際的值。
引用數據類型的值是保存在內存中的對象,JS不容許直接訪問內存中的位置,因此在操做的時候操做的是對象的引用;所以是引用數據類型是按照引用訪問的。es6
複製基本類型的值數組
var num1 = 5; var num2 = num1;
num1和num2中的5是徹底獨立的,互不影響
數據結構
複製引用類型函數
var obj1 = new Object(); var obj2 = obj1; obj1.name = 'lucyStar'; console.log(obj2.name); // lucyStar
咱們能夠看到,obj1保存了一個對象的實例,這個值被複制到 Obj2中。複製操做完成後,兩個變量實際引用的是同一個對象,改變了其中一個,會影響另一個值
spa
參數傳遞就跟把函數外部的值複製給函數內部的參數;設計
基本類型傳參3d
function addTen(num) { num+=10; return num; } const count = 20; const result = addTen(count); console.log(count); // 20,沒有變化 console.log(result); // 30
引用類型傳參指針
function setName(obj) { obj.name = 'luckyStar'; obj = new Object(); obj.name = 'litterStar' } const person = new Object(); setName(person); console.log(person.name); // luckyStar
在函數內部修改了參數的值,可是原始的引用仍然保持未變。
實際上,在函數內部重寫 obj時,這個變量引用的就是一個局部對象了。而這個局部對象會在函數執行完畢以後當即銷燬。code
爲了更好地解釋聲明提高,下面例子中使用 var 而不是使用 ES6新增的let和const(它們不存在聲明提高)
a = 2; var a; console.log(a); // 2
可能有人會認爲是 undefined, 由於 var a 聲明在 a = 2以後,會被從新賦值爲 undefined。但他實際上的輸出結果是 2對象
console.log(a); var a = 2;
可能有人會認爲,因爲變量 a 在使用前沒有先進行聲明,所以會拋出 ReferenceError異常。但實際它的輸出是 undefined
。
引擎會在解釋JavaScript代碼以前首先會對其進行編譯。編譯階段中一部分工做就是找到全部的聲明,並用合適的做用域將他們關聯起來。
因此正確的思考思路是:包含變量和函數在內的全部聲明都會在任何代碼被執行前首先被處理。
當你看到 var a = 2
時,可能會被認爲這是一個聲明。可是 JavaScript實際上會將其當作兩個聲明:var a
和 a = 2
; 第一個聲明是在編譯階段進行的。第二個聲明會被留在原地等待執行階段。
因此第一個例子中的代碼會以以下的形式進行處理
var a; a = 2; console.log(a);
其中第一部分是編譯,第二部分是執行。
第二個例子會按照如下流程進行處理
var a; console.log(a); a = 2;
注意:只有聲明自己會被提高,而賦值或其餘運行邏輯會留在原地。
函數聲明和變量聲明都會被提高,可是函數會首先被提高,而後纔是變量
foo(); // 1 var foo; function foo(){ console.log(1); } foo = function() { console.log(2); } // 上面代碼會按照如下流程進行處理 // 函數聲明會提高到變量前面 function foo(){ console.log(1); } var foo; foo(); // 1 foo = function() { console.log(2); }
雖然重複的 var聲明會被忽略掉,可是出如今後面的函數聲明仍是會覆蓋以前的
foo(); // 3 function foo(){ console.log(1); } var foo = function() { console.log(2); } function foo() { console.log(3); }
思考一下下面的代碼輸出什麼
var name = 'Tom'; (function() { if (typeof name == 'undefined') { var name = 'Jack'; console.log('Goodbye ' + name); } else { console.log('Hello ' + name); } })();
答案是 Goodbye Jack
。
改爲下面這樣應該會更容易理解一些
// 去掉下面這行也是同樣的,由於會優先訪問函數做用域內部的變量 // var name = 'Tom'; (function() { var name; // 注意這行 if (typeof name == 'undefined') { var name = 'Jack'; console.log('Goodbye ' + name); } else { console.log('Hello ' + name); } })();
當即執行函數的中的變量 name 的定義被提高到了頂部,並在初始化賦值以前是 undefined
,因此 typeof name == 'undefined'
咱們先來看看,var,let,const 聲明變量的位置
能夠看到 let和const聲明的變量在塊級做用域中,不存在變量提高。
// var 的狀況 console.log(a); // 輸出undefined var a = 2; // let 的狀況 console.log(b); // 報錯ReferenceError let b = 2;
總之,在代碼塊內,使用let命令聲明變量以前,該變量都是不可用的。這在語法上,稱爲「暫時性死區」(temporal dead zone,簡稱 TDZ)
function foo(x = y, y = 2) { return [x, y]; } foo(); // 報錯
由於參數x默認值等於另外一個參數y,而此時y尚未聲明,屬於「死區」。
聲明的變量是常量;
const
實際保證的,並非變量的值不變,而是變量指向的那個內存地址所保存的數據不得改動。
對於基本數據類型(數值。字符串。布爾值)。值就保存在變量指向的那個內存地址,所以等同於常量。
但對於引用數據類型主要是對象和數組)。變量指向的內存地址,保存的只是一個指向實際數據的指針。
const 只能保證這個指針是固定的(即便老是指向另外一個固定的地址),至於它指向的數據結構是否是可變的,那就徹底不能控制了。所以,將一個對象聲明爲常量必須很是當心。
const foo = {}; // 爲 foo 添加一個屬性,能夠成功 foo.prop = 123; foo.prop // 123 // 將 foo 指向另外一個對象,就會報錯 foo = {}; // TypeError: "foo" is read-only