Javascript:类实例初始化和继承 [英] Javascript: class instance initialization and inheritance

查看:160
本文介绍了Javascript:类实例初始化和继承的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  class动物{
name:string;
numberOfLegs:number = 4;
aboutMe:string;
构造函数(名称:字符串){
this.name = theName;
this.init();
}
init(){
this.aboutMe =`我是$ {this.name},带有$ {this.numberOfLegs} leg`;
}
}

class Bird extends Animal {
numberOfLegs:number = 2;
构造函数(名称:字符串){
super(theName);
}
}

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

正确的预期价值 bird.aboutMe 将是我是两条腿的Bimbo ,但实际上你会得到我是Bimbo有4条腿 。当您将上述TypeScript代码编译为纯JavaScript 此处时,很明显为什么这样做不正确。



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



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

  class Animal {
protected $ name;
protected $ numberOfLegs = 4;
public $ aboutMe;
public function __construct($ theName){
$ this-> name = $ theName;
$ this-> init();
}
受保护的函数init(){
$ this-> aboutMe =我是{$ this-> name} {{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代码回应的结果是我是Bimbo with 2双腿



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



也许由于TypeScript我会添加如果TypeScript试图看起来像C风格的类定义那么它会非常有意义,它也会像说,。有没有办法实现这个目标?



编辑2:提出了非常好的通用解决方案下面是EmilS.Jørgensen。即使在较长的继承链(例如 Bird extends Animal CityBird extends Bird )的情况下,这也适用。我在他的答案中添加了一些代码,以表明在每个级别上你可以重用父(超级)类 init()并在需要时添加你自己的初始化逻辑: / p>

  / * class Animal {static _isInheritable = true;公共名称:string; public numberOfLegs:number = 4; public aboutMe:string;构造函数(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(跳过动物init()因为继承了); init(){console.log(动物init()调用); this.aboutMe =`我是$ {this.name},带有$ {this.numberOfLegs} leg`; Bird扩展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(跳过Bird init()因为继承了); init(){super.init(); console.log(还有Bird init()中的一些附加内容称为); CityBird扩展了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(在CityBird中是); this.init(); } else {console.log(跳过CityBird init()因为继承了); init(){super.init(); console.log(以及CityBird init()中的一些附加内容称为); 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(跳过动物init()因为继承了);}} Animal.prototype.init = function(){console.log(动物init()调用); this.aboutMe =我是+ this.name +和+ this.numberOfLegs +legs;}; return Animal;}()); Animal._isInheritable = true; var Bird =(function(_super){__extends(Bird, _super);函数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(跳过Bird init()因为继承了); } return _this; } Bird.prototype.init = function(){_ super.prototype.init.call(this); console.log(还有Bird init()中的一些附加内容称为); }; 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(跳过CityBird init()因为继承了);} return _this;} CityBird.prototype.init = function(){_ supers.prototype.init.call(this); console.log (以及CityBird init()中的一些附加内容称为);};返回CityBird;}(Bird)); var bird = new CityBird('Bimbo'); console.log(bird.aboutMe);  



这个解决方案的缺点是你不能在<$ c $中使用它c>'use strict'模式为调用者被调用者,以及在严格模式下可能无法访问 arguments 属性。

解决方案

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



  / * class Animal {public name:string; public numberOfLegs:number = 4; public aboutMe:string;构造函数(theName:string){this.name = theName; this.init(); } init(){console.log(init called); this.aboutMe =`我是$ {this.name},带有$ {this.numberOfLegs} leg`; Bird扩展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 .__ 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 =我是+ 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 =新鸟('Bimbo'); console.log(bird.aboutMe);  



JavaScript与其他OO语言不同,您必须尊重原型链,以及它所暗示的固有对象创建规则。



如果您需要测试对于继承,您可以向基类添加静态属性,并只测试调用者是否继承了所述静态属性:



  / * class Animal {static _isInheritable = true;公共名称:string; public numberOfLegs:number = 4; public aboutMe:string;构造函数(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因为继承了); init(){console.log(init called); this.aboutMe =`我是$ {this.name},带有$ {this.numberOfLegs} leg`; Bird扩展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 = 我是+ this.name +with+ this.numberOfLegs +legs;}; return Animal;}()); Animal._isInheritable = tru e; var Bird =(function(_super){__extends(Bird,_super); function Bird(theName){var _this = _super.call(this,theName)||这个; _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);  


Here is an example of class Animal and its child class Bird definition in JavaScript (using 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);

The correct expected value of property bird.aboutMe would be I'm Bimbo with 2 legs, but in reality you will get I'm Bimbo with 4 legs. When you compile the above TypeScript code into pure JavaScript here it is quite obvious why this works incorrectly.

My question: How to properly write initialization logic of JavaScript classes so that it works also for inheritance and in a manner as we are used to in other OO languages? TypeScript tries to resolve this gap between JavaScript and other OO languages, but even in such an trivial case it fails. Am I missing something?

Just to prove that my expectation of correct result is valid I have rewritten the above code to 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;

The result echoed by the above PHP code is I'm Bimbo with 2 legs

EDIT 1: Of course I know how to make the above code work correctly. My need is not to make this trivial code work but to get a way to treat JS class instance initialization in such manner that it works correctly also in complex cases.

And maybe on account of TypeScript I would add "If TypeScript tries to look like C-style class definition then it would be highly appreciable that it also works like that". Is there a way to achieve this?

EDIT 2: Very nice general solution is proposed here below by Emil S. Jørgensen. This works even in case of longer inheritance chain (e.g. Bird extends Animal and CityBird extends Bird). I have added some more code to his answer to show that on each level you can reuse the parent (super) class init() and add your own initialization logic if needed:

/*
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);

Drawback of this solution is that you cannot to use it in 'use strict' mode as caller, callee, and arguments properties may not be accessed on strict mode.

解决方案

The easiest solution would be to call init from both constructors.

/*
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 isn't like other OO languages in that you must respect the prototype chain, and the inherent object creation rules it implies.

If you need to test for inheritance, you can add a static property to your base class, and simply test if caller has inherited said static property:

/*
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:类实例初始化和继承的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆