zoukankan      html  css  js  c++  java
  • [译]JavaScript: __proto__

    原文:http://www.2ality.com/2012/10/proto.html


    本文讲一下特殊属性__proto__,通过该属性可以获取或设置一个对象的原型.想要理解这篇文章,你必须已经熟悉了JavaScript的原型继承 [1].

    1.特殊属性__proto__

    ECMAScript标准中规定,一个对象的内部属性[[Prototype]]指向自己的原型.在ECMAScript 5中,该属性是不能被直接读取或修改的,但是可以通过Object.getPrototypeOf()间接的读取到它.还可以使用Object.create()创建一个拥有指定原型的新对象.例如,下面的代码创建了一个对象obj,它的原型是myProto
    > var myProto = {};
    > var obj = Object.create(myProto);
    
    > Object.getPrototypeOf(obj) === myProto
    true

    __proto__ (发音为“dunder proto”,dunder是“double underscore”的简拼)最初出现在Firefox中,作为内部属性[[Prototype]]的别名.使用__proto__的话,上面的代码可以改写成:

    > var myProto = {};
    > var obj = { __proto__: myProto };
    
    > obj.__proto__ === myProto
    true
    __proto__属性的值就是它的原型:
    > obj.__proto__ === Object.getPrototypeOf(obj)
    true
    自从__proto__出现在Firefox中以后,它就变的越来越流行,现在V8 (Chrome, Node.js)和Nitro (Safari)也已经支持了它.但ECMAScript 5并没有标准化__proto__,不过由于它现在的流行程度,它将会成为ECMAScript 6规范的一部分.

    1.1 检测是否支持__proto__

    如果JavaScript引擎实现了 __proto__ ,那么下面的表达式会返回true:
    Object.getPrototypeOf({ __proto__: null }) === null

    1.2 对象作为Map使用

    如果你需要把一个对象作为一个字符串到值的Map来使用的话(真正的Map可以有任意类型的键)[2],那么你必须做个判断,如果传入的键名是"__proto__",则将它转义为一个不和已有的属性'__proto__'冲突的键名[2].例如:
    function escapeKey(key) {
        // 我们需要转义"__proto__"成为"__proto__%",还得转义"__proto__%"成为"__proto__%%",依次类推.
    if (key.indexOf("__proto__") === 0) { return key+"%"; } else { return key; } }

    2.两个用处

    __proto__两个最大的用处是:创建一个以指定对象为原型的对象,以及,为内置类型添加子类型.

    2.1 创建一个以指定对象为原型的对象

    如果你要创建一个非空的对象,则使用__proto__的优点会比较明显:
    var obj = {
        __proto__: myProto,
        foo: 123,
        bar: "abc"
    };
    使用Object.create()来替代的话,你有两种写法,但都不是很简洁:
    // 替代方法1:创建一个空对象,然后赋值
    var obj = Object.create(myProto);
    obj.foo = 123;
    obj.bar = "abc";
    
    // 替代方法2:属性描述符
    var obj = Object.create(myProto, {
        foo: {
            value: 123,
            writable: true,
            enumerable: true,
            configurable: true
        },
        bar: {
            value: "abc",
            writable: true,
            enumerable: true,
            configurable: true
        }
    });

    2.2 为内置类型添加子类型

    总所周知,在JavaScript中很难为内置类型添加子类型[3],这有两个原因:第一个是,JavaScript中,标准的子类型化模式是,在子类的构造函数中,将子类型的实例this通过call方法传入到超类型的构造函数中,这样子类型的实例就能拥有超类型的属性.但是,大部分内置类型的构造函数都不会允许你那样做,它们会忽略掉传入的this.第二个原因是,一些内置类型的对象实例是很特殊的,即使你能成功的把子类型的对象实例传进超类型的构造函数中去,你也得不到按预期工作的对象实例.最常见的例子就是数组,如果数组中新添加了一个元素,它的length属性会自动更新,这个特性子类型无法继承下来.
    译者注:为自定义类型添加子类型的通用方法是:
    function Super(x, y) {
    this.x = x;
    this.y = y;
    }
    function Sub(x, y, z) {
    Super.call(this, x, y); //为子类型的对象实例添加超类型的属性,子类型的对象实例作为超类型的构造函数中的this值 this.z = z; //添加子类型的扩展属性
    }

    但如果是内置类型的话,就不行了.

    借助__proto__的话,可以这样实现数组的子类型:

    var MyArrayProto = Object.create(Array.prototype);
    //还可以写成var MyArrayProto = {__proto__:Array.prototype};
    MyArrayProto.foo = function (...) { ... };
    function createMyArray() {
        var arr = Array.prototype.slice.call(arguments);
        arr.__proto__ = MyArrayProto;
        return arr;   
    }
    var myarr = createMyArray(1,2,3);    //myarr会有foo方法,也会有其他的数组方法

    3.ECMAScript 6

    正如前面所提到的,ECMAScript 6将会添加对__proto__的支持.目前可以确定的功能有,你可以用它来获取到一个对象的原型,还可以用在对象字面量中设置这个对象的原型.对象字面量配合__proto__可以视为是一种更友好的Object.create()的替代者,对于大多数情况来说,足够好用了.

    目前还不确定的功能是,能不能通过__proto__修改一个已存在对象的原型.目前最主要需要用到这个特性的地方就是给数组新增子类型,不过未来也许会有更好的方法来实现这个需求,比如通过函数Array.createArray(proto).

    ECMAScript 6还可能会提供一种手段来关闭一些对象上的__proto__属性的访问,甚至可能是全部对象.

    4.更多资料

    5.参考

    1. Prototypes as classes – an introduction to JavaScript inheritance
    2. The pitfalls of using objects as maps in JavaScript
    3. Subtyping JavaScript built-ins
  • 相关阅读:
    一行Haskell语句打印勾股数
    给孩子增加学习生物的兴趣,买了个显微镜
    实现求n个随机数和为sum的haskell程序
    用haskell实现的八皇后程序
    桥牌笔记:第一墩决定成败
    读书笔记:父母离去前要做的55件事
    LINQ to SQL系列三 使用DeferredLoadingEnabled,DataLoadOption指定加载选项
    LINQ to SQL系列四 使用inner join,outer join
    Asp.Net 4.0 新特性 系列 之一 从页面标记<%%>说起
    使用javascript自动标记来自搜索结果页的关键字
  • 原文地址:https://www.cnblogs.com/ziyunfei/p/2710955.html
Copyright © 2011-2022 走看看