天天看点

js高级---Object.create()方法实现对象继承与创建新的JavaScript对象

Object.create()

方法是 ECMAScript 5中新增的方法,这个方法用于创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。被创建的对象会继承另一个对象的原型,在创建新对象时还可以指定一些属性。

1.

Object.create()

语法结构

Object.create(proto [, propertiesObject])
           
  • proto

    ,{object},新对象要继承的对象/要继承对象的原型
  • propertiesObject

    ,{object},可选参数,为新创建对象指定的属性对象。该参数描述符对象(Object.defineProperties())是一组属性和值,对象的属性名将会是新创建的对象的属性名,属性值是属性描述符。该对象可能会包含以下值:
    • configurable

      ,表示新创建的对象是否是可配置的,即:对象的属性是否可以被删除或修改,默认

      false

    • enumerable

      ,对象属性是否可枚举的,即:是否可以for of枚举,默认

      false

    • enumerable

      ,对象的值,可以是任何合法的JavaScript的值,如:number、object、function等,默认

      undefined

    • writable

      ,对象是否可写,是否或以为对象添加新属性,默认

      false

    • get

      ,对象

      getter

      函数,默认

      undefined

    • set

      ,对象

      setter

      函数,默认

      undefined

使用

Object.create()

方法创建新对象时,如果不是继承一个原有的对象,而是要创建一个全新的对象,就要把

proto

参数设置为

null

。如果

proto

参数不是

null

或一个对象时,会抛出一个

TypeError

异常。

2.

Object.create()

的使用

使用

Object.create()

方法使用类式继承(4步曲):

// 第一步:父类
function Site() {
  this.name = 'Site';
  this.domain = 'domain';
}

Site.prototype.create = function(name, domain) {
  this.name = name;
  this.domain = domain;
};

// 第二步:子类
function Itbilu() {
  Site.call(this); //调用基类的构造函数
}

// 第三步:继承父类
Itbilu.prototype = Object.create(Site.prototype);

// 创建类实例
var itbilu = new Itbilu();

itbilu instanceof Site;  // true
tbilu instanceof Itbilu;  // true

itbilu.create('IT笔录', 'itbilu.com');
itbilu.name;    // 'IT笔录'
itbilu.domain;  // 'itbilu.com'
           

上面实现继承还存在一个小小的问题:

//上面的代码子类的构造器指向了父类,并没有指向自己
console.log(Itbilu.prototype.constructor);//Site {}
           

解决方案:

//第四步:改变构造器
Itbilu.prototype.constructor = Itbilu
           

这个时候在打印子类的构造器就是自己。

在不指定

propertiesObject

设置对象属性时,使用

Object.create()

方法实现继承与JavaScript原型继承并没有什么区别。因此,上面的示例的后半部分也可以写成下面这样:

// 继承父类
Itbilu.prototype = Site.prototype;

// 创建类实例
var itbilu = new Itbilu();

itbilu instanceof Site;  // true
tbilu instanceof Itbilu;  // true

itbilu.create('IT笔录', 'itbilu.com');
itbilu.name;    // 'IT笔录'
itbilu.domain;  // 'itbilu.com'
           

3.Object.create()、{…}/new Object()的区别

先看看我们经常使用的

{}

创建的对象是什么样子的:

var o = {a:1};
console.log(o)
           
js高级---Object.create()方法实现对象继承与创建新的JavaScript对象

从上图可以看到,新创建的对象继承了

Object

自身的方法,如

hasOwnProperty

toString

等,在新对象上可以直接使用。

再看看使用

Object.create()

创建对象:

var o = Object.create(null,{
    a:{
           writable:true,
        configurable:true,
        value:'1'
    }
})
console.log(o)
           

在chrome控制台打印如下:

js高级---Object.create()方法实现对象继承与创建新的JavaScript对象

可以看到,新创建的对象除了自身属性a之外,原型链上没有任何属性,也就是没有继承Object的任何东西,此时如果我们调用

o.toString()

会报

Uncaught TypeError

的错误。

大家可能会注意到,第一个参数使用了null。也就是说将null设置成了新创建对象的原型,自然就不会有原型链上的属性。我们再把上面的例子改一改:

var o = Object.create({},{
    a:{
        writable:true,
        configurable:true,
        value:'1'
    }
})
console.log(o);
           

null

改为

{}

,结果是怎样的?在chrome控制台打印如下:

js高级---Object.create()方法实现对象继承与创建新的JavaScript对象

我们看到,这样创建的对象和使用

{}

创建对象已经很相近了,但是还是有一点区别:多了一层

__proto

__嵌套。

我们最后再来改一下:

var o = Object.create(Object.prototype,{
    a:{
        writable:true,
        configurable:true,
        value:'1'
    }
})
console.log(o);
           

chrome控制台打印如下:

js高级---Object.create()方法实现对象继承与创建新的JavaScript对象

这次就和使用

{}

创建的对象一模一样了。至此,我相信大家已经对两者的区别十分清楚了。

Object.create(null)的使用场景

再回到文章开头的问题,为什么很多源码作者会使用

Object.create(null)

来初始化一个新对象呢?这是作者的习惯,还是一个最佳实践?

其实都不是,这并不是作者不经思考随便用的,也不是javascript编程中的最佳实践,而是需要因地制宜,具体问题具体分析。

我们进一步比较一下

Object.create(null)

{}

创建控对象的区别:

在chrome打印如下:

js高级---Object.create()方法实现对象继承与创建新的JavaScript对象

从上图可以看到,使用

create

创建的对象,没有任何属性,显示

No properties

,我们可以把它当作一个非常纯净的map来使用,我们可以自己定义

hasOwnProperty

toString

方法,不管是有意还是不小心,我们完全不必担心会将原型链上的同名方法覆盖掉。举个例子:

//Demo1:
var a= {...省略很多属性和方法...};
//如果想要检查a是否存在一个名为toString的属性,你必须像下面这样进行检查:
if(Object.prototype.hasOwnProperty.call(a,'toString')){
    ...
}
//为什么不能直接用a.hasOwnProperty('toString')?因为你可能给a添加了一个自定义的hasOwnProperty
//你无法使用下面这种方式来进行判断,因为原型上的toString方法是存在的:
if(a.toString){}

//Demo2:
var a=Object.create(null)
//你可以直接使用下面这种方式判断,因为存在的属性,都将定义在a上面,除非手动指定原型:
if(a.toString){}
           

另一个使用

create(null)

的理由是,在我们使用

for..in

循环的时候会遍历对象原型链上的属性,使用

create(null)

就不必再对属性进行检查了,当然,我们也可以直接使用

Object.keys()只能得到自身的属性

总结:

  1. 你需要一个非常干净且高度可定制的对象当作数据字典的时候;
  2. 想节省

    hasOwnProperty

    带来的一丢丢性能损失并且可以偷懒少些一点代码的时候

Object.create(null)

吧!其他时候,请用

{}