为什么 Object.create 在 JavaScript 中的原型继承方面没有达到我的预期
Why Object.create is not doing what I expect in terms of prototypal inheritance in JavaScript
我仍在努力弄清楚这个 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: 'base message'
baseSettings.getMessage(): 'base message from function'
homeSettings.name: ''
homeSettings.getName(): 'name from function'
homeSettings.message: 'undefined'
其中两个实际上抛出 JS 异常。
尝试 2
如果我添加对超级构造函数的调用,它会稍微改变一下:
Link: 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: 'base message'
baseSettings.getMessage(): 'base message from function'
homeSettings.name: '' (注意!这是空的,不应该是)
homeSettings.getName(): 'name from function'
homeSettings.message: 'base message'
homeSettings.getMessage(): 'base message from function'
大部分都可以,但我不明白为什么 homeSettings.name returns 什么都没有?
在我看来,Object.create 确实什么也没做,或者更确切地说,它需要调用超级构造函数才能有所作为,但即便如此,它也不完美。
尝试 3
删除对 Object.create 的调用并保留对超级构造函数的调用。
Link: 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: 'base message'
baseSettings.getMessage(): 'base message from function'
homeSettings.name: 'name'
homeSettings.getName(): 'name from function'
homeSettings.message: 'base message'
homeSettings.getMessage(): 'base message from function'
结论
我正在研究我在 SO 上看到的各种示例,我不得不承认我还没有达到我理解为什么继承在 JS 中有效或无效的地步。我遇到的另一个问题是,在各种测试中 'this' 的上下文发生了变化,我知道会发生这种情况,但我似乎无法很好地理解为什么或何时发生,因此我无法确切地知道发生了什么.
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()
更有意义对象如我上面的例子。要使用您的测试代码:
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
作为原型创建对象,它们将从该对象继承 name
和 getName
,以及 message
和 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()
——在这些示例中由 x
引用的对象没有自己的属性。
您代码中的主要问题是这一行:
homeSettings.prototype = Object.create(baseSettings);
当 Object.create 只需要一个对象时,您正在将函数构造函数传递给它。在这种情况下,它只是 return 一个空对象。
使用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。因此,不会复制 base class 中定义的 public 属性。因此 Bike class 将具有启动方法但没有名称 属性.
这个问题可以在一定程度上通过调用 Base class 上下文为 subclass 的调用方法来解决,如下所示。
通过在 subclass 中调用 Base class 调用方法,我们可以将属性从 Base 复制到 Sub class。
但是,属性是按实例复制的,而不是在原型上,因此这需要更多内存。
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.
上有更多详细信息
我仍在努力弄清楚这个 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: 'base message'
baseSettings.getMessage(): 'base message from function'
homeSettings.name: ''
homeSettings.getName(): 'name from function'
homeSettings.message: 'undefined'
其中两个实际上抛出 JS 异常。
尝试 2
如果我添加对超级构造函数的调用,它会稍微改变一下:
Link: 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: 'base message'
baseSettings.getMessage(): 'base message from function'
homeSettings.name: '' (注意!这是空的,不应该是)
homeSettings.getName(): 'name from function'
homeSettings.message: 'base message'
homeSettings.getMessage(): 'base message from function'
大部分都可以,但我不明白为什么 homeSettings.name returns 什么都没有?
在我看来,Object.create 确实什么也没做,或者更确切地说,它需要调用超级构造函数才能有所作为,但即便如此,它也不完美。
尝试 3
删除对 Object.create 的调用并保留对超级构造函数的调用。
Link: 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: 'base message'
baseSettings.getMessage(): 'base message from function'
homeSettings.name: 'name'
homeSettings.getName(): 'name from function'
homeSettings.message: 'base message'
homeSettings.getMessage(): 'base message from function'
结论
我正在研究我在 SO 上看到的各种示例,我不得不承认我还没有达到我理解为什么继承在 JS 中有效或无效的地步。我遇到的另一个问题是,在各种测试中 'this' 的上下文发生了变化,我知道会发生这种情况,但我似乎无法很好地理解为什么或何时发生,因此我无法确切地知道发生了什么.
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()
更有意义对象如我上面的例子。要使用您的测试代码:
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
作为原型创建对象,它们将从该对象继承 name
和 getName
,以及 message
和 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()
——在这些示例中由 x
引用的对象没有自己的属性。
您代码中的主要问题是这一行:
homeSettings.prototype = Object.create(baseSettings);
当 Object.create 只需要一个对象时,您正在将函数构造函数传递给它。在这种情况下,它只是 return 一个空对象。
使用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。因此,不会复制 base class 中定义的 public 属性。因此 Bike class 将具有启动方法但没有名称 属性.
这个问题可以在一定程度上通过调用 Base class 上下文为 subclass 的调用方法来解决,如下所示。
通过在 subclass 中调用 Base class 调用方法,我们可以将属性从 Base 复制到 Sub class。
但是,属性是按实例复制的,而不是在原型上,因此这需要更多内存。
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.
上有更多详细信息