Dieser Artikel vermittelt Ihnen relevante Kenntnisse über die Prinzipien von Dekoratoren in Javascript. Der offensichtlichste Unterschied besteht darin, dass die Dekoratoren in den meisten Sprachen eine Zeile sein müssen sind getrennt, während der js-Dekorator in einer Zeile stehen kann. Ich hoffe, dass dies für alle hilfreich ist.
Ein beschreibendes Wort, das mit @ beginnt. Das englische Verb „decorator“ lautet „decorieren“, was „dekorieren“ bedeutet. Die Wurzel dek (ausgesprochen dec) bedeutet in der proto-indogermanischen Sprache „Akzeptanz“. Das heißt, etwas Neues wird zu etwas Neuem (und wird besser).
Aus einer anderen Perspektive beschrieben arbeiten Dekorateure hauptsächlich an der Außenseite des dekorierten Objekts, anstatt in Veränderungen im Inneren einzudringen. Der Dekorationsmodus ist ebenfalls ein Entwicklungsmodus, obwohl sein Status schwächer ist als der von MVC, IoC usw., ist er dennoch ein ausgezeichneter Modus.
JavaScript-Dekoratoren können von Python oder Java ausgeliehen werden. Der offensichtlichere Unterschied besteht darin, dass die Dekoratoren in den meisten Sprachen Zeile für Zeile getrennt werden müssen, während die Dekoratoren in js in einer Zeile stehen können.
Zum Beispiel: Ich habe mit meiner Mitarbeiterkarte das Gebäude der Firmenzentrale betreten. Da jeder Mitarbeiter einer anderen Abteilung und Ebene angehört, kann er keinen Raum im Gebäude betreten. Jeder Raum hat eine Tür; dann muss das Unternehmen mindestens eine Person in jedem Büro beauftragen, Besucher zu überprüfen:
Registrieren Sie den Besucher zuerst
Überprüfen Sie, ob er die Erlaubnis zum Betreten hat, und wenn nicht, fragen Sie ihn Verlassen
Erfassen Sie die Abfahrtszeit
Eine weitere Möglichkeit besteht darin, ein elektronisches Türschloss zu installieren. Das Türschloss überträgt nur die Informationen der Mitarbeiterkarte an den Computerraum, die durch ein bestimmtes Programm überprüft werden.
Nennen wir den früheren dummen Modus. Der Code lautet wie folgt:
function A101(who){ record(who,new Date(),'enter'); if (!permission(who)) { record(who,new Date(),'no permission') return void; } // 继续执行 doSomeWork(); record(who,new Date(),'leave')} function A102(who){record(who,new Date(),'enter'); if (!permission(who)) { record(who,new Date(),'no permission') return void; } // 继续执行 doSomeWork(); record(who,new Date(),'leave')} // ...
Jeder mit Erfahrung muss sofort daran gedacht haben, diese wiederholten Anweisungen in eine Methode zu kapseln und sie einheitlich aufzurufen. Ja, das löst die meisten Probleme, aber es ist nicht „elegant“ genug. Gleichzeitig gibt es ein weiteres Problem, wenn es zu viele „Räume“ gibt oder nur ungerade Räume im Gebäude überprüft werden müssen, gerade Zahlen jedoch nicht überprüft werden, wäre das nicht „unnormal“? Wenn Sie das Dekoratormuster verwenden, sieht der Code folgendermaßen aus:
@verify(who)class Building { @verify(who) A101(){/*...*/} @verify(who) A102(){/*...*/} //...}
verify ist ein Verifizierungsdekorator, dessen Kern aus einer Reihe von Funktionen besteht.
Wie im vorherigen Beispiel ist der Decorator selbst eigentlich eine Funktion, die vor der Ausführung des dekorierten Objekts ausgeführt wird.
In JavaScript sind die Arten von Dekoratoren:
Klasse
Zugriffsmethoden (Attribut get und set)
Felder
Methoden
Parameter
Seit der Dekorateurin Das Konzept befindet sich noch im Vorschlagsstadium und ist keine offiziell verfügbare JS-Funktion. Wenn Sie diese Funktion verwenden möchten, müssen Sie ein Übersetzertool wie das Babel-Tool oder TypeScript verwenden, um den JS-Code zu kompilieren, bevor er ausgeführt werden kann. Wir müssen zuerst die Betriebsumgebung einrichten und einige Parameter konfigurieren. (Der folgende Prozess setzt voraus, dass die NodeJS-Entwicklungsumgebung und die Paketverwaltungstools korrekt installiert wurden.)
cd project && npm initnpm i -D @babel/cli @babel/core @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators @babel/preset-env babel-plugin-parameter-decorator
Erstellen Sie eine .babelrc-Konfigurationsdatei wie folgt:
{ "presets": ["@babel/preset-env"], "plugins": [ ["@babel/plugin-proposal-decorators", { "legacy": true }], ["@babel/plugin-proposal-class-properties", { "loose": true }], "babel-plugin-parameter-decorator" ]}
Mit dem folgenden Konvertierungsbefehl können wir das ES5-Konvertierungsprogramm erhalten:
npx babel source.js --out-file target.js
Erstellen Sie ein JS-Programm, das Dekoratoren verwendet, decor-class.js
@classDecoratorclass Building { constructor() { this.name = "company"; }} const building = new Building(); function classDecorator(target) { console.log("target", target);}
Das Obige ist das einfachste Dekoratorprogramm, das wir für Babel verwenden In ein ES5-Programm „übersetzt“ und dann verschönert, um das folgende Programm zu erhalten.
"use strict"; var _class; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); }} var Building = classDecorator( (_class = function Building() { _classCallCheck(this, Building); this.name = "company"; }) ) || _class; var building = new Building(); function classDecorator(target) { console.log("target", target);}
第12行就是在类生成过程中,调用函数形态的装饰器,并将构造函数(类本身)送入其中。同样揭示了装饰器的第一个参数是类的构造函数的由来。
稍微修改一下代码,依旧是尽量保持最简单:
class Building { constructor() { this.name = "company"; } @methodDecorator openDoor() { console.log("The door being open"); }} const building = new Building(); function methodDecorator(target, property, descriptor) { console.log("target", target); if (property) { console.log("property", property); } if (descriptor) { console.log("descriptor", descriptor); } console.log("=====end of decorator========="); }
然后转换代码,可以发现,这次代码量突然增大了很多。排除掉_classCallCheck、_defineProperties和_createClass三个函数,关注_applyDecoratedDescriptor函数:
function _applyDecoratedDescriptor( target, property, decorators, descriptor, context) { var desc = {}; Object.keys(descriptor).forEach(function (key) { desc[key] = descriptor[key]; }); desc.enumerable = !!desc.enumerable; desc.configurable = !!desc.configurable; if ("value" in desc || desc.initializer) { desc.writable = true; } desc = decorators .slice() .reverse() .reduce(function (desc, decorator) { return decorator(target, property, desc) || desc; }, desc); if (context && desc.initializer !== void 0) { desc.value = desc.initializer ? desc.initializer.call(context) : void 0; desc.initializer = undefined; } if (desc.initializer === void 0) { Object.defineProperty(target, property, desc); desc = null; } return desc;}
它在生成构造函数之后,执行了这个函数,特别注意,这个装饰器函数是以数组形式的参数传递的。然后到上述代码的17~22行,将装饰器逐个应用,其中对装饰器的调用就在第21行。
它发送了3个参数,target指类本身。property指方法名(或者属性名),desc是可能被先前装饰器被处理过的descriptor,如果是第一次循环或只有一个装饰器,那么就是方法或属性本身的descriptor。
JS关于类的定义中,支持get和set关键字针对设置某个字段的读写操作逻辑,装饰器也同样支持这类方法的操作。
class Building { constructor() { this.name = "company"; } @propertyDecorator get roomNumber() { return this._roomNumber; } _roomNumber = ""; openDoor() { console.log("The door being open"); }}
有心的读者可能已经发现了,存取器装饰的代码与上面的方法装饰代码非常接近。关于属性 get和set方法,其本身也是一种方法的特殊形态。所以他们之间的代码就非常接近了。
继续修改源代码:
class Building { constructor() { this.name = "company"; } @propertyDecorator roomNumber = ""; } const building = new Building(); function propertyDecorator(target, property, descriptor) { console.log("target", target); if (property) { console.log("property", property); } if (descriptor) { console.log("descriptor", descriptor); } console.log("=====end of decorator========="); }
转换后的代码,还是与上述属性、存取器的代码非常接近。但除了_applyDecoratedDescriptor外,还多了一个_initializerDefineProperty函数。这个函数在生成构造函数时,将声明的各种字段绑定给对象。
参数装饰器的使用位置较之前集中装饰器略有不同,它被使用在行内。
class Building { constructor() { this.name = "company"; } openDoor(@parameterDecorator num, @parameterDecorator zoz) { console.log(`${num} door being open`); }} const building = new Building(); function parameterDecorator(target, property, key) { console.log("target", target); if (property) { console.log("property", property); } if (key) { console.log("key", key); } console.log("=====end of decorator========="); }
转换后的代码区别就比较明显了,babel并没有对其生成一个特定的函数对其进行特有的操作,而只在创建完类(构造函数)以及相关属性、方法后直接调用了开发者自己编写的装饰器函数:
var Building = /*#__PURE__*/function () { function Building() { _classCallCheck(this, Building); this.name = "company"; } _createClass(Building, [{ key: "openDoor", value: function openDoor(num, zoz) { console.log("".concat(num, " door being open")); } }]); parameterDecorator(Building.prototype, "openDoor", 1); parameterDecorator(Building.prototype, "openDoor", 0); return Building;}();
以上所有的案例,装饰器本身均没有使用任何参数。然实际应用中,经常会需要有特定的参数需求。我们再回到一开头的例子中verify(who),其中需要传入一个身份变量。哪又怎么做?我们少许改变一下类装饰器的代码:
const who = "Django";@classDecorator(who)class Building { constructor() { this.name = "company"; }}
转换后得到
// ...var who = "Django";var Building = ((_dec = classDecorator(who)), _dec( (_class = function Building() { _classCallCheck(this, Building); this.name = "company"; }) ) || _class); // ...
请注意第4第5行,它先执行了装饰器,然后再用返回值将类(构造函数)送入。相对应的,我们就应该将构造函数写成下面这样:
function classDecorator(people) { console.log(`hi~ ${people}`); return function (target) { console.log("target", target); }; }
同样的,方法、存取器、属性和参数装饰器均是如此。
到此,我们已经可以将装饰器参数与目标对象结合起来,进行一些逻辑类的操作。那么再回到文章的开头的例子中:需求中要先验证来访者权限,然后记录,最后在来访者离开时再做一次记录。此时需要监管对象方法被调用的整个过程。
请大家留意那个方法装饰器的descriptor,我们可以利用这个对象来“重写”这个方法。
class Building { constructor() { this.name = "company"; } @methodDecorator("Gate") openDoor(firstName, lastName) { return `The door will be open, when ${firstName} ${lastName} is walking into the ${this.name}.`; }} let building = new Building();console.log(building.openDoor("django", "xiang")); function methodDecorator(door) { return function (target, property, descriptor) { let fn = descriptor.value; descriptor.value = function (...args) { let [firstName, lastName] = args; console.log(`log: ${firstName}, who are comming.`); // verify(firstName,lastName) let result = Reflect.apply(fn, this, [firstName, lastName]); console.log(`log: ${result}`); console.log(`log: ${firstName}, who are leaving.`); return result; }; return descriptor; };}
代码第17行,将原方法暂存;18行定义一个新的方法,20~25行,记录、验证和记录离开的动作。
log: Django, who are comming.log: The door will be open, when Django Xiang is walking in to the company.log: Django, who are leaving.The door will be open, when Django Xiang is walking in to the company
通过阅读转换后的代码,我们知道装饰器工作的时刻是在类被实例化之前,在生成之中完成装饰函数的动作。那么,如果不同类型的多个装饰器同时作用,其过程是怎样的?我们将先前的案例全部整合到一起看看:
const who = "Django";@classDecorator(who)class Building { constructor() { this.name = "company"; } @propertyDecorator roomNumber = ""; @methodDecorator openDoor(@parameterDecorator num) { console.log(`${num} door being open`); } @accessorDecorator get roomNumber() { return this._roomNumber; }} const building = new Building(); function classDecorator(people) { console.log(`class decorator`); return function (target) { console.log("target", target); };} function methodDecorator(target, property, descriptor) { console.log("method decorator");} function accessorDecorator(target, property, descriptor) { console.log("accessor decorator");} function propertyDecorator(target, property, descriptor) { console.log("property decoator");} function parameterDecorator(target, property, key) { console.log("parameter decorator");}
class decorator
parameter decorator
property decoator
method decorator
accessor decorator
还可以通过阅读转换后的源代码得到执行顺序:
类装饰器(在最外层)
参数装饰器(在生成构造函数最里层)
按照出现的先后顺序的:属性、方法和存取器
Decorator ist ein elegantes Entwicklungsmodell, das den Codierungsprozess des Entwicklers erheblich erleichtert und die Lesbarkeit des Codes verbessert. Wenn wir Dekoratoren zur Entwicklung verwenden, ist es immer noch sehr wichtig, den Funktionsmechanismus zu verstehen.
Verwandte Empfehlungen: Javascript-Lern-Tutorial
Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung und Falleinführung des Javascript-Dekoratorprinzips. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!