为什么Object.create在JavaScript

时间:2016-03-01 13:50:32

标签: javascript inheritance prototypal-inheritance

我仍然试图了解这个JS继承的东西如何运作,每次我认为我拥有它......我显然不会这样做。

尝试1

link:https://jsfiddle.net/jacquesvanderhoven/4qkvdn46/

代码

var baseSettings = function ()
{
    this.message = "base message";
    this.getMessage = function(){
        return  "base message from function";
    }
};

var homeSettings = function ()
{
    this.name = "name";
    this.getName = function()
    {
        return "name from function";
    };
};

homeSettings.prototype = Object.create(baseSettings);

var $log = $("#log");
var base = new baseSettings();
var home = new homeSettings();

$log.html($log.html() + "<br /> baseSettings.message: '" + base.message + "'");
$log.html($log.html() + "<br /> baseSettings.getMessage(): '" + base.getMessage() + "'");
$log.html($log.html() + "<br /> homeSettings.name: '" + home.name + "'");
$log.html($log.html() + "<br /> homeSettings.getName(): '" + home.getName() + "'");
$log.html($log.html() + "<br /> homeSettings.message: '" + home.message + "'");
$log.html($log.html() + "<br /> homeSettings.getMessage(): '" + home.getMessage() + "'");

结果

baseSettings.message:&#39;基本消息&#39;
baseSettings.getMessage():&#39;来自函数&#39;的基本消息 homeSettings.name:&#39;&#39;
homeSettings.getName():&#39;来自函数&#39;的名称 homeSettings.message:&#39; undefined&#39;

其中两个实际上抛出JS异常。

Attemp 2

如果我添加对超级构造函数的调用,它会稍微改变一下:
链接:https://jsfiddle.net/jacquesvanderhoven/4qkvdn46/5/

代码

var baseSettings = function ()
{
    this.message = "base message";
    this.getMessage = function(){
        return  "base message from function";
    }
};

var homeSettings = function ()
{
    baseSettings.call(this);
    this.name = "name";
    this.getName = function()
    {
        return "name from function";
    };
};

homeSettings.prototype = Object.create(baseSettings);

var $log = $("#log");
var base = new baseSettings();
var home = new homeSettings();

$log.html($log.html() + "<br /> baseSettings.message: '" + base.message + "'");
$log.html($log.html() + "<br /> baseSettings.getMessage(): '" + base.getMessage() + "'");
$log.html($log.html() + "<br /> homeSettings.name: '" + home.name + "'");
$log.html($log.html() + "<br /> homeSettings.getName(): '" + home.getName() + "'");
$log.html($log.html() + "<br /> homeSettings.message: '" + home.message + "'");
$log.html($log.html() + "<br /> homeSettings.getMessage(): '" + home.getMessage() + "'");

结果

baseSettings.message:&#39;基本消息&#39;
baseSettings.getMessage():&#39;来自函数&#39;的基本消息 homeSettings.name:&#39;&#39; (注意!这是空的,不应该)
homeSettings.getName():&#39;来自函数&#39;的名称 homeSettings.message:&#39;基本信息&#39;
homeSettings.getMessage():&#39;来自函数&#39;的基本消息

他们中的大多数都有效,但我无法弄清楚为什么homeSettings.name什么都不返回?

它开始向我看起来像Object.create没有做任何事情,或者更确切地说它需要调用超级构造函数来做出任何改变,但即使这样它也不完美。

尝试3

删除对Object.create的调用,并将调用留给超级构造函数 链接:https://jsfiddle.net/jacquesvanderhoven/4qkvdn46/8/

代码

var baseSettings = function ()
{
    this.message = "base message";
    this.getMessage = function(){
        return  "base message from function";
    }
};

var homeSettings = function ()
{
    baseSettings.call(this);
    this.name = "name";
    this.getName = function()
    {
        return "name from function";
    };
};

var $log = $("#log");
var base = new baseSettings();
var home = new homeSettings();

$log.html($log.html() + "<br /> baseSettings.message: '" + base.message + "'");
$log.html($log.html() + "<br /> baseSettings.getMessage(): '" + base.getMessage() + "'");
$log.html($log.html() + "<br /> homeSettings.name: '" + home.name + "'");
$log.html($log.html() + "<br /> homeSettings.getName(): '" + home.getName() + "'");
$log.html($log.html() + "<br /> homeSettings.message: '" + home.message + "'");
$log.html($log.html() + "<br /> homeSettings.getMessage(): '" + home.getMessage() + "'");

结果

baseSettings.message:&#39;基本消息&#39;
baseSettings.getMessage():&#39;来自函数&#39;的基本消息 homeSettings.name:&#39; name&#39;
homeSettings.getName():&#39;来自函数&#39;的名称 homeSettings.message:&#39;基本信息&#39;
homeSettings.getMessage():&#39;来自函数&#39;的基本消息

结论

我正在研究我在SO上看到的各种例子,我不得不承认我没有达到理解为什么继承有效或无法在JS工作的地步。我遇到的另一个问题是,在各种测试中,这个&#39;这个&#39;改变,我知道发生了,但我似乎无法很好地理解为什么或什么时候,以便我确切知道发生了什么。

2 个答案:

答案 0 :(得分:1)

JavaScript中继承的关键是原型链。每当发生属性查找时:

something.propertyName

运行时首先检查直接引用的对象。如果找不到该属性,则搜索由原型内部属性链接的对象链(通常只是一个对象)的过程。

因此,继承工作的关键是创建拥有原型链的对象。传统的方法是使用new运算符和构造函数:

function Constructor() {}
Constructor.prototype = {
  propertyName: function() { alert("hello world"); }
};

现在可以创建一个对象:

var something = new Constructor();

和引用的属性:

something.propertyName(); // "hello world"

请注意,此构造函数不会执行任何操作可以做某事,但它只是通过现有的原型对象来实现继承工作。

Object.create()函数只是制作具有原型链的对象的另一种方法:

var something = Object.create({
  propertyName: function() { alert("hello world"); }
});

something.propertyName(); // "hello world"

那也可以写成

var something = Object.create(Constructor.prototype);

从第一个例子中重新使用原型对象。

当然,当要用作原型的对象是在代码中创建并为此目的而保留的对象时,Object.create()会更有意义,而不是on-on -fly对象,如上例所示。要使用您的测试代码:

var baseSettings = { // plain object, not a constructor
  message: "base message",
  getMessage: function() {
    return "base message: " + this.message;
  }
};

var homeSettings = Object.create(baseSettings, {
  name: "home name",
  getName: function() {
    return "home name: " + this.name;
  }
});

现在可以使用homeSettings作为原型创建对象,并且他们将从该对象继承namegetNamemessage和{{1来自getMessage对象:

baseSettings

可以使用构造函数而不是var x = Object.create(homeSettings); console.log(x.name); // "home name" console.log(x.getName()); // "home name: home name" console.log(x.message); // "base message" console.log(x.getMessage()); // "base message: base message" 完成:

Object.create()

function HomeBase() {}; HomeBase.prototype = homeSettings; var x = new HomeBase(); 调用的相同序列会产生相同的结果。在任何一种情况下 - 通过console.log()或通过new - 这些示例中Object.create()引用的对象都没有自己的属性。

答案 1 :(得分:0)

代码中的主要问题是这一行:

homeSettings.prototype = Object.create(baseSettings);

当它只需要一个对象时,您正在将函数构造函数传递给Object.create。在这种情况下,它只会返回一个空对象。

使用Object.create进行继承非常简单。

//Base class
function Vehicle() {
  this.name = "vehicle " + parseInt( Math.random()*100 );
}

Vehicle.prototype.getName = function () {
  console.log( this.name );
};

//sub class
function Bike() {}

Bike.prototype = Object.create( Vehicle.prototype );
var honda = new Bike();

但是,请注意,只设置了原型引用,构造函数Vehicle永远不会执行。因此,不会复制基类中定义的公共属性。因此,Bike类将具有start方法,但没有name属性。

通过使用context作为子类调用Base类调用方法可以在一定程度上解决此问题,如下所示。

通过在子类中调用Base类调用方法,我们可以将属性从Base复制到Sub类。

但是,每个实例都会复制属性而不是原型,因此需要更多内存。

function Scooter() {
  //runs on creating every new instance so the name property is copied to each instance not to prototype
  Vehicle.call(this);
}

Scooter.prototype = Object.create( Vehicle.prototype );
var scooty = new Scooter();

我的github wiki page上的更多细节。