1. Background
Combinatorial inheritance is the most commonly used inheritance in JS, which is more likely to be encountered in actual projects. Recently, I encountered this on a project, but I didn’t recognize it at first. It was strange to see code that involved composite inheritance, and my gut told me there was a design pattern. Open the little Red Book prototype chain chapter, as expected appeared four characters [combination inheritance]. This baby likes to talk about things, so we are going to talk about combination inheritance with project practice.
A form consists of several controls, such as text controls, date controls, etc. These controls all have similar behaviors, such as reading and writing data, but the details of reading or writing are different. For example, date control should be processed according to format when reading or writing data, and text control has requirements on the length of text written. But no matter what control, some behaviors are the same such as checking whether the control is null.
In such a scenario, you can define the general operations of the control on the base class of the control, and subclasses can override them based on their own situation.
2. Design ideas
2.1 Definition of base class controls
function BaseControl(options) {
this.controlKey = options.controlkey;
this.dataField = options.dataField;
}
BaseControl.prototype.$getValue = () = >{}; BaseControl.prototype.$setValue =() = >{};Copy the code
2.2 Definition of text controls
function FormTextBox() {
}
FormTextBox.prototype.$getValue = function getValue(val) {
return this.filter(val);
}
FormTextBox.prototype.$setValue = function setValue(val) {
if (val.length > 2000) {
return val.substr(0.2000);
} else {
returnval; }}// Filter sensitive words
FormTextBox.prototype.$filter = function filter(value) {... }Copy the code
2.3 Definition of factory function
function controlFactory(formInstance, updateView) {
function IBaseControl() {}
IBaseControl.prototype = {
formInstance: formInstance, // Form instance
updateView: updateView, // View update
}
function IControl() {}
const controlProto = new IBaseControl();
IControl.prototype = Object.assign(controlProto, BaseControl.prototype);
return function impControl(options) {
function IFormControl() {
// Borrow the constructor
BaseControl.call(this, options);
// Pay attention to the call order
FormTextBox.call(this, options);
}
const formControlProto = new IControl();
IFormControl.prototype = Object.assign(formControlProto,FormTextBox.prototype);
return newIFormControl(options); }}Copy the code
2.4 Borrow constructors
function IFormControl() {
// Borrow the constructor
BaseControl.call(this, options);
// Pay attention to the call order
FormTextBox.call(this, options);
}
Copy the code
This function is the core function. The BaseControl constructor and the FormTextBox constructor are called inside the IFormControl constructor. Notice that the function context is the function context of IFormControl, and the BaseControl constructor and FormTextBox constructor are just plain old functions. By borrowing constructors, IFormControl has its own copy of the data attributes to complete property inheritance from the parent.
2.5 Analysis of prototype chain
The prototype object for IFormControl is an extended IControl instance
const formControlProto = new IControl();
IFormControl.prototype = Object.assign(formControlProto,FormTextBox.prototype)
Copy the code
The IControl prototype object is an extended instance of IBaseControl
const controlProto = new IBaseControl();
IControl.prototype = Object.assign(controlProto, BaseControl.prototype);
Copy the code
The prototype object for IBaseControl is an object that has a form instance and a form update method
IBaseControl.prototype = {
formInstance: formInstance, // Form instance
updateView: updateView, // View update
}
Copy the code
So IFormControl -> IControl -> IBaseControl -> {formInstance, updateView}
Test cases
describe('Combinatorial inheritance'.() = > {
const formInstance = {};
const updateView = () = > {};
const options = {
controlkey: 'FormTextBox'.dataField: 'F00002'.visible: true};const impControl = controlFactory(formInstance, updateView);
const formTextBox = impControl(options);
test('formTextBox not instanceof FormTextBox'.() = > {
expect(formTextBox instanceof FormTextBox).toBeFalsy();
});
test('formTextBox not instanceof BaseControl'.() = > {
expect(formTextBox instanceof BaseControl).toBeFalsy();
});
});
Copy the code
4. Conclusion analysis
The newly created text control instance formTextBox is of type neither formTextBox nor BaseControl. The formTextBox is of type IFormControl -> IControl -> IBaseControl.
5. Theoretical endorsement
Borrow constructor: Calls the supertype constructor inside the subtype constructor.
The idea of composite inheritance is to use the stereotype chain to achieve inheritance of stereotype attributes and methods, and to achieve inheritance of instance attributes by borrowing constructors.
6. Reference materials
- JavaScript Advanced Programming
7. Reference code
Prototype inheritance