看透JavaScript:原理、方法与实践
上QQ阅读APP看书,第一时间看更新

5.1 创建object类型对象的三种方式

ES中object类型的对象大致有三种创建方式:直接使用花括号创建、使用function创建以及使用Object.create方法创建。

5.1.1 直接使用花括号创建

使用花括号创建对象时直接将属性写到花括号中就可以了,属性名和属性值使用冒号(:)分割,不同属性之间使用逗号(, )分割(注意,最后一个属性后面没有逗号),属性的值可以是直接量,也可以是object类型对象或者function类型对象。我们来看下面的例子。

    var obj = {
        v:6,                          //直接量属性
        innerObj:{                    //object类型对象属性
            v:7
        },
        logV: function () {          //function类型对象属性
            console.log(this.v);
        }
    }


    console.log(obj.v);              //6
    console.log(obj.innerObj.v);     //7
    obj.logV ();                    //6

这个例子中定义了object类型的对象obj,它包括直接量v、object类型对象innerObj以及function类型对象logV三种属性,其中innerObj对象也是通过花括号来定义的。使用花括号定义完对象之后又分别使用obj对象调用了它的三个属性。

object对象调用属性有两种方法:直接使用点操作符调用;使用方括号调用。因为使用方括号没有使用点操作符方便,所以一般使用点操作符调用的比较多,不过当属性名为一个变量时则只能使用方括号来调用,例如下面的例子。

    var obj = {a:1, b:2, c:3};
    var propName = "c";
    console.log(obj[propName]);     //3

这个例子中,属性名保存在propName变量中,这时就只能使用方括号来调用了。

5.1.2 使用function创建

关于使用function创建object实例对象,我们在4.4节中已经讲过了,是使用new关键字来创建的,而且创建出来的对象还可以使用function的prototype属性对象中的属性。具体的内容这里就不再重述了。

5.1.3 使用Object.create方法创建

还有一种创建object类型对象的方式,那就是使用Object.create来创建。Object是ES中内置的一个function类型的对象,create是Object对象的一个属性方法,其作用是根据传入的参数创建object类型的对象。create方法的调用语法如下。

    Object.create(prototype, [ propertiesObject ])

其中,第一个参数prototype是创建的对象所对应的prototype,相当于使用function创建时function中的prototype属性对象,创建出来的object对象实例可以直接调用。

第二个参数propertiesObject为属性描述对象,是可选参数,用于描述所创建对象的自身属性。属性描述对象是object类型对象,它里面的属性名会成为所创建对象的属性名,属性值为属性的描述对象,其中包含属性的特性(属性的特性我们后面会详细讲解,这里大家只要知道其中的value就是属性值就可以了)。我们来看下面的例子。

    var obj = Object.create(
            //prototype
              {
                  type: "by create"
              },
            //propertiesObject
              {
                  color: {
                      value: "red",
                      enumerable: true
                  },
                  size: {
                      value: "37",
                      enumerable: true
                  }
              }
      );


    console.log(obj.type);                           //by create
    console.log(obj.color);                          //red
    console.log(obj.size);                           //37
    console.log(Object.getOwnPropertyNames(obj));    //["color", "size"]

这个例子中,使用Object.create创建了一个obj对象,第一个参数prototype中有一个属性type,第二个参数propertiesObject有两个属性:color和size。对于创建的obj对象来说,这三个属性都可以使用,但其自身其实只有两个属性。虽然obj可以调用prototype中的type属性,但是并不属于obj,使用Object.getOwnPropertyNames方法就可以获取obj自己所拥有的属性。

另外,需要注意的一点是,使用花括号和function创建的对象都可以调用Object的prototype属性对象中的属性。即这两种方法创建出来的对象首先会拥有公共的prototype,然后使用function创建的对象还可以调用function的prototype中的属性,而且即使function的prototype为null,创建的对象也可以调用Object的prototype中的属性。但是,使用Object. create创建对象时,如果第一个参数为null,那么它所创建的对象将不可以调用Object的prototype中的属性。我们来看下面的例子。

    //使用花括号创建对象braceObj
    var braceObj = {}


    //使用function创建对象functionObj
    function F(){}
    F.prototype = null;
    var functionObj = new F();


    //使用create方法创建对象createObj
    var createObj = Object.create(null);


    console.log(braceObj.toString());         //[object Object]
    console.log(functionObj.toString());     //[object Object]
    console.log(createObj.toString());       //抛出方法没找到异常

在这个例子中,toString方法是Object的prototype属性中的一个方法,如果创建出来的对象可以调用toString方法,就说明可以调用Object的prototype属性对象中的方法,否则就是不可以调用。从这个例子中可以看出,使用花括号和function创建的对象都可以调用tostring方法,而且即使将function的prototype设置为null,创建出来的对象也可以调用,但使用Object.create创建出来的对象,如果其prototype参数为null就不可以调用了。

多知道点

Object的prototype属性对象里面都有什么

Object的prototype属性对象在ES5.1和ES2015中都规定了constructor、toString ( )、toLocaleString ( )、valueOf ( )、hasOwnProperty (V) 、isPrototypeOf (V)和propertyIsEnumerable (V)7个属性,其中,constructor会默认指向创建对象的function,其他6个是方法,分别介绍如下。

▯ toString:这是6个方法中最常使用的方法。它可以将对象转换为字符串,不同类型的对象可能会重写自己的toString方法。例如,Array的toString方法会将其所包含的元素使用逗号连接起来组成字符串并返回、Date的toString方法会返回Date的时间字符串等,普通object类型对象会返回[object Object]。

▯ toLocaleString:会使用本地化格式来生成字符串,对于时间日期类型和数字类型的用处较大。

▯ valueOf:会返回原始值。例如,因为Date类型对象是通过数字来保存的,所以当Date类型对象调用valueOf时就会获得相应的数字。

▯ hasOwnProperty:判断是否包含指定属性。注意,这里判断的是对象自身是否包含指定的属性,不包括创建对象的function对象的prototype中的属性。

▯ isPrototypeOf:判断某个对象是否是另一个对象所对应的prototype对象。

▯ propertyIsEnumerable:判断某个属性是否可以枚举。关于属性的枚举我们到属性的特性一节再详细介绍。

例如下面的例子。

    function log(msg){
          console.log(msg);
      }


    var array = [1,3,5];
    var date = new Date();
    log(array.toString());         //1,3,5
    log(date.toString());         //Tue Jun 092015 10:48:10 GMT+0800
    log(date.toLocaleString());  //2015/6/9 上午10:48:10
    log(date.valueOf());        //1433818090599  从1970年到现在的毫秒数


    var Obj = function(){
        this.msg = "hello";
        this.say = function () {
            log(msg);
        }
    }
    var proto = {color: "red"};
    Obj.prototype = proto;
    var obj = new Obj();


    log(obj.constructor);                       //Object()
    log(obj.hasOwnProperty("msg"));             //true
    log(obj.hasOwnProperty("color"));           //false, color在Obj的prototype中,
                                                //obj虽然可以调用但是不拥有
    log(obj.propertyIsEnumerable("msg"));       //true
    log(obj.isPrototypeOf(proto));              //false
    log(proto.isPrototypeOf(obj));              //true

通过这个例子,就可以理解Object的prototype属性对象中7个属性的用法了。需要注意的是,因为将Obj的prototype属性赋值为proto对象,所以创建的obj对象在调用constructor属性时就不会返回Obj对象而是返回Object对象。可以通过对proto对象进行修改来修复这一问题,代码如下。

    var proto = {color: "red"};
    proto.constructor = Obj;
    Obj.prototype = proto;
    var obj = new Obj();
    console.log(obj.constructor);                     //Obj

这时就可以正确地输出constructor了。

上面所介绍的是标准中所规定的Object的prototype属性对象中的7个属性,但是不同的浏览器还会有一些自己的扩展。例如,在Firefox中就扩展到14个属性:constructor、toSource、toString、toLocaleString、valueOf、watch、unwatch、hasOwnProperty、isPrototypeOf、propertyIsEnumerable、_ _defineGetter_ _、__defineSetter_ _、_ _lookupGetter_ _和_ _lookupSetter_ _。但是,新增的属性并不是通用属性,其他浏览器中可能并没有定义,如果使用则很可能造成浏览器不兼容的问题,因此应该尽量少使用。如果非要用,那么最好在使用前先判断浏览器是否支持。