使用“ Object.create”代替“ new”

Javascript 1.9.3/ECmascript 5引入了道格拉斯·克罗克福特 Object.create,它在很长一段时间内一直是 主张。如何用 Object.create代替下面代码中的 new

var UserA = function(nameParam) {
this.id = MY_GLOBAL.nextId();
this.name = nameParam;
}
UserA.prototype.sayHello = function() {
console.log('Hello '+ this.name);
}
var bob = new UserA('bob');
bob.sayHello();

(假设 MY_GLOBAL.nextId存在)。

我能想到的最好的办法就是:

var userB = {
init: function(nameParam) {
this.id = MY_GLOBAL.nextId();
this.name = nameParam;
},
sayHello: function() {
console.log('Hello '+ this.name);
}
};
var bob = Object.create(userB);
bob.init('Bob');
bob.sayHello();

似乎没有任何优势,所以我想我没有得到它。我可能太新古典主义了。如何使用 Object.create创建用户‘ bob’?

201748 次浏览

由于只有一层继承,你的例子可能不会让你看到Object.create的真正好处。

此方法允许您轻松实现微分继承,其中对象可以直接从其他对象继承。

在你的userB例子中,我认为你的init方法不应该是公共的,甚至不应该存在,如果你在一个现有的对象实例上再次调用这个方法,idname属性将会改变。

Object.create允许你使用它的第二个参数初始化对象属性,例如:

var userB = {
sayHello: function() {
console.log('Hello '+ this.name);
}
};


var bob = Object.create(userB, {
'id' : {
value: MY_GLOBAL.nextId(),
enumerable:true // writable:false, configurable(deletable):false by default
},
'name': {
value: 'Bob',
enumerable: true
}
});

正如你所看到的,属性可以在Object.create的第二个参数上初始化,使用与Object.definePropertiesObject.defineProperty方法使用的语法相似的对象文字。

它允许你设置属性属性(enumerablewritable,或configurable),这非常有用。

你必须自定义Object.create()函数。它解决了Crockfords的问题,也调用了init函数。

这是可行的:

var userBPrototype = {
init: function(nameParam) {
this.name = nameParam;
},
sayHello: function() {
console.log('Hello '+ this.name);
}
};




function UserB(name) {
function F() {};
F.prototype = userBPrototype;
var f = new F;
f.init(name);
return f;
}


var bob = UserB('bob');
bob.sayHello();

这里UserB类似于Object。创造,但要适应我们的需要。

如果你愿意,也可以拨打:

var bob = new UserB('bob');

对象。create在一些浏览器上还不是标准的,比如IE8, Opera v11.5, Konq 4.3都没有。你可以使用Douglas Crockford的Object版本。为这些浏览器创建,但这不包括在CMS的回答中使用的第二个“初始化对象”参数。

对于跨浏览器代码,同时进行对象初始化的一种方法是自定义Crockford的object .create。这里有一个方法:-

Object.build = function(o) {
var initArgs = Array.prototype.slice.call(arguments,1)
function F() {
if((typeof o.init === 'function') && initArgs.length) {
o.init.apply(this,initArgs)
}
}
F.prototype = o
return new F()
}

这维护了Crockford原型继承,也检查对象中的任何init方法,然后用参数运行它,比如说new man('John','Smith')。然后你的代码变成:-

MY_GLOBAL = {i: 1, nextId: function(){return this.i++}}  // For example


var userB = {
init: function(nameParam) {
this.id = MY_GLOBAL.nextId();
this.name = nameParam;
},
sayHello: function() {
console.log('Hello '+ this.name);
}
};
var bob = Object.build(userB, 'Bob');  // Different from your code
bob.sayHello();

所以bob继承了sayHello方法,现在拥有自己的属性id=1和name=' bob '。当然,这些属性既可写又可枚举。这也是一种比ECMA Object简单得多的初始化方法。创建,特别是当您不关心可写、可枚举和可配置属性时。

对于没有init方法的初始化,可以使用以下Crockford mod

Object.gen = function(o) {
var makeArgs = arguments
function F() {
var prop, i=1, arg, val
for(prop in o) {
if(!o.hasOwnProperty(prop)) continue
val = o[prop]
arg = makeArgs[i++]
if(typeof arg === 'undefined') break
this[prop] = arg
}
}
F.prototype = o
return new F()
}

这将使用Object填充userB自己的属性,按照它们定义的顺序。在userB参数之后从左到右生成参数。它使用for(prop in o)循环,因此,根据ECMA标准,属性枚举的顺序不能保证与属性定义的顺序相同。然而,在(4)主要浏览器上测试的几个代码示例显示,如果使用hasOwnProperty过滤器,它们是相同的,有时甚至不使用。

MY_GLOBAL = {i: 1, nextId: function(){return this.i++}};  // For example


var userB = {
name: null,
id: null,
sayHello: function() {
console.log('Hello '+ this.name);
}
}


var bob = Object.gen(userB, 'Bob', MY_GLOBAL.nextId());

我想说的是比Object简单一些。因为userB不需要init方法。此外,userB并不是特定的构造函数,但看起来像一个普通的单例对象。因此,使用这个方法,你可以从普通的普通对象构造和初始化。

你可以让init方法返回this,然后将调用链接在一起,像这样:

var userB = {
init: function(nameParam) {
this.id = MY_GLOBAL.nextId();
this.name = nameParam;
return this;
},
sayHello: function() {
console.log('Hello '+ this.name);
}
};


var bob = Object.create(userB).init('Bob');

Object的另一种可能用法。create是在廉价有效的方式中克隆不可变对象。

var anObj = {
a: "test",
b: "jest"
};


var bObj = Object.create(anObj);


bObj.b = "gone"; // replace an existing (by masking prototype)
bObj.c = "brand"; // add a new to demonstrate it is actually a new obj


// now bObj is {a: test, b: gone, c: brand}

笔记:上面的代码段创建了源对象的克隆(也不是引用,如cObj = aObj)。它优于copy-properties方法(参见1),因为它不复制对象成员属性。相反,它创建了另一个-destination-对象,并将其原型设置在源对象上。此外,当dest对象上的属性被修改时,它们是“动态”创建的,掩盖了原型(src)的属性。这构成了一种快速有效的克隆不可变对象的方法。

这里需要注意的是,这适用于创建后不应该修改的源对象(不可变)。如果源对象在创建后被修改,克隆的所有未屏蔽属性也将被修改。

在这里(http://jsfiddle.net/y5b5q/1/)(需要对象。创建功能强大的浏览器)。

使用Object.create(...)比使用new object确实没有优势。

那些主张这种方法的人通常会陈述相当模糊的优点:“可扩展性”,或“更适合JavaScript”等等。

然而,我还没有看到一个具体的例子,表明Object.create比使用new任何的优势。相反,它存在一些已知的问题。Sam Elsamman描述了当有嵌套对象并且使用Object.create(...)时会发生什么:

var Animal = {
traits: {},
}
var lion = Object.create(Animal);
lion.traits.legs = 4;
var bird = Object.create(Animal);
bird.traits.legs = 2;
alert(lion.traits.legs) // shows 2!!!

这是因为Object.create(...)提倡使用数据创建新对象的实践;在这里,Animal数据成为lionbird原型的一部分,并在共享时引起问题。当使用new时,原型继承是显式的:

function Animal() {
this.traits = {};
}


function Lion() { }
Lion.prototype = new Animal();
function Bird() { }
Bird.prototype = new Animal();


var lion = new Lion();
lion.traits.legs = 4;
var bird = new Bird();
bird.traits.legs = 2;
alert(lion.traits.legs) // now shows 4

至于传递给Object.create(...)的可选属性属性,可以使用Object.defineProperties(...)添加。

优点是在大多数浏览器上,Object.create通常比new

在这个jsperf的例子中,在chrome浏览器中,浏览器new快30倍作为Object.create(obj),尽管两者都非常快。这很奇怪,因为new要做更多的事情(比如调用构造函数),而Object。create应该只是创建一个新的对象,并将传入的对象作为原型(crockford语言中的秘密链接)

也许浏览器还没有使Object.create更有效(也许他们在背后基于new……甚至在本地代码中)

TL;博士:

new Computer()将调用构造函数Computer(){}一次,而Object.create(Computer.prototype)则不会。

所有的优势都是基于这一点。

关于性能的附加说明:像new Computer()这样的构造函数调用被引擎大量优化,所以它可能比Object.create更快。

有时不能使用NEW创建对象,但仍然可以调用create方法。

例如:如果你想定义一个自定义元素,它必须派生自HTMLElement。

proto = new HTMLElement  //fail :(
proto = Object.create( HTMLElement.prototype )  //OK :)
document.registerElement( "custom-element", { prototype: proto } )

我认为问题的重点是理解newObject.create方法之间的区别。根据这个答案这个视频new关键字做以下事情:

  1. 创建新对象。

  2. 将新对象链接到构造函数(prototype)。

  3. 使this变量指向新对象。

  4. 使用new对象执行构造函数,隐式执行return this;

  5. 将构造函数名称赋给新对象的属性constructor

Object.create只执行1st2nd步骤!!

在代码示例中提供的问题,这不是什么大问题,但在下一个例子中,它是:

var onlineUsers = [];
function SiteMember(name) {
this.name = name;
onlineUsers.push(name);
}
SiteMember.prototype.getName = function() {
return this.name;
}
function Guest(name) {
SiteMember.call(this, name);
}
Guest.prototype = new SiteMember();


var g = new Guest('James');
console.log(onlineUsers);

作为副作用的结果将是:

[ undefined, 'James' ]

因为Guest.prototype = new SiteMember(); 但是我们不需要执行父构造函数方法,我们只需要使方法getName在Guest中可用。 因此,我们必须使用Object.create
如果替换Guest.prototype = new SiteMember();
Guest.prototype = Object.create(SiteMember.prototype);的结果为:

[ 'James' ]

虽然Douglas Crockford曾经是Object.create()的狂热拥护者,而且基本上是他导致了这个构造实际上是在javascript中,但他不再有这种观点了。

他不再使用Object。创建,因为他停止使用关键字,因为它导致太多的麻烦。例如,如果不小心,它很容易指向全局对象,这可能会产生非常糟糕的后果。他声称没有使用 Object。创造已经没有意义了。

你可以看看2014年他在Nordic.js演讲的视频:

https://www.youtube.com/watch?v=PSGEjv3Tqo0

enter image description here

我更喜欢结束的方法。

我仍然使用new。 我没有使用Object.create。 我没有使用this

我仍然使用new,因为我喜欢它的声明性。

考虑简单的继承。

window.Quad = (function() {


function Quad() {


const wheels = 4;
const drivingWheels = 2;


let motorSize = 0;


function setMotorSize(_) {
motorSize = _;
}


function getMotorSize() {
return motorSize;
}


function getWheelCount() {
return wheels;
}


function getDrivingWheelCount() {
return drivingWheels;
}
return Object.freeze({
getWheelCount,
getDrivingWheelCount,
getMotorSize,
setMotorSize
});
}


return Object.freeze(Quad);
})();


window.Car4wd = (function() {


function Car4wd() {
const quad = new Quad();


const spareWheels = 1;
const extraDrivingWheels = 2;


function getSpareWheelCount() {
return spareWheels;
}


function getDrivingWheelCount() {
return quad.getDrivingWheelCount() + extraDrivingWheels;
}


return Object.freeze(Object.assign({}, quad, {
getSpareWheelCount,
getDrivingWheelCount
}));
}


return Object.freeze(Car4wd);
})();


let myQuad = new Quad();
let myCar = new Car4wd();
console.log(myQuad.getWheelCount()); // 4
console.log(myQuad.getDrivingWheelCount()); // 2
console.log(myCar.getWheelCount()); // 4
console.log(myCar.getDrivingWheelCount()); // 4 - The overridden method is called
console.log(myCar.getSpareWheelCount()); // 1

反馈鼓励。

简介:

  • Object.create()是一个Javascript函数,它接受两个参数并返回一个新对象。
  • 第一个参数是一个对象,它将是新创建对象的原型
  • 第二个参数是一个对象,它将是新创建对象的属性

例子:

const proto = {
talk : () => console.log('hi')
}


const props = {
age: {
writable: true,
configurable: true,
value: 26
}
}




let Person = Object.create(proto, props)


console.log(Person.age);
Person.talk();

实际应用:

  1. 以这种方式创建对象的主要优点是原型可以显式地定义. xml。当使用对象文字或new关键字时,你无法控制这一点(当然,你可以覆盖它们)。
  2. 如果我们想要一个原型,new关键字调用一个构造函数。Object.create()不需要调用甚至声明构造函数
  3. 当你想以非常动态的方式创建对象时,它基本上是一个有用的工具。我们可以创建一个对象工厂函数,根据接收到的参数创建具有不同原型的对象。

newObject.create有不同的用途。new用于创建对象类型的新实例。Object.create用于简单地创建一个新对象并设置其原型。为什么这个有用?实现继承而不访问__proto__属性。对象实例的原型([[Prototype]])是虚拟机的内部属性,不能被直接访问。实际上可以直接访问[[Prototype]]作为__proto__属性的唯一原因是,它一直是每个主要虚拟机实现ECMAScript的事实上的标准,在这一点上,删除它将破坏大量现有的代码。

对于7ochem上面的回答,对象绝对不应该将它们的原型设置为new语句的结果,不仅因为没有必要多次调用相同的原型构造函数,而且因为如果原型在创建后被修改,同一类的两个实例可能会以不同的行为结束。这两个例子都是由于误解和破坏了原型继承链的预期行为而导致的糟糕代码。

与访问__proto__不同,实例的原型应该在使用Object.create或之后使用Object.setPrototypeOf创建时写入,并使用Object.getPrototypeOfObject.isPrototypeOf读取。

也,Object.setPrototypeOf的Mozilla文档指出的那样,这是一个坏主意修改原型对象的创建后,由于性能的原因,除了修改一个对象的原型创建后会导致未定义行为,如果给定的代码访问它可以修改原型之前或之后执行,除非代码非常仔细检查当前的原型或不能访问任何财产,两者之间的不同。

< p >在< br >
<代码>const X =函数(v) {this。V = V}; X.prototype. whatami = 'X'; X.prototype.getWhatIAm = () =>this.whatAmI; X.prototype.getV = () =>this.v; <代码> / < br >
下面的虚拟机伪代码等价于语句const x0 = new X(1);:

const x0 = {}; x0。[[Prototype]] = X.prototype; X.prototype.constructor。代码调用(x0 1); < / > < br >
注意,尽管构造函数可以返回任何值,但new语句总是忽略它的返回值,并返回对新创建对象的引用
下面的伪代码等价于语句const x1 = Object.create(X.prototype);:

const x0 = {}; x0。[[Prototype]] = X.prototype;
.
正如你所看到的,两者之间的唯一区别是Object.create不执行构造函数,它实际上可以返回任何值,但如果没有特别指定,只返回新的对象引用this 现在,如果我们想用下面的定义创建一个子类Y
const Y =函数(u) {this。U = U;} Y.prototype. whatami = 'Y'; Y.prototype.getU = () =>this.u; <代码> / < br >
然后写入__proto__:
,使它像这样从X继承
Y.prototype.__proto__ = X.prototype; < br >
而同样的事情可以在不写入__proto__的情况下完成:

<代码> Y。prototype = Object.create(X.prototype); b .prototype.constructor = Y;


在后一种情况下,必须设置原型的构造函数属性,以便new Y语句调用正确的构造函数,否则new Y将调用函数X。如果程序员确实希望new Y调用X,则在Y的构造函数中使用X.call(this, u)

更合适

new运营商

  • 用于从构造函数创建对象
  • new关键字也执行构造函数
function Car() {
console.log(this) // this points to myCar
this.name = "Honda";
}


var myCar = new Car()
console.log(myCar) // Car {name: "Honda", constructor: Object}
console.log(myCar.name) // Honda
console.log(myCar instanceof Car) // true
console.log(myCar.constructor) // function Car() {}
console.log(myCar.constructor === Car) // true
console.log(typeof myCar) // object


Object.create

  • 你也可以使用Object.create来创建一个新对象
  • 但是,它不执行构造函数
  • Object.create用于从另一个对象创建一个对象
const Car = {
name: "Honda"
}


var myCar = Object.create(Car)
console.log(myCar) // Object {}
console.log(myCar.name) // Honda
console.log(myCar instanceof Car) // ERROR
console.log(myCar.constructor) // Anonymous function object
console.log(myCar.constructor === Car) // false
console.log(typeof myCar) // object