__proto__屬性未來會成為ES6標准的一部分,目前,該屬性在各個瀏覽器下的實現差別也許比較大.本文我們只討論它在Firefox最新版本中的表現,因為Firefox是最先實現的這個魔法屬性(magic property)的瀏覽器(同類的屬性還有__parent__和__count__等,但這兩個已經被廢棄了.),而且該屬性在Firefox中的表現也最有望能成為標准.
首先要說的是,我們通常用的__proto__屬性都是從Object.prototype上繼承下來的,該屬性是個訪問器屬性:
>uneval(Object.getOwnPropertyDescriptor(Object.prototype,"__proto__")) "({ configurable: true, enumerable: false, get: function () { [native code] }, set: function () { [native code] } })"
可以看到,該屬性是可以配置的.那就意味着我們可以刪除它.
console.log((function(){}).__proto__ === Function.prototype) //true,__proto__可以讀取到一個對象的原型 delete Object.prototype.__proto__ //true,可以刪除__proto__屬性 console.log((function(){}).__proto__) //undefined,__proto__不復存在,但仍然可以使用Object.getPrototypeOf方法獲取到一個對象的原型
我們還可以修改該屬性的表現,比如禁止它的寫操作.
console.log(Object.getPrototypeOf({__proto__:null})) //null,可以使用__proto__指定某個新建對象的原型,相當於Object.create(null)
window.__proto__ = null //重寫已有對象的屬性,這個功能無法用其他方法替代.因為我們沒有Object.setPrototypeOf方法
Object.defineProperty(Object.prototype,"__proto__",{set:function(){}}); //將__proto__屬性的set訪問器設置為一個空函數
console.log(Object.getPrototypeOf({__proto__:null})) //返回Object.prototype,不能再使用__proto__來指定原型
console.log({__proto__:null}.__proto__) //返回Object.prototype,還可以使用__proto__來讀取原型
我們甚至可利用__proto__的屬性描述符復制一個一模一樣功能的屬性.
Object.defineProperty(Object.prototype, "原型", Object.getOwnPropertyDescriptor(Object.prototype, "__proto__")); //復制一個屬性"原型"
delete Object.prototype.__proto__ //刪除__proto__屬性
console.log(Object.原型) //返回Function.prototype
其實最有用的方法就是__proto__屬性描述符上的set(),假如我們想在禁用__proto__屬性的前提下實現一個自定義的Object.setPrototypeOf方法,可以這樣來做:
(function () {
var set = Object.getOwnPropertyDescriptor(Object.prototype, "__proto__").set;
delete Object.prototype.__proto__;
Object.setPrototypeOf = function setPrototypeOf(object, prototype) {
set.call(object, prototype)
}
})();
var obj = {__proto__:"我是一個普通的屬性"}; //__proto__和__xxoo__一樣了,是個普通的屬性,不會再有可能出現的沖突
console.log(obj.__proto__); //"我是一個普通的屬性"
Object.setPrototypeOf(window,{}); //設置window對象的原型
console.log(typeof alert) //undefined,window上什么都沒了
在不刪除__proto__的前提下,我們難道就不能擁有"__proto__"這個普通的自定義屬性了嗎?可以的:
var map = {}; //一個普通對象
map["__proto__"] = "值"; //添加鍵"__proto__"
console.log(map.__proto__) //返回Object.prototype,上面的代碼沒有作用,獲取到的是對象的原型
Object.defineProperty(map,"__proto__",{value:"值"}) //定義一個自身屬性"__proto__",則不會再繼承原型鏈上的同名屬性.
console.log(map.__proto__) //"值"