前言
今天在一個前端羣組裏,看到有人發出一個問題:
var o = new Object();
function foo(obj){
var obj = o;
obj.name = '123';
obj = new Object();
obj.name = '456';
}
foo(o);
console.log(o.name)
如上代碼所示,在不改變任何條件和數據的情況下,有沒有可能輸出“456”?
沒多久,羣組中人就開始下結論,正常情況下是不能的。原因嗎,稍微知道引用類型的使用方式的人都知道的,new Object(),改變了obj的指向,永遠沒法獲得456。
問題來了
正常情況下不可能輸出456,那在什麼樣的錯誤情況下能夠輸出456呢?畢竟程序員就是擅長寫BUG的嗎,應該能夠搞得非正常情況的吧。
然後,手殘的自己突然想到前兩天剛學習的Proxy。我們現在是賦值時出的問題,那麼能不能從元數據上解決問題呢?嘗試一下:
//demo_01
var o = new Object();
o = new Proxy(o,{
set(target, key, value,receiver){
if(Object.is('name',key))
return Reflect.set(target, key, `456`,receiver);
return Reflect.set(target, key, value , receiver);
}
})
function foo(obj){
var obj = o;
obj.name = '123';
obj = new Object();
obj.name = '456';
}
foo(o);
console.log(o.name)
完美,成功地輸出了456。
什麼?你不知道Proxy是什麼?點擊這裏 –> ES6 攔截器,Proxy
那麼,現在就是嘚瑟的時間了,準備將結果發送到羣組炫耀下。先翻看下聊天記錄,呦呵,已經有人給出了一個答案,比我還快。好吧,先看看人家的答案:
//demo_02
var o = new Object();
Object.defineProperty(o,'name',{
set(val){
this.value = '456';
},
get(){
return this.value;
}
})
function foo(obj){
var obj = o;
obj.name = '123';
obj = new Object();
obj.name = '456';
}
foo(o);
console.log(o.name)
厲害的小夥子,跟我想到了一起去了,都是在getter和setter上做文章,都是在元級別上處理數據。
這位同學同時貼出一篇文章,說是從這裏獲取的靈感,狠戳這裏,一起欣賞下吧,寫得的確很不錯 –> 無懈可擊的鉤子
接下來,也貼下我的答案吧,就當時湊個熱鬧吧。等等,有人表示不服了,說他是在投機取巧。demo_02代碼完全等價與demo_03代碼:
//demo_03
var o = new Object();
function foo(obj){
var obj = o;
obj.name = '123';
obj = new Object();
obj.name = '456';
}
foo(o);
o.name = '456';
console.log(o.name);
當demo_02的方法中最後一行代碼”obj.name = ‘456’;”進行修改時,demo_02的代碼中的”this.value = ‘456’;”也得隨之修改。
說的好有道理,我們的確是在投機取巧,我寫得demo_01也是同樣的問題,還好沒點回車鍵,不然就沒臉了。
那麼有沒得什麼辦法可以解決這裏問題,實現真正的輸出456
new操作符做了什麼工作呢?
話說我們現在的問題是什麼呢?問題就是處在了new Object()上,它修改了obj的this指向,那麼我們有沒有什麼辦法規避呢?
什麼?你問我爲什麼this指向改變了,我只能說,你真的得好好學習JS了,看看這篇文章吧,作者的筆風好有趣,從一個士兵說起new都做了什麼
我記得在Proxy的handler中有個construct方法,如果我能在Object構造器中修改它的this。想法很好,但是失敗了,沒能實現。如果,有哪位能在這個思路上走下去,求告知答案 >_<
查看聊天記錄,那位同學又給出了一種解決方法:
var o = new Object();
var _Object = Object;
Object = function(){
return o;
}
function foo(obj){
var obj = o;
obj.name = '123';
obj = new Object();
obj.name = '456';
}
foo(o);
console.log(o.name)
Object = _Object;
這次這位同學先重寫了Object對象,讓它返回o這個對象,這就避免了this指向的變動,有才啊,佩服。默默收藏<_<