最近發現一個問題很好玩,來跟你們分享一下。javascript
var a = {};
funcutin b(a) {
a.b = 1;
}
b(a);
console.log(a);
複製代碼
若是可以理解值傳遞和引用傳遞的同窗,就知道打印出來的就是 { b: 1};java
若是是下邊這樣的寫法呢es6
var a = {};
function b(a) {
a = 1;
}
b(a);
console.log(a);
複製代碼
此時打印出來是編程
若是改寫成這樣的:編程語言
var a = {};
function b(a) {
a.b = 1;
var a = 1;
}
b(a);
console.log(a);
複製代碼
此時打印出來是函數
這樣的呢:ui
var a = {};
function b(a) {
var a = 1;
a.b = 1;
}
b(a);
console.log(a);
複製代碼
此時打印出來是lua
終極大招來臨:es5
var a = { b: 1 };
function b(a) {
var a;
console.log(a.b);
a.b = 3;
a = { b: 2};
console.log(a.b);
}
b(a);
console.log(a.b);
複製代碼
此時打印的是?spa
在 javascript 中數據類型能夠分爲兩類:
原始數據類型值 Primitive type,好比Undefined,Null,Boolean,Number,String。
引用類型值,也就是對象類型 Object type,好比Object,Array,Function,Date等。
按值傳遞(call by value)是最經常使用的求值策略:函數的形參是被調用時所傳實參的副本。修改形參的值並不會影響實參。
按引用傳遞(call by reference)時,函數的形參接收實參的隱式引用,傳遞的是副本。這意味着函數形參的值若是被修改,實參也會被修改。同時二者指向相同的值。
通俗來講就是值傳遞將整個變量都穿進去了,引用傳遞傳入進去的是一個對象的副本(指針)
在計算機科學中,指針(Pointer)是編程語言中的一個對象,利用地址,它的值直接指向(points to)存在電腦存儲器中另外一個地方的值。因爲經過地址能找到所需的變量單元,能夠說,地址指向該變量單元。所以,將地址形象化的稱爲「指針」。意思是經過它能找到以它爲地址的內存單元。——百度百科
JS中保存基本類型的值和標識符在棧區,而引用類型的標識符和地址保存在棧區,值保存在堆內存中
簡單舉例
var a = 1;
var b = a;
b = 2;
console.log(a); // 1
console.log(b); // 2
var c = {};
var d = c;
c.name = '小周';
console.log(c); // { name: '小周' }
console.log(d); // { name: '小周' }
複製代碼
接下來回到正題
首選咱們來看一段代碼
var a = 1;
var a;
console.log(a); // 1
複製代碼
這裏第二行對a是一個重複宣告,而不是賦值,變量只有定義(宣告)後未賦值的狀況下才會輸出undefined,除非手動賦值undefined;那麼這裏,JS引擎對於重複宣告的規定以最近的變量指定(也就是賦值)做爲變量在執行時的值,因此第二行的var a;其實至關於無效;
ps: 在咱們本身寫代碼中,通常不會作這樣的蠢事。
js對形參在變量對象中是如何保存的呢,請看規範:
10.5 Declaration Binding Instantiation Every execution context has an associated VariableEnvironment. Variables and functions declared in ECMAScript code evaluated in an execution context are added as bindings in that VariableEnvironment‘s Environment Record. For function code, parameters are also added as bindings to that Environment Record.
意思就是: 不管是形參仍是函數中聲明的變量,JS對他們的處理是沒有區別的,都是保存在這個函數的變量對象中做爲局部變量進行處理;結合下咱們剛纔說的同名宣告,下邊的題就能讀懂了
function b(param) {
var param;
console.log(param); // 1
param = 2;
console.log(param); // 2
}
b(1);
複製代碼
其實行參跟局部變量是同一個東西,都是保存在函數內部的變量。
回到咱們剛纔終極大問題
var a = { b: 1 };
function b(a) {
var a;
console.log(a.b);
a.b = 3;
a = { b: 2};
console.log(a.b);
}
b(a);
console.log(a.b);
複製代碼
總體分析一下
思考:
這次分享只是單純的發現若是去深刻理解js,就會打開了一扇新世界的大門。隨時歡迎你們來喝咱們一塊兒互相交流,一塊兒打開代碼新世界的大門。
補充:
(function() {
var a = {}
function b(a) { // 函數聲明的時候,叫作形參
// 形參等同於一次聲明
a = 1;
}
b(a) // 此時的a是實參
console.log(a) // {}
})();
(function() {
//var a = 1;
//var a;
//==
//var a;
//var a;
//a = 1;
// 變量命名提高規則
// 當對一個變量進行重複聲明的時候,默認以最後一次
// 有效聲明爲主
})();
(function () {
var a = {};
function b(a) {
var a
a.b = 1;
var a = 1;
console.log(a)
}
console.dir(b);
b(a);
console.log(a); // { b:1 }
})();
console.log('--------------------------');
(function() {
var a = {};
function b(a) {
var a = 1;
a.b = 1;
console.dir(a);
function c() {}
console.dir(c)
}
b(a);
console.log(a);
})()
var a = {}
// 對象的淺拷貝跟深拷貝
// 淺拷貝
// Object.assign(currentObj, targetObj)
// { ...currentObj, ...targetObj}
var b = {...a}
b.c = 1;
console.log(b, a)
// 淺拷貝的適用範圍 對象的全部的值都只能是基礎數據類型
// 深拷貝 _.clone()
複製代碼
// 在es5當中 var 聲明的是就是全局變量,global做用域等同於window對象
// 在es6中 let 和 const聲明的在Script 做用域上,級別略低於全局做用域
// 這樣就很好的解釋了爲何window.a獲取不到,可是a卻能獲取到
// 在函數初始化的時候,會將本身內部全部權限訪問到的做用域都掛載到[[Scope]]上
// 每一個函數都會有自身的[[Scope]]只讀屬性,這樣的一個屬性被咱們稱之爲做用域鏈
// 跟script標籤有關係
let a = 1;
const b = 2;
var c = 3;
console.log('window.d', window.d)
function d() {
}
console.log('window.a', window.a)
console.log('window.b', window.b)
console.log('window.c', window.c)
console.dir(d)
複製代碼