Home >Web Front-end >JS Tutorial >Detailed explanation of the characteristics and usage examples of Javascript decorator pattern
Decorator Pattern: Dynamically extend object functions without changing the original class and inheritance, and implement a new object with the same interface of the original object by wrapping an object.
Features of the decorator pattern:
1. Add functionality without changing the original structure of the original object.
2. The decorated object and the original object have the same interface, which allows customers to use the decorated object in the same way as the original object.
3. The decoration object contains a reference to the original object, that is, the decoration object is a packaged object of the real original object.
Detailed explanation of Javascript decorator pattern:
In the decorator pattern, additional functions can be dynamically added to the object at runtime. This can be a challenge when dealing with static classes. In Javascript, since objects are mutable, the process of adding functionality to an object is not a problem in itself.
One of the more convenient features of the decorator pattern is the customizable and configurable nature of its expected behavior. You can start with a normal object with only some basic functionality, then choose from the pool of available decoration resources which features you need to enhance the normal object, and decorate them in order, especially when the order of decoration is important.
One way to implement the decorator pattern is to make each decorator an object, and the object contains methods that should be overloaded. Each decorator actually inherits the object that has been enhanced by the previous decorator. Each decorator method calls the same method on the "inherited object" and gets its value, plus it continues to perform some operations.
Let’s start with Example 1:
//需要装饰的类(函数) function Macbook() { this.cost = function () { return 1000; }; } //计算商品的包装费 function PackagingFee(macbook) { this.cost = function () { return macbook.cost() + 75; }; } //计算商品的运费 function Freight(macbook) { this.cost = function () { return macbook.cost() + 300; }; } //计算商品的保险费用 function Insurance(macbook) { this.cost = function () { return macbook.cost() + 250; }; } // 用法 var myMacbook = new Insurance(new Freight(new PackagingFee(new Macbook()))); console.log(myMacbook.cost());//1625
Let’s briefly analyze the above code. In the above code, a total of four functions are defined (one of which Functions that need to be modified, three functions for modification).
Then, declare a variable myMacbook to point to the new Insurance object. The formal parameters of the Insurance object point to the new Freight object. The formal parameters of the Freight object point to the new PackagingFee object. The formal parameters of the PackagingFee object point to new. out of the Macbook object.
Next, call the cost method of myMacbook. From the above analysis, we can conclude that the value of myMacbook.cost() is equal to (the cost method of the Freight object + 250), the cost method of the Freight object is equal to (the cost method of the PackagingFee object + 300), and the cost method of the PackagingFee object is equal to ( Macbook object's cost method +75).
So the final result is: the value of myMacbook.cost() = 250 + (300 + (75 + 1000)) = 1625.
// 用法 var myMacbook = new Insurance(new Freight(new PackagingFee(new Macbook()))); console.log(myMacbook.cost());//1625 //上面的代码等价于下面拆分后的代码,或许拆分后代码你更能看出前后的逻辑性 var macbook = new Macbook(); var package = new PackagingFee(macbook); var freight = new Freight(package); var myMacbook = new Insurance(freight); //当然,如果你不想声明这么多变量(macbook、package、freight),只用一个变量也是可以的 var macbook = new Macbook(); macbook = new PackagingFee(macbook); macbook = new Freight(macbook); var myMacbook = new Insurance(macbook);
Look at Example 2 again:
function ConcreteClass() { this.performTask = function () { this.preTask(); console.log('doing something'); this.postTask(); }; } function AbstractDecorator(decorated) { this.performTask = function () { decorated.performTask(); }; } function ConcreteDecoratorClass(decorated) { this.base = AbstractDecorator; this.base(decorated);// add performTask method decorated.preTask = function () { console.log('pre-calling..'); }; decorated.postTask = function () { console.log('post-calling..'); }; } var concrete = new ConcreteClass(); var decorator1 = new ConcreteDecoratorClass(concrete); decorator1.performTask(); //pre-calling.. //doing something //post-calling..
Instance 2 is actually very similar to Example 1. Let’s briefly analyze it. First, three functions are defined in Example 2, then two variables concrete and decorator1 are declared, and finally the performTask method of decorator1 is called.
At a glance, it seems that there is no performTask method in ConcreteDecoratorClass. Let’s first analyze the following two lines of code:
var concrete = new ConcreteClass(); //声明一个变量concrete指向new出来的ConcreteClass对象 var decorator1 = new ConcreteDecoratorClass(concrete); //声明一个变量decorator1指向new出来的ConcreteDecoratorClass对象,并传入变量concrete作为形参
Then, let’s analyze the code in the ConcreteDecoratorClass function line by line:
this.base = AbstractDecorator; //定义一个当前对象(decorator1)的base属性,并指向函数AbstractDecorator this.base(decorated); //调用base属性指向的函数,也就是调用AbstractDecorator函数,同时传入形参decorated,形参decorated指向new出来的ConcreteClass对象
Speaking of this, it seems that there is still a performTask method in the ConcreteDecoratorClass function. The key point is to look at "this"!
This in the ConcreteDecoratorClass function points to the new ConcreteDecoratorClass object (that is, it points to the same object as decorator1);
The key to this in the AbstractDecorator function is to see which object calls this function, this Which object points to (from the code "this.base = AbstractDecorator; this.base(decorated);" we can see that the new ConcreteDecoratorClass object is calling the AbstractDecorator function), so in the AbstractDecorator function this points to the new ConcreteDecoratorClass object (also points to the same object as decorator1).
To summarize, we will find that in the above code, Whether it is this in the ConcreteDecoratorClass function or this in the AbstractDecorator function, they all point to the new ConcreteDecoratorClass object.
So, when we execute decorator1.performTask(), it will continue to execute the code in the anonymous function (decorated.performTask();), and the decorated formal parameter in the anonymous function points to new. ConcreteClass object and execute the object's performTask method.
Finally take a look at Example 3:
var tree = {}; tree.decorate = function () { console.log('Make sure the tree won\'t fall'); }; tree.getDecorator = function (deco) { tree[deco].prototype = this; return new tree[deco]; }; tree.RedApples = function () { this.decorate = function () { this.RedApples.prototype.decorate(); // 第7步:先执行原型(这时候是Angel了)的decorate方法 console.log('Add some red apples'); // 第8步 再输出 red // 将这2步作为RedApples的decorate方法 } }; tree.BlueApples = function () { this.decorate = function () { this.BlueApples.prototype.decorate(); // 第1步:先执行原型的decorate方法,也就是tree.decorate() console.log('Put on some blue apples'); // 第2步 再输出blue // 将这2步作为BlueApples的decorate方法 } }; tree.Angel = function () { this.decorate = function () { this.Angel.prototype.decorate(); // 第4步:先执行原型(这时候是BlueApples了)的decorate方法 console.log('An angel on the top'); // 第5步 再输出angel // 将这2步作为Angel的decorate方法 } }; tree = tree.getDecorator('BlueApples'); // 第3步:将BlueApples对象赋给tree,这时候父原型里的getDecorator依然可用 tree = tree.getDecorator('Angel'); // 第6步:将Angel对象赋给tree,这时候父原型的父原型里的getDecorator依然可用 tree = tree.getDecorator('RedApples'); // 第9步:将RedApples对象赋给tree tree.decorate(); // 第10步:执行RedApples对象的decorate方法 //Make sure the tree won't fall //Add blue apples //An angel on the top //Put on some red apples
Example 3 looks very complicated. In fact, the analysis logic is still the same as the previous two examples. We can see In Example 3, a total of 5 function expressions are declared. Let’s focus on analyzing the following code:
//tree.getDecorator('BlueApples')返回new出来的tree.BlueApples的实例对象,并将该对象赋值给空的tree对象 tree = tree.getDecorator('BlueApples'); //new出来的tree.BlueApples的实例对象的原型指向 --> 空对象tree //tree.getDecorator('Angel')返回new出来的tree.Angel的实例对象(这行代码中的第二个tree已经是上面一行代码运行结果后的tree.BlueApples的实例对象) tree = tree.getDecorator('Angel'); //new出来的tree.Angel的实例对象的原型指向 --> tree.BlueApples的实例对象 //tree.getDecorator('RedApples')返回new出来的tree.RedApples的实例对象(这行代码中的第二个tree已经是上面一行代码运行结果后的tree.Angel的实例对象) tree = tree.getDecorator('RedApples'); //new出来的tree.RedApples的实例对象的原型指向 --> tree.Angel的实例对象 //调用tree.decorate(),这里的tree已经是new出来的tree.RedApples的实例对象了。 //tree.RedApples的实例对象的decorate属性方法里面的第一行代码是 “this.RedApples.prototype.decorate()” //结合上面的分析可以得出以下的原型链结构: //this.RedApples.prototype --> tree.Angel; //tree.Angel.prototype --> tree.BlueApples; //tree.BlueApples.prototype --> 空对象tree tree.decorate();
After analyzing this, it is not difficult to know the final output result.
The above is the detailed content of Detailed explanation of the characteristics and usage examples of Javascript decorator pattern. For more information, please follow other related articles on the PHP Chinese website!