JavaScript:繼續理解__proto__


__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__)
//"值"


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM