Object.create() - JavaScript 编辑

The Object.create() method creates a new object, using an existing object as the prototype of the newly created object.

The source for this interactive example is stored in a GitHub repository. If you'd like to contribute to the interactive examples project, please clone https://github.com/mdn/interactive-examples and send us a pull request.

Syntax

Object.create(proto, [propertiesObject])

Parameters

proto
The object which should be the prototype of the newly-created object.
propertiesObject Optional
If specified and not undefined, an object whose enumerable own properties (that is, those properties defined upon itself and not enumerable properties along its prototype chain) specify property descriptors to be added to the newly-created object, with the corresponding property names. These properties correspond to the second argument of Object.defineProperties().

Return value

A new object with the specified prototype object and properties.

Exceptions

The proto parameter has to be either

If proto is neither of these a TypeError is thrown.

Custom and Null objects

A new object created from a completely custom object (especially one created from the null object, which is basically a custom object with NO members) can behave in unexpected ways. This is especially true when debugging, since common object-property converting/detecting utility functions may generate errors, or lose information (especially if using silent error-traps that ignore errors). For example, here are two objects:

oco = Object.create( {} );   // create a normal object
ocn = Object.create( null ); // create a "null" object

> console.log(oco) // {} -- Seems normal
> console.log(ocn) // {} -- Seems normal here too, so far

oco.p = 1; // create a simple property on normal obj
ocn.p = 0; // create a simple property on "null" obj

> console.log(oco) // {p: 1} -- Still seems normal
> console.log(ocn) // {p: 0} -- Still seems normal here too. BUT WAIT...

As shown above, all seems normal so far. However, when attempting to actually use these objects, their differences quickly become apparent:

> "oco is: " + oco // shows "oco is: [object Object]"

> "ocn is: " + ocn // throws error: Cannot convert object to primitive value

Testing just a few of the many most basic built-in functions shows the magnitude of the problem more clearly:

> alert(oco) // shows [object Object]
> alert(ocn) // throws error: Cannot convert object to primitive value

> oco.toString() // shows [object Object]
> ocn.toString() // throws error: ocn.toString is not a function

> oco.valueOf() // shows {}
> ocn.valueOf() // throws error: ocn.valueOf is not a function

> oco.hasOwnProperty("p") // shows "true"
> ocn.hasOwnProperty("p") // throws error: ocn.hasOwnProperty is not a function

> oco.constructor // shows "Object() { [native code] }"
> ocn.constructor // shows "undefined"

As said, these differences can make debugging even simple-seeming problems quickly go astray. For example:

A simple common debugging function:

// display top-level property name:value pairs of given object
function ShowProperties(obj){
  for(var prop in obj){
    console.log(prop + ": " + obj[prop] + "\n" );
  }
}

Not such simple results: (especially if silent error-trapping had hidden the error messages)

ob={}; ob.po=oco; ob.pn=ocn; // create a compound object using the test objects from above as property values

> ShowProperties( ob ) // display top-level properties
- po: [object Object]
- Error: Cannot convert object to primitive value

Note that only first property gets shown.

(But if the same object is created in a different order -- at least in some implementations...)

ob={}; ob.pn=ocn; ob.po=oco; // create same compound object again, but create same properties in different order

> ShowProperties( ob ) // display top-level properties
- Error: Cannot convert object to primitive value

Note that neither property gets shown.

Note that such a different order may arise statically via disparate fixed codings such as here, but also dynamically via whatever the order any such property-adding code-branches actually get executed at runtime as depends on inputs and/or random-variables. Then again, the actual iteration order is not guaranteed no matter what the order members are added.

Be aware of, also, that using Object.entries() on an object created via Object.create() will result in an empty array being returned.

var obj = Object.create({ a: 1, b: 2 });

> console.log(Object.entries(obj)); // shows "[]"

Some NON-solutions

A good solution for the missing object-methods is not immediately apparent.

Adding the missing object-method directly from the standard-object does NOT work:

ocn = Object.create( null ); // create "null" object (same as before)

ocn.toString = Object.toString; // since new object lacks method then try assigning it directly from standard-object

> ocn.toString // shows "toString() { [native code] }" -- missing method seems to be there now
> ocn.toString == Object.toString // shows "true" -- method seems to be same as the standard object-method

> ocn.toString() // error: Function.prototype.toString requires that 'this' be a Function


Adding the missing object-method directly to new object's "prototype" does not work either, since the new object does not have a real prototype (which is really the cause of ALL these problems) and one cannot be directly added:

ocn = Object.create( null ); // create "null" object (same as before)

ocn.prototype.toString = Object.toString; // Error: Cannot set property 'toString' of undefined

ocn.prototype = {};                       // try to create a prototype
ocn.prototype.toString = Object.toString; // since new object lacks method then try assigning it from standard-object  

> ocn.toString() // error: ocn.toString is not a function


Adding the missing object-method by using the standard-object as new object's prototype does not work either:

ocn = Object.create( null );        // create "null" object (same as before)
Object.setPrototypeOf(ocn, Object); // set new object's prototype to the standard-object

> ocn.toString() // error: Function.prototype.toString requires that 'this' be a Function

Some OK solutions

Again, adding the missing object-method directly from the standard-object does NOT work. However, adding the generic method directly, DOES:

ocn = Object.create( null ); // create "null" object (same as before)

ocn.toString = toString; // since new object lacks method then assign it directly from generic version

> ocn.toString() // shows "[object Object]"
> "ocn is: " + ocn // shows "ocn is: [object Object]"

ob={}; ob.pn=ocn; ob.po=oco; // create a compound object (same as before)

> ShowProperties(ob) // display top-level properties
- po: [object Object]
- pn: [object Object]

However, setting the generic prototype as the new object's prototype works even better:

ocn = Object.create( null );                  // create "null" object (same as before)
Object.setPrototypeOf(ocn, Object.prototype); // set new object's prototype to the "generic" object (NOT standard-object)

(In addition to all the string-related functions shown above, this also adds:)

> ocn.valueOf() // shows {}
> ocn.hasOwnProperty("x") // shows "false"
> ocn.constructor // shows "Object() { [native code] }"

// ...and all the rest of the properties and methods of Object.prototype.

As shown, objects modified this way now look very much like ordinary objects.

Polyfill

This polyfill covers the main use case, which is creating a new object for which the prototype has been chosen but doesn't take the second argument into account.

Note that while the setting of null as [[Prototype]] is supported in the real ES5 Object.create, this polyfill cannot support it due to a limitation inherent in versions of ECMAScript lower than 5.

 if (typeof Object.create !== "function") {
    Object.create = function (proto, propertiesObject) {
        if (typeof proto !== 'object' && typeof proto !== 'function') {
            throw new TypeError('Object prototype may only be an Object: ' + proto);
        } else if (proto === null) {
            throw new Error("This browser's implementation of Object.create is a shim and doesn't support 'null' as the first argument.");
        }

        if (typeof propertiesObject != 'undefined') {
            throw new Error("This browser's implementation of Object.create is a shim and doesn't support a second argument.");
        }

        function F() {}
        F.prototype = proto;

        return new F();
    };
}

Examples

Classical inheritance with Object.create()

Below is an example of how to use Object.create() to achieve classical inheritance. This is for a single inheritance, which is all that JavaScript supports.

// Shape - superclass
function Shape() {
  this.x = 0;
  this.y = 0;
}

// superclass method
Shape.prototype.move = function(x, y) {
  this.x += x;
  this.y += y;
  console.info('Shape moved.');
};

// Rectangle - subclass
function Rectangle() {
  Shape.call(this); // call super constructor.
}

// subclass extends superclass
Rectangle.prototype = Object.create(Shape.prototype);

//If you don't set Rectangle.prototype.constructor to Rectangle,
//it will take the prototype.constructor of Shape (parent).
//To avoid that, we set the prototype.constructor to Rectangle (child).
Rectangle.prototype.constructor = Rectangle;

var rect = new Rectangle();

console.log('Is rect an instance of Rectangle?', rect instanceof Rectangle); // true
console.log('Is rect an instance of Shape?', rect instanceof Shape); // true
rect.move(1, 1); // Outputs, 'Shape moved.'

If you wish to inherit from multiple objects, then mixins are a possibility.

function MyClass() {
  SuperClass.call(this);
  OtherSuperClass.call(this);
}

// inherit one class
MyClass.prototype = Object.create(SuperClass.prototype);
// mixin another
Object.assign(MyClass.prototype, OtherSuperClass.prototype);
// re-assign constructor
MyClass.prototype.constructor = MyClass;

MyClass.prototype.myMethod = function() {
  // do something
};

Object.assign() copies properties from the OtherSuperClass prototype to the MyClass prototype, making them available to all instances of MyClass. Object.assign() was introduced with ES2015 and can be polyfilled. If support for older browsers is necessary, jQuery.extend() or _.assign() can be used.

Using propertiesObject argument with Object.create()

var o;

// create an object with null as prototype
o = Object.create(null);

o = {};
// is equivalent to:
o = Object.create(Object.prototype);

// Example where we create an object with a couple of
// sample properties. (Note that the second parameter
// maps keys to *property descriptors*.)
o = Object.create(Object.prototype, {
  // foo is a regular 'value property'
  foo: {
    writable: true,
    configurable: true,
    value: 'hello'
  },
  // bar is a getter-and-setter (accessor) property
  bar: {
    configurable: false,
    get: function() { return 10; },
    set: function(value) {
      console.log('Setting `o.bar` to', value);
    }
/* with ES2015 Accessors our code can look like this
    get() { return 10; },
    set(value) {
      console.log('Setting `o.bar` to', value);
    } */
  }
});

function Constructor() {}
o = new Constructor();
// is equivalent to:
o = Object.create(Constructor.prototype);
// Of course, if there is actual initialization code
// in the Constructor function,
// the Object.create() cannot reflect it

// Create a new object whose prototype is a new, empty
// object and add a single property 'p', with value 42.
o = Object.create({}, { p: { value: 42 } });

// by default properties ARE NOT writable,
// enumerable or configurable:
o.p = 24;
o.p;
// 42

o.q = 12;
for (var prop in o) {
  console.log(prop);
}
// 'q'

delete o.p;
// false

// to specify an ES3 property
o2 = Object.create({}, {
  p: {
    value: 42,
    writable: true,
    enumerable: true,
    configurable: true
  }
});
/* is not equivalent to:
This will create an object with prototype : {p: 42 }
o2 = Object.create({p: 42}) */

Specifications

Specification
ECMAScript (ECMA-262)
The definition of 'Object.create' in that specification.

Browser compatibility

BCD tables only load in the browser

See also

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

词条统计

浏览:85 次

字数:18247

最后编辑:7 年前

编辑次数:0 次

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文