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__)
//"值"
posted @ 2012-10-22 15:07  紫云飞  阅读(4026)  评论(1编辑  收藏  举报