我希望能够学习按照新的JavaScript ECMA 5标准创建对象,并在我当前的项目中使用它们,而不会破坏功能。但我看到让我害怕的不可解释的东西
请考虑以下代码:
var foo = {
oldProp: 'ECMA Script 3',
newProp: 'ECMA Script 5'
};
// The new way of inheritance
var bar = Object.create( foo );
// and of assigning properties, if I am right
var bar2 = Object.create( Object.prototype, {
oldProp: { value: 'ECMA Script 3'},
newProp: { value: 'ECMA Script 5'}
});
// and then
bar;
// console output
Object {oldProp: "ECMA Script 3", newProp: "ECMA Script 5"}
// whereas !!
bar2;
Object {} // Why this?
另外,
bar.hasOwnProperty('oldProp'); // false, whereas
bar2.hasOwnProperty('oldProp'); // true !! It should be the other way round, right?
此外,我无法更改bar2
bar2.oldProp = "Marry had a little lamb";
bar2.oldProp; // "ECMA Script 3"
还有更多 -
for (k in bar2) console.log(bar2[k]) // undefined
我是否以正确的方式创建对象? 我基本上想要实现的是摆脱构造函数。
旧代码:
var Slideshow = function(type) {
this.type = type;
this.deleteSlideshow = function() {
// some code
}
}
Slideshow.prototype.nextSlide = function() {
// lotsa code
}
var vanityFairSlideshow = new Slideshow('wide-screen');
我想更改为Object.create
,可能是这样的:
var Slideshow = Object.create({ /* prototype */ }, { /* properties and methods */ });
var vanityFairSlideshow = Object.create( Slideshow , { /* properties and methods */ });
这样做的正确方法是什么?
答案 0 :(得分:2)
我会尝试回答这个问题:
我是否以正确的方式创建了对象?我基本上想要实现的是摆脱构造函数。
第一个小例子。想象一下,我们有这个简单的结构:
var Slideshow = function(type) {
this.type = type;
}
Slideshow.prototype.nextSlide = function() {
console.log('next slide');
}
Slideshow.prototype.deleteSlideshow = function() {
console.log('delete slideshow');
}
var AdvancedSlideshow = function(bgColor) {
this.bgColor = bgColor;
}
AdvancedSlideshow.prototype.showMeBgColor = function() {
console.log('bgColor iS: ' + bgColor);
}
现在我们希望AdvancedSlideshow
继承Slideshow
。因此,我们希望父级的所有函数都可用于其子级的实例。没有Object.create
,我们就这样做了:
// THIS CODE ISNT CORRECT
// code for slideshow is same
var AdvancedSlideshow = function(type, bgColor) {
// this line can supply whole Slideshow constructor
Slideshow.call( this, type );
this.bgColor = bgColor;
}
AdvancedSlideshow.prototype = Slideshow.prototype; // there problems start!!!!
AdvancedSlideshow.prototype.showMeBgColor = function() { // we did augumentation of Slideshow
console.log('bgColor is: ' + bgColor);
}
现在AdvancedSlideshow
继承了Slideshow
的所有内容,但 Slideshow
继承了AdvancedSlideshow
的所有内容。这就是我们不想要的。
var simpleSlideshow = new Slideshow('simple');
simpleSlideshow.showMeBgColor(); // ReferenceError: bgColor is not defined
// but function exists and thats wrong!
所以我们必须使用更复杂的东西。很久以前,Douglas Crockford制造了一种填充物。
if (!Object.create) {
Object.create = function (o) {
if (arguments.length > 1) {
throw new Error('Object.create implementation only accepts the first parameter.');
}
function F() {}
F.prototype = o;
return new F();
};
}
首先,它不是polyfill,它是小图案,看起来不像。但模式正在发展,然后它变得非常好,它成为了javascript的一部分。所以现在我们只为浏览器提供了一个不支持ecmascript-5的polyfill。我们可以编码:
// THIS IS CORRECT
AdvancedSlideshow.prototype = Object.create(Slideshow.prototype);
而不是
function F() {};
F.prototype = Slideshow.prototype;
AdvancedSlideshow.prototype = new F();
然后AdvancedSlideshow
继承自Slideshow
,但 Slideshow
不会继承AdvancedSlideshow
。
所以Object.create
的目的不是要摆脱构造者或者#34;新的"关键字。它的目的是制作正确的原型链!
编辑20.2.2014:
几天前我重新确定,constructor
属性是原型链的一部分。因此,如果我们使用Object.create
进行继承,我们还会更改子对象的constructor
属性(不是构造函数本身,只是属性)。因此,子对象的新实例的构造函数属性指向其父对象。我们可以使用简单的Child.prototype.constructor = Child
修复此问题。
...
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
var first = new Child();
first.constructor === Child; // true
first.constructor === Parent; // false
first instanceOf Child; // true
first instanceOf Parent; // true
现在,Object.create有第二个参数,它允许你让我们说出隐藏的秘密"对象但是我不推荐你这样做,因为你不需要它。它是先进的,也像某些模式一样工作。
如果你想避免构造函数," new"关键字或任何重要的东西,那么你可能正在寻找一些工厂模式。但请记住,例如" new"有一些弱点,但删除内置功能的模式通常更糟糕!无论如何,也许你可以在这里找到一些灵感:
http://www.2ality.com/2010/12/javascripts-prototypal-inheritance.html
答案 1 :(得分:1)
此处的问题是您为每个媒体资源省略的descriptors(value
除外)的默认设置。
默认使用此语法(例如defineProperty
和defineProperties
),writable
为false
,enumerable
为false
。
将这两者设置为true
将导致您期望的行为。
var bar2 = Object.create( Object.prototype, {
oldProp: { value: 'ECMA Script 3', writable: true, enumerable: true}
});
此外,希望您了解所有浏览器都不支持此功能;请注意compatibility table for Object.create
。符合writable
和configurable
属性的垫片不存在,也不会存在。
最后,+1这样一个写得很好的问题;这是一股清新的空气。
答案 2 :(得分:1)
bar
时没有任何属性,foo
作为原型,因此bar.hasOwnProperty('oldProp');
返回false。
对于bar2
,您不提供描述符,enumerable
和可写的默认值为false
。因此,您无法在for...in
循环中更改属性或枚举它们。
至于我是否以正确的方式创建了对象?:你在做什么是合理的。您可以创建Slideshow
作为所有幻灯片的原型,并使用此原型和其他属性创建幻灯片。
答案 3 :(得分:0)
bar2 // logs Object {}
我无法更改bar2的属性
for (k in bar2) console.log(bar2[k]) // undefined
这是因为Object.create
的第二个参数类似于Object.defineProperties
- 这意味着您正在使用property descriptors。虽然您只设置值,但enumerable
,writable
和configurable
标记默认为false
。
这样做的正确方法是什么?
您明确地将enumberable
和writable
标志明确设置为true
,或者您不使用第二个参数:
var bar2 = Object.create( Object.prototype ); // or Slideshow.prototype or whatever
bar2.oldProp = 'ECMA Script 3';
bar2.newProp = 'ECMA Script 5';
但是,如果这对你来说太长了,你会使用工厂实用程序函数 - 基本上是构造函数。