这是在 JavaScript 中定义继承链的 'standard' 方式吗?
Is this the 'standard' way to define inheritance chains in JavaScript?
我正在尝试了解 Javascript 中的继承。
我知道每个对象都有一个原型,这是一个它从中继承属性的对象。我知道 .prototype
属性仅存在于函数中,当它用作构造函数时,它将被设置为从该函数创建的对象的原型。
我知道通常对象的原型是不可访问的,尽管有些浏览器支持 __proto__
属性。 (但由于它不是语言的 'classical' 部分,我想了解如何在没有它的情况下使用该语言)。
所以如果所有这些都是正确的 (?),我想了解一下定义继承链的标准方法是什么。
我能想到的唯一方法是:
我希望它们从另一个对象继承的所有对象,必须通过构造函数创建。他们的 'base object' 将被设置为其构造函数的 .prototype
。
当我希望其中一个成为其他对象的 'base object' 时,我会将其设置为另一个构造函数的 .prototype
。等等。
这看起来很奇怪。有没有办法(在 'normal' JS 中)直接设置对象的 'base'?或者我是否必须以上述方式使用构造函数才能创建继承链?
创建继承的 'standard' 方法是什么?我描述的方法是标准方法吗?
您可以通过 javascript 中的 2 种方式继承 - 经典和原型
古典
function inherit (C, P) {
var F = function () {};
F.prototype = P.prototype;
C.prototype = new F();
}
原型
function inherit (o) {
function F() {}
F.prototype = o;
return new F();
}
JavaScript支持继承的主要方式是原型继承。具体来说,只要无法在初始对象上找到 属性 查找,JavaScript 中的对象就会将 委托给其他对象。此委派一直持续到 JavaScript 引擎到达 Object.prototype
,其中发现 属性 或抛出错误。
当前以特定对象为原型创建对象的最佳做法是使用Object.create
- 您可以查看更多信息here。
这是一个例子:
var methods = {
method1: function () { console.log( 'something' ); },
method2: function () { return 'cool'; }
};
/*
* Now firstObj will delegate to methods whenever a property lookup can't
* be found on firstObj itself
*/
var firstObj = Object.create( methods );
// You can add custom properties on firstObj
firstObj.someOtherProperty = 'custom property';
/*
* You can create a chain of delegations! Property lookup first happens on secondObj.
* If its not found there, it looks up the property in firstObj. If its not found there,
* then it looks up the property in methods. Finally, if not found, it tries
* Object.prototype
*/
var secondObj = Object.create ( firstObj );
JavaScript 中的继承起初有点难以理解,因为:
- JavaScript 是一种原型面向对象编程语言(即对象直接继承自其他对象)。这意味着 classes 和对象之间没有区别。用作 classes 的对象称为原型。
- 不幸的是,创建原型实例的传统方法是使用
new
(这让人认为实例继承自构造函数,而不是原型)。这称为 constructor pattern,它是 JavaScript. 中混淆的主要原因
因此引入了 Object.create
。它允许对象直接继承自其他对象。但是,与使用 new
相比,Object.create
速度较慢。我遇到了和你一样的问题,我正在寻找替代方案;我确实想出了一个。
JavaScript
中OOP的传统方式
考虑以下代码:
function Person(firstname, lastname, gender) {
this.firstname = firstname;
this.lastname = lastname;
this.gender = gender;
}
Person.prototype.getFullname = function () {
return this.firstname + " " + this.lastname;
};
Man.prototype = new Person;
Man.prototype.constructor = Man;
function Man(firstname, lastname) {
Person.call(this, firstname, lastname, "M");
}
var bobMarley = new Man("Bob", "Marley");
alert(bobMarley.getFullname());
这种编写代码的方式存在几个问题:
- 没有封装。构造函数和原型方法在各处都有定义。它看起来不连贯。像意大利通心粉。它看起来不像一个逻辑单元。
- 我们通过将
Man.prototype
设置为 new Person
来继承 Person.prototype
。但是,在这样做的过程中,我们在 Man.prototype
上初始化了 firstname
、lastname
和 gender
属性,这是错误的。
JavaScript
中的 OOP 新方法
随着 Object.create
的引入,我们现在可以编写如下代码:
function Person(firstname, lastname, gender) {
this.firstname = firstname;
this.lastname = lastname;
this.gender = gender;
}
Person.prototype.getFullname = function () {
return this.firstname + " " + this.lastname;
};
Man.prototype = Object.create(Person.prototype);
Man.prototype.constructor = Man;
function Man(firstname, lastname) {
Person.call(this, firstname, lastname, "M");
}
var bobMarley = new Man("Bob", "Marley");
alert(bobMarley.getFullname());
唯一的变化是我们写 Man.prototype = Object.create(Person.prototype)
而不是 Man.prototype = new Person
。这就解决了传统方法的第二个问题。但是,代码仍然看起来像意大利面条。
不过,Object.create
还是挺厉害的。您还可以使用它来编写面向对象的代码,而无需创建构造函数。有些人称之为 initializer pattern:
var person = {
init: function (firstname, lastname, gender) {
this.firstname = firstname;
this.lastname = lastname;
this.gender = gender;
},
getFullname: function () {
return this.firstname + " " + this.lastname;
}
};
var man = Object.create(person, {
init: {
value: function (firstname, lastname) {
person.init.call(this, firstname, lastname, "M");
}
}
});
var bobMarley = Object.create(man);
bobMarley.init("Bob", "Marley");
alert(bobMarley.getFullname());
这解决了传统方法的所有问题。但是,它也引入了一些新的问题:
- 创建原型实例的方式与创建对象字面量的方式不一致。
- 您必须使用
Object.create
创建实例,然后使用 init
初始化新对象。这比简单地使用 new
. 慢得多
我的OOP方式是JavaScript
为了解决这个问题,我在 JavaScript:
中为 OOP 编写了自己的函数
var Person = defclass({
constructor: function (firstname, lastname, gender) {
this.firstname = firstname;
this.lastname = lastname;
this.gender = gender;
},
getFullname: function () {
return this.firstname + " " + this.lastname;
}
});
var Man = extend(Person, {
constructor: function (firstname, lastname) {
Person.call(this, firstname, lastname, "M");
}
});
var bobMarley = new Man("Bob", "Marley");
alert(bobMarley.getFullname());
function defclass(prototype) {
var constructor = prototype.constructor;
constructor.prototype = prototype;
return constructor;
}
function extend(constructor, properties) {
var prototype = Object.create(constructor.prototype);
var keys = Object.keys(properties);
var length = keys.length;
var index = 0;
while (index < length) {
var key = keys[index++];
prototype[key] = properties[key];
}
return defclass(prototype);
}
我在 JavaScript 中为 OOP 定义了两个函数 defclass
和 extend
。 defclass
函数从原型创建一个“class”。这是可能的,因为 prototypes and classes are isomorphic.
extend函数是为了继承。它创建了一个 constructor
的 prototype
的实例,并在返回新原型的“class”之前将一些属性复制到它上面。
这是我目前在JavaScript中创建原型链的方式。与其他方法相比,它具有以下优点:
- 每个“class”都被封装了。没有原型方法随处可见。它看起来不像意大利面。
extend
函数使用Object.create
进行继承。因此,没有额外的属性被添加到新的原型中。这是一个空白原型。
- 您不必担心在
prototype
上重置 constructor
属性。它会自动为您完成。
defclass
和 extend
函数与初始化模式中的对象文字和 Object.create
函数一致。
- 我们使用
new
而不是 Object.create
和 init
创建实例。因此生成的代码要快得多。
我现在可能是错的,但我不这么认为。希望对您有所帮助。
我正在尝试了解 Javascript 中的继承。
我知道每个对象都有一个原型,这是一个它从中继承属性的对象。我知道 .prototype
属性仅存在于函数中,当它用作构造函数时,它将被设置为从该函数创建的对象的原型。
我知道通常对象的原型是不可访问的,尽管有些浏览器支持 __proto__
属性。 (但由于它不是语言的 'classical' 部分,我想了解如何在没有它的情况下使用该语言)。
所以如果所有这些都是正确的 (?),我想了解一下定义继承链的标准方法是什么。
我能想到的唯一方法是:
我希望它们从另一个对象继承的所有对象,必须通过构造函数创建。他们的 'base object' 将被设置为其构造函数的 .prototype
。
当我希望其中一个成为其他对象的 'base object' 时,我会将其设置为另一个构造函数的 .prototype
。等等。
这看起来很奇怪。有没有办法(在 'normal' JS 中)直接设置对象的 'base'?或者我是否必须以上述方式使用构造函数才能创建继承链?
创建继承的 'standard' 方法是什么?我描述的方法是标准方法吗?
您可以通过 javascript 中的 2 种方式继承 - 经典和原型
古典
function inherit (C, P) {
var F = function () {};
F.prototype = P.prototype;
C.prototype = new F();
}
原型
function inherit (o) {
function F() {}
F.prototype = o;
return new F();
}
JavaScript支持继承的主要方式是原型继承。具体来说,只要无法在初始对象上找到 属性 查找,JavaScript 中的对象就会将 委托给其他对象。此委派一直持续到 JavaScript 引擎到达 Object.prototype
,其中发现 属性 或抛出错误。
当前以特定对象为原型创建对象的最佳做法是使用Object.create
- 您可以查看更多信息here。
这是一个例子:
var methods = {
method1: function () { console.log( 'something' ); },
method2: function () { return 'cool'; }
};
/*
* Now firstObj will delegate to methods whenever a property lookup can't
* be found on firstObj itself
*/
var firstObj = Object.create( methods );
// You can add custom properties on firstObj
firstObj.someOtherProperty = 'custom property';
/*
* You can create a chain of delegations! Property lookup first happens on secondObj.
* If its not found there, it looks up the property in firstObj. If its not found there,
* then it looks up the property in methods. Finally, if not found, it tries
* Object.prototype
*/
var secondObj = Object.create ( firstObj );
JavaScript 中的继承起初有点难以理解,因为:
- JavaScript 是一种原型面向对象编程语言(即对象直接继承自其他对象)。这意味着 classes 和对象之间没有区别。用作 classes 的对象称为原型。
- 不幸的是,创建原型实例的传统方法是使用
new
(这让人认为实例继承自构造函数,而不是原型)。这称为 constructor pattern,它是 JavaScript. 中混淆的主要原因
因此引入了 Object.create
。它允许对象直接继承自其他对象。但是,与使用 new
相比,Object.create
速度较慢。我遇到了和你一样的问题,我正在寻找替代方案;我确实想出了一个。
JavaScript
中OOP的传统方式考虑以下代码:
function Person(firstname, lastname, gender) {
this.firstname = firstname;
this.lastname = lastname;
this.gender = gender;
}
Person.prototype.getFullname = function () {
return this.firstname + " " + this.lastname;
};
Man.prototype = new Person;
Man.prototype.constructor = Man;
function Man(firstname, lastname) {
Person.call(this, firstname, lastname, "M");
}
var bobMarley = new Man("Bob", "Marley");
alert(bobMarley.getFullname());
这种编写代码的方式存在几个问题:
- 没有封装。构造函数和原型方法在各处都有定义。它看起来不连贯。像意大利通心粉。它看起来不像一个逻辑单元。
- 我们通过将
Man.prototype
设置为new Person
来继承Person.prototype
。但是,在这样做的过程中,我们在Man.prototype
上初始化了firstname
、lastname
和gender
属性,这是错误的。
JavaScript
中的 OOP 新方法随着 Object.create
的引入,我们现在可以编写如下代码:
function Person(firstname, lastname, gender) {
this.firstname = firstname;
this.lastname = lastname;
this.gender = gender;
}
Person.prototype.getFullname = function () {
return this.firstname + " " + this.lastname;
};
Man.prototype = Object.create(Person.prototype);
Man.prototype.constructor = Man;
function Man(firstname, lastname) {
Person.call(this, firstname, lastname, "M");
}
var bobMarley = new Man("Bob", "Marley");
alert(bobMarley.getFullname());
唯一的变化是我们写 Man.prototype = Object.create(Person.prototype)
而不是 Man.prototype = new Person
。这就解决了传统方法的第二个问题。但是,代码仍然看起来像意大利面条。
不过,Object.create
还是挺厉害的。您还可以使用它来编写面向对象的代码,而无需创建构造函数。有些人称之为 initializer pattern:
var person = {
init: function (firstname, lastname, gender) {
this.firstname = firstname;
this.lastname = lastname;
this.gender = gender;
},
getFullname: function () {
return this.firstname + " " + this.lastname;
}
};
var man = Object.create(person, {
init: {
value: function (firstname, lastname) {
person.init.call(this, firstname, lastname, "M");
}
}
});
var bobMarley = Object.create(man);
bobMarley.init("Bob", "Marley");
alert(bobMarley.getFullname());
这解决了传统方法的所有问题。但是,它也引入了一些新的问题:
- 创建原型实例的方式与创建对象字面量的方式不一致。
- 您必须使用
Object.create
创建实例,然后使用init
初始化新对象。这比简单地使用new
. 慢得多
我的OOP方式是JavaScript
为了解决这个问题,我在 JavaScript:
中为 OOP 编写了自己的函数var Person = defclass({
constructor: function (firstname, lastname, gender) {
this.firstname = firstname;
this.lastname = lastname;
this.gender = gender;
},
getFullname: function () {
return this.firstname + " " + this.lastname;
}
});
var Man = extend(Person, {
constructor: function (firstname, lastname) {
Person.call(this, firstname, lastname, "M");
}
});
var bobMarley = new Man("Bob", "Marley");
alert(bobMarley.getFullname());
function defclass(prototype) {
var constructor = prototype.constructor;
constructor.prototype = prototype;
return constructor;
}
function extend(constructor, properties) {
var prototype = Object.create(constructor.prototype);
var keys = Object.keys(properties);
var length = keys.length;
var index = 0;
while (index < length) {
var key = keys[index++];
prototype[key] = properties[key];
}
return defclass(prototype);
}
我在 JavaScript 中为 OOP 定义了两个函数 defclass
和 extend
。 defclass
函数从原型创建一个“class”。这是可能的,因为 prototypes and classes are isomorphic.
extend函数是为了继承。它创建了一个 constructor
的 prototype
的实例,并在返回新原型的“class”之前将一些属性复制到它上面。
这是我目前在JavaScript中创建原型链的方式。与其他方法相比,它具有以下优点:
- 每个“class”都被封装了。没有原型方法随处可见。它看起来不像意大利面。
extend
函数使用Object.create
进行继承。因此,没有额外的属性被添加到新的原型中。这是一个空白原型。- 您不必担心在
prototype
上重置constructor
属性。它会自动为您完成。 defclass
和extend
函数与初始化模式中的对象文字和Object.create
函数一致。- 我们使用
new
而不是Object.create
和init
创建实例。因此生成的代码要快得多。
我现在可能是错的,但我不这么认为。希望对您有所帮助。