gpt4 book ai didi

Javascript:类实例初始化与继承

转载 作者:行者123 更新时间:2023-11-30 15:18:41 24 4
gpt4 key购买 nike

下面是类 Animal 及其子类 Bird 在 JavaScript 中的定义示例(使用 TypeScript ):

class Animal {
name: string;
numberOfLegs: number = 4;
aboutMe: string;
constructor (theName: string) {
this.name = theName;
this.init();
}
init() {
this.aboutMe = `I'm ${this.name} with ${this.numberOfLegs} legs`;
}
}

class Bird extends Animal {
numberOfLegs: number = 2;
constructor (theName: string) {
super(theName);
}
}

var bird = new Bird('Bimbo');
console.log(bird.aboutMe);

bird.aboutMe 属性的正确期望值应该是I'm Bimbo with 2 legs,但实际上你将得到 I'm Bimbo with 4 legs。当你将上面的 TypeScript 代码编译成纯 JavaScript 时 here很明显为什么这不能正常工作。

我的问题:如何正确编写 JavaScript 类的初始化逻辑,使其也适用于继承,并以我们在其他 OO 语言中习惯的方式工作? TypeScript 试图解决 JavaScript 和其他 OO 语言之间的差距,但即使在这种微不足道的情况下它也失败了。我错过了什么吗?

为了证明我对正确结果的期望是有效的,我将上面的代码重写为 PHP:

class Animal {
protected $name;
protected $numberOfLegs = 4;
public $aboutMe;
public function __construct ($theName) {
$this->name = $theName;
$this->init();
}
protected function init() {
$this->aboutMe = "I'm {$this->name} with {$this->numberOfLegs} legs";
}
}

class Bird extends Animal {
protected $numberOfLegs = 2;
public function __construct ($theName) {
parent::__construct($theName);
}
}

$bird = new Bird('Bimbo');
echo $bird->aboutMe;

上述 PHP 代码返回的结果是 I'm Bimbo with 2 legs

编辑 1: 我当然知道如何使上面的代码正常工作。我的需要不是让这个微不足道的代码工作,而是找到一种方法来处理 JS 类实例初始化,使其在复杂情况下也能正常工作。

也许考虑到 TypeScript,我会添加 “如果 TypeScript 试图看起来像 C 风格的类定义,那么它也能像那样工作将是非常值得赞赏的”。有办法实现吗?

编辑 2: 提出了非常好的通用解决方案 here below by Emil S. Jørgensen .这甚至适用于更长的继承链(例如 Bird extends AnimalCityBird extends Bird)。我在他的回答中添加了更多代码,以表明在每个级别上您都可以重用父(超)类 init() 并在需要时添加您自己的初始化逻辑:

/*
// TYPESCIPT
class Animal {
static _isInheritable = true;
public name: string;
public numberOfLegs: number = 4;
public aboutMe: string;
constructor(theName: string) {
this.name = theName;

var isInheirited = (arguments.callee.caller != null ? arguments.callee.caller._isInheritable != void 0 : false);
if (!isInheirited) {
console.log("In Animal is ");
this.init();
} else {
console.log("Skipping Animal init() because inherited");
}
}
init() {
console.log("the Animal init() called");
this.aboutMe = `I'm ${this.name} with ${this.numberOfLegs} legs`;
}
}

class Bird extends Animal {
public numberOfLegs: number = 2;
constructor(theName: string) {
super(theName);

var isInheirited = (arguments.callee.caller != null ? arguments.callee.caller._isInheritable != void 0 : false);
if (!isInheirited) {
console.log("In Bird is ");
this.init();
} else {
console.log("Skipping Bird init() because inherited");
}
}
init() {
super.init();
console.log("and also some additionals in the Bird init() called");
}
}

class CityBird extends Bird {
public numberOfLegs: number = 1;
constructor(theName: string) {
super(theName);

var isInheirited = (arguments.callee.caller != null ? arguments.callee.caller._isInheritable != void 0 : false);
if (!isInheirited) {
console.log("In CityBird is ");
this.init();
} else {
console.log("Skipping CityBird init() because inherited");
}
}
init() {
super.init();
console.log("and also some additionals in the CityBird init() called");
}
}

var bird = new CityBird('Bimbo');
console.log(bird.aboutMe);
*/
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var Animal = (function () {
function Animal(theName) {
this.numberOfLegs = 4;
this.name = theName;
var isInheirited = (arguments.callee.caller != null ? arguments.callee.caller._isInheritable != void 0 : false);
if (!isInheirited) {
console.log("In Animal is ");
this.init();
}
else {
console.log("Skipping Animal init() because inherited");
}
}
Animal.prototype.init = function () {
console.log("the Animal init() called");
this.aboutMe = "I'm " + this.name + " with " + this.numberOfLegs + " legs";
};
return Animal;
}());
Animal._isInheritable = true;
var Bird = (function (_super) {
__extends(Bird, _super);
function Bird(theName) {
var _this = _super.call(this, theName) || this;
_this.numberOfLegs = 2;
var isInheirited = (arguments.callee.caller != null ? arguments.callee.caller._isInheritable != void 0 : false);
if (!isInheirited) {
console.log("In Bird is ");
_this.init();
}
else {
console.log("Skipping Bird init() because inherited");
}
return _this;
}
Bird.prototype.init = function () {
_super.prototype.init.call(this);
console.log("and also some additionals in the Bird init() called");
};
return Bird;
}(Animal));
var CityBird = (function (_super) {
__extends(CityBird, _super);
function CityBird(theName) {
var _this = _super.call(this, theName) || this;
_this.numberOfLegs = 1;
var isInheirited = (arguments.callee.caller != null ? arguments.callee.caller._isInheritable != void 0 : false);
if (!isInheirited) {
console.log("In CityBird is ");
_this.init();
}
else {
console.log("Skipping CityBird init() because inherited");
}
return _this;
}
CityBird.prototype.init = function () {
_super.prototype.init.call(this);
console.log("and also some additionals in the CityBird init() called");
};
return CityBird;
}(Bird));
var bird = new CityBird('Bimbo');
console.log(bird.aboutMe);

此解决方案的缺点是您不能在 'use strict' 模式下将其用作 callercallee arguments 属性可能无法在严格模式下访问(see)。

编辑 3: 严格模式和 ES6 类兼容解决方案(避免使用严格模式禁止 callee)是基于比较 this.construct和类(函数)本身( see )。 init() 仅在两者相等时启动 - 这意味着 init() 仅在实例化类的构造函数中调用。这是 EDIT 2 重写的代码:

/*   
// TYPESCIPT
class Animal {
public name: string;
public numberOfLegs: number = 4;
public aboutMe: string;
constructor(theName: string) {
this.name = theName;
if (this.constructor === Animal) {
console.log("In Animal is ");
this.init();
} else {
console.log("Skipping Animal init() because inherited");
}
}
init() {
console.log("the Animal init() called");
this.aboutMe = `I'm ${this.name} with ${this.numberOfLegs} legs`;
}
}

class Bird extends Animal {
public numberOfLegs: number = 2;
constructor(theName: string) {
super(theName);
if (this.constructor === Bird) {
console.log("In Bird is ");
this.init();
} else {
console.log("Skipping Bird init() because inherited");
}
}
init() {
super.init();
console.log("and also some additionals in the Bird init() called");
}
}

class CityBird extends Bird {
public numberOfLegs: number = 1;
constructor(theName: string) {
super(theName);
if (this.constructor === CityBird) {
console.log("In CityBird is ");
this.init();
} else {
console.log("Skipping CityBird init() because inherited");
}
}
init() {
super.init();
console.log("and also some additionals in the CityBird init() called");
}
}

var bird = new CityBird('Bimbo');
console.log(bird.aboutMe);
*/
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var Animal = (function () {
function Animal(theName) {
this.numberOfLegs = 4;
this.name = theName;
if (this.constructor === Animal) {
console.log("In Animal is ");
this.init();
}
else {
console.log("Skipping Animal init() because inherited");
}
}
Animal.prototype.init = function () {
console.log("the Animal init() called");
this.aboutMe = "I'm " + this.name + " with " + this.numberOfLegs + " legs";
};
return Animal;
}());
var Bird = (function (_super) {
__extends(Bird, _super);
function Bird(theName) {
var _this = _super.call(this, theName) || this;
_this.numberOfLegs = 2;
if (_this.constructor === Bird) {
console.log("In Bird is ");
_this.init();
}
else {
console.log("Skipping Bird init() because inherited");
}
return _this;
}
Bird.prototype.init = function () {
_super.prototype.init.call(this);
console.log("and also some additionals in the Bird init() called");
};
return Bird;
}(Animal));
var CityBird = (function (_super) {
__extends(CityBird, _super);
function CityBird(theName) {
var _this = _super.call(this, theName) || this;
_this.numberOfLegs = 1;
if (_this.constructor === CityBird) {
console.log("In CityBird is ");
_this.init();
}
else {
console.log("Skipping CityBird init() because inherited");
}
return _this;
}
CityBird.prototype.init = function () {
_super.prototype.init.call(this);
console.log("and also some additionals in the CityBird init() called");
};
return CityBird;
}(Bird));
var bird = new CityBird('Bimbo');
console.log(bird.aboutMe);

这个解决方案也可以与新的 ES6 class 语法一起使用,它在类定义中强制使用严格模式,因此禁止使用 callee:

class Animal {
constructor (theName) {
this.name = theName;
this.numberOfLegs = 4;
if (this.constructor === Animal) {
console.log("In Animal is ");
this.init();
} else {
console.log("Skipping Animal init() because inherited");
}
}
init() {
console.log("the Animal init() called");
this.aboutMe = "I'm " + this.name + " with " + this.numberOfLegs + " legs";
}
}

class Bird extends Animal {
constructor (theName) {
super(theName);
this.numberOfLegs = 2;
if (this.constructor === Bird) {
console.log("In Bird is ");
this.init();
} else {
console.log("Skipping Bird init() because inherited");
}
}
init() {
super.init();
console.log("and also some additionals in the Bird init() called");
}
}

class CityBird extends Bird {
constructor (theName) {
super(theName);
this.numberOfLegs = 1;
if (this.constructor === CityBird) {
console.log("In CityBird is ");
this.init();
} else {
console.log("Skipping CityBird init() because inherited");
}
}
init() {
super.init();
console.log("and also some additionals in the CityBird init() called");
}
}

var bird = new CityBird('Bimbo');
console.log(bird.aboutMe);

最佳答案

最简单的解决方案是从两个构造函数调用 init

/*
class Animal {
public name: string;
public numberOfLegs: number = 4;
public aboutMe: string;
constructor(theName: string) {
this.name = theName;
this.init();
}
init() {
console.log("init called");
this.aboutMe = `I'm ${this.name} with ${this.numberOfLegs} legs`;
}
}

class Bird extends Animal {
public name: string;
public numberOfLegs: number = 2;
constructor(theName: string) {
super(theName);
this.init();
}
}

var bird = new Bird('Bimbo');
console.log(bird.aboutMe);
*/
var __extends = (this && this.__extends) || (function() {
var extendStatics = Object.setPrototypeOf ||
({
__proto__: []
}
instanceof Array && function(d, b) {
d.__proto__ = b;
}) ||
function(d, b) {
for (var p in b)
if (b.hasOwnProperty(p)) d[p] = b[p];
};
return function(d, b) {
extendStatics(d, b);

function __() {
this.constructor = d;
}
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var Animal = (function() {
function Animal(theName) {
this.numberOfLegs = 4;
this.name = theName;
this.init();
}
Animal.prototype.init = function() {
console.log("init called");
this.aboutMe = "I'm " + this.name + " with " + this.numberOfLegs + " legs";
};
return Animal;
}());
var Bird = (function(_super) {
__extends(Bird, _super);

function Bird(theName) {
var _this = _super.call(this, theName) || this;
_this.numberOfLegs = 2;
_this.init();
return _this;
}
return Bird;
}(Animal));
var bird = new Bird('Bimbo');
console.log(bird.aboutMe);

JavaScript 与其他 OO 语言不同,您必须遵守原型(prototype)链及其隐含的固有对象创建规则。

如果你需要测试继承,你可以在你的基类中添加一个静态属性,然后简单地测试 caller 是否继承了这个静态属性:

/*
class Animal {
static _isInheritable = true;
public name: string;
public numberOfLegs: number = 4;
public aboutMe: string;
constructor(theName: string) {
this.name = theName;

var isInheirited = (arguments.callee.caller != null ? arguments.callee.caller._isInheritable != void 0 : false);
if (!isInheirited) {
this.init();
} else {
console.log("Skipped because inherited");
}
}
init() {
console.log("init called");
this.aboutMe = `I'm ${this.name} with ${this.numberOfLegs} legs`;
}
}

class Bird extends Animal {
public name: string;
public numberOfLegs: number = 2;
constructor(theName: string) {
super(theName);

var isInheirited = (arguments.callee.caller != null ? arguments.callee.caller._isInheritable != void 0 : false);
if (!isInheirited) {
this.init();
}
}
}

var bird = new Bird('Bimbo');
console.log(bird.aboutMe);
*/

var __extends = (this && this.__extends) || (function() {
var extendStatics = Object.setPrototypeOf ||
({
__proto__: []
}
instanceof Array && function(d, b) {
d.__proto__ = b;
}) ||
function(d, b) {
for (var p in b)
if (b.hasOwnProperty(p)) d[p] = b[p];
};
return function(d, b) {
extendStatics(d, b);

function __() {
this.constructor = d;
}
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var Animal = (function() {
function Animal(theName) {
this.numberOfLegs = 4;
this.name = theName;
var isInheirited = (arguments.callee.caller != null ? arguments.callee.caller._isInheritable != void 0 : false);
if (!isInheirited) {
this.init();
} else {
console.log("Skipped because inherited");
}
}
Animal.prototype.init = function() {
console.log("init called");
this.aboutMe = "I'm " + this.name + " with " + this.numberOfLegs + " legs";
};
return Animal;
}());
Animal._isInheritable = true;
var Bird = (function(_super) {
__extends(Bird, _super);

function Bird(theName) {
var _this = _super.call(this, theName) || this;
_this.numberOfLegs = 2;
var isInheirited = (arguments.callee.caller != null ? arguments.callee.caller._isInheritable != void 0 : false);
if (!isInheirited) {
_this.init();
}
return _this;
}
return Bird;
}(Animal));
var bird = new Bird('Bimbo');
console.log(bird.aboutMe);

关于Javascript:类实例初始化与继承,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44109220/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com