如何在原型继承中创建新对象时覆盖函数?

Nik*_*ola 11 javascript inheritance javascript-objects

这篇博文中,我们有一个JavaScript原型继承的例子:

var human = {
    name: '',
    gender: '',
    planetOfBirth: 'Earth',
    sayGender: function () {
        alert(this.name + ' says my gender is ' + this.gender);
    },
    sayPlanet: function () {
        alert(this.name + ' was born on ' + this.planetOfBirth);
    }
};

var male = Object.create(human, {
    gender: {value: 'Male'}
});

var female = Object.create(human, {
    gender: {value: 'Female'}
});

var david = Object.create(male, {
    name: {value: 'David'},
    planetOfBirth: {value: 'Mars'}
});

var jane = Object.create(female, {
    name: {value: 'Jane'}
});

david.sayGender(); // David says my gender is Male
david.sayPlanet(); // David was born on Mars

jane.sayGender(); // Jane says my gender is Female
jane.sayPlanet(); // Jane was born on Earth
Run Code Online (Sandbox Code Playgroud)

现在,我想知道的是如何正确 "覆盖",例如,sayPlanet功能?

我试过这样的:

jane.sayPlanet = function(){
    console.log("something different");
};
Run Code Online (Sandbox Code Playgroud)

这很有效.

但是,我也尝试过这样:

var jane = Object.create(female, {
    name: {value: 'Jane'},

    sayPlanet: function(){
        console.log("something different");
    }
});
Run Code Online (Sandbox Code Playgroud)

但我得到一个类型错误.

我的问题是:

  • 我怎样才能添加sayPlanet里面的功能Object.create
  • 这是"一个好方法"还是有更好的(最佳实践)方式?

编辑: 我想办法如何添加sayPlanet内部Object.create:

sayPlanet: {
    value: function(){
        console.log("something different");
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,第二个问题仍然存在.此外,如果有人能够在更深层次上解释它,我会很感激,如果这是一个像这样"使用它的好方法".

编辑#2:随着马哈维亚下面指出的,这是一个可怕的例子,因为事实证明你不能(请纠正我,如果我错了)改变namejane,一旦它已被Object.create天.

编辑#3 :(男人,男人,这会让我进入某个人们穿白大褂的设施).正如@WhiteHat指出的那样,确实可以将name属性设置为可更新,如下所示:

var jane = Object.create(female, {
    name: {
        value: 'Jane',
        writable: true
    }
});
Run Code Online (Sandbox Code Playgroud)

然后你就可以做到jane.name="Jane v2.0";.

我在这里说实话的人 - 我似乎没有一个线索,看似有这么多的选择.就在今天,我读到Eric Elliot https://medium.com/javascript-scene/the-two-pillars-of-javascript-ee6f3281e7f3,现在我不知道该怎么想,因为他继续争辩说人们在ES6并没有做得很好:O.嗯,我想我将不得不再次重新阅读Crockfords的书,决定"单程"并看看它需要多远.

Nic*_*Guy 6

我学到的最佳实践是在Object原型级别定义写权限.我从阅读Addy Osmani的JavaScript设计模式中学到了这种技术,它在网上非常有信誉和开源:http://addyosmani.com/resources/essentialjsdesignpatterns/book/

请查看以下示例中的sayPlanet属性.请记住,您不需要将所有其他属性"可写"设置为false,我只在我的示例代码中进行说明以说明这一点.与其他仍然有效的方法相比,这种方法提供了更大的灵活性和可重用性.在这里,您可能会注意到这是Object.defineProperties原型中的语法.

var human = {
  name: {
    value: '',
    writable: false //only to illustrate default behavior not needed
  },
  gender: {
    value: '',
    writable: false //only to illustrate default behavior not needed
  },
  planetOfBirth: {
    value: "Earth",
    configurable: false //only to illustrate default behavior not needed
  }

};

//here we define function properties at prototype level

Object.defineProperty(human, 'sayGender', {
  value: function() {
    alert(this.name + ' says my gender is ' + this.gender);
  },
  writable: false
});

Object.defineProperty(human, 'sayPlanet', {
  value: function() {
    alert(this.name + ' was born on ' + this.planetOfBirth);
  },
  writable: true
});

//end definition of function properties

var male = Object.create(human, {
  gender: {
    value: 'Male'
  }
});

var female = Object.create(human, {
  gender: {
    value: 'Female'
  }
});

var david = Object.create(male, {
  name: {
    value: 'David'
  },
  planetOfBirth: {
    value: 'Mars'
  }
});

var jane = Object.create(female, {
  name: {
    value: 'Jane'
  }
});

//define the writable sayPlanet function for Jane
jane.sayPlanet = function() {
  alert("something different");
};

//test cases

//call say gender before attempting to ovverride
david.sayGender(); // David says my gender is Male

//attempt to override the sayGender function for david 
david.sayGender = function() {
  alert("I overrode me!")
};
//I tried and failed to change an unwritable fucntion
david.sayGender(); //David maintains that my gender is Male

david.sayPlanet(); // David was born on Mars

jane.sayGender(); // Jane says my gender is Female
jane.sayPlanet(); // something different
Run Code Online (Sandbox Code Playgroud)


Jul*_*ire 4

使用Object.create当然是一种有效的方法,但在我看来,这个例子本身对于Object.create. 这篇博客文章很好地总结了在 javascript 中创建对象的不同方法,但我不认为 的示例很好地Object.create说明了它的工作原理,它与该new/constructor方法比看起来更相似。

Object.create允许创建基于 a 的对象prototype,但没有 constructor . 这意味着prototype chain创建的对象的 不依赖于 constructor , (这就是为什么它可能更容易理解,通过构造函数链接的这个原型不是很简单或容易理解)。但仍然以同样的方式Object.create创建一个, 。prototype chainnew

因此,在您的示例中,当您namehuman此处定义时:

var human = {
    name: '',
Run Code Online (Sandbox Code Playgroud)

然后当你创建时jane

var jane = Object.create(female, {
    name: {value: 'Jane'}
Run Code Online (Sandbox Code Playgroud)

name property您并没有真正为您在 中定义的值赋值human。事实上,您正在向 jane 添加属性。但仍然是的human.name财产。它之所以有效,是因为 javascript 将沿着原型链找到第一个匹配的属性,但仍然以某种方式链接到.prototype chainjanehuman.namejane

请参阅此处: https: //developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain

如果您使用构造函数,也会发生同样的情况:

var Person = function(gender, name) {
     this.name = name;
}

var newPerson = new Person();
Run Code Online (Sandbox Code Playgroud)

功能也是如此sayPlanet

这是一种有效的方法,但可能会导致奇怪的行为。例如,您可以决定sayPlanet通过以下方式分配它来修改所有人类:

human.sayPlanet = function(){console.log('new sayPlanet')}
Run Code Online (Sandbox Code Playgroud)

这将适用于所有人humans,除了那些您为其赋予了sayPlanet自己的财产的人。在你的情况下这可能是预期的结果。但仍然,你必须看看是否sayPlanet真的应该是人类的财产。

对于gender,它应用于human、 以及malefemale。所以改变human.gender对任何人都不起作用。但它仍然是 的一个属性human,当您想要使用这些对象时,这会有点令人困惑。基本上,您有一个已定义的属性,该属性是可写的,但更改后根本没有任何效果。它主要指示您需要将哪些属性添加到人类实例或原型链中的某个位置。同样,它似乎被使用了很多,但是当用这种例子来解释时,它在某种程度上给人的印象是Object.create只是组合了属性,但事实并非如此。

最后,您需要选择是否愿意合作prototypes。如果没有,那么函数式继承可能是最好的方法。那么每个对象都是不同的,并且有自己的一组属性,您可以对其进行初始化,并且不必担心prototypes.

如果你想使用prototypes,那么你可以使用new/constructorObject.create方法。但Object.create将以同样的方式创建原型链new,它只是摆脱了构造函数。

Object.create一个关于如何分享一些行为的小例子new

事实上(只是为了更令人困惑),有些人Object.createnew/constructor函数式继承相结合。例如这样的事情:

https://john-dugan.com/object-orientation-javascript-pattern-comparison/#oloo-pattern

应用于您的示例,它将给出如下内容:

var jane = Object.create(female, {
    name: {value: 'Jane'}
Run Code Online (Sandbox Code Playgroud)

不一定更好,但效果也不错,而且在我看来有一定的优势。

无论如何,正如其他人所说,似乎没有标准或默认方法可以做到这一点。