Ответ 1
Поддержка устаревших script
Я расширил прототип Function
, чтобы предоставить доступ к автоматическому внедрению параметров ко всем конструкторам. Я знаю, что нам следует избегать добавления функциональности к глобальным объектам, но если вы знаете, что делаете, это может быть нормально.
Итак, здесь функция adoptArguments
:
var comments = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/g;
var parser = /^function[^\(]*\(([^)]*)\)/i;
var splitter = /\s*,\s*/i;
Function.prototype.adoptArguments = function(context, values) {
/// <summary>Injects calling constructor function parameters as constructed object instance members with the same name.</summary>
/// <param name="context" type="Object" optional="false">The context object (this) in which the the calling function is running.</param>
/// <param name="values" type="Array" optional="false">Argument values that will be assigned to injected members (usually just provide "arguments" array like object).</param>
"use strict";
// only execute this function if caller is used as a constructor
if (!(context instanceof this))
{
return;
}
var args;
// parse parameters
args = this.toString()
.replace(comments, "") // remove comments
.match(parser)[1].trim(); // get comma separated string
// empty string => no arguments to inject
if (!args) return;
// get individual argument names
args = args.split(splitter);
// adopt prefixed ones as object instance members
for(var i = 0, len = args.length; i < len; ++i)
{
context[args[i]] = values[i];
}
};
Итоговый вызов, который принимает все аргументы вызова конструктора, теперь выглядит следующим образом:
function Person(firstName, lastName, address) {
// doesn't get simpler than this
Person.adoptArguments(this, arguments);
}
var p1 = new Person("John", "Doe");
p1.firstName; // "John"
p1.lastName; // "Doe"
p1.address; // undefined
var p2 = new Person("Jane", "Doe", "Nowhere");
p2.firstName; // "Jane"
p2.lastName; // "Doe"
p2.address; // "Nowhere"
Принятие только определенных аргументов
Мое верхнее решение принимает все аргументы функции в качестве экземпляров объектов-экземпляров. Но поскольку вы имеете в виду CoffeeScript, вы пытаетесь принять только выбранные аргументы и не все. В идентификаторах Javascript, начинающихся с @
, нелегально по спецификации. Но вы можете прикрепить их к чему-то еще, например, $
или _
, которые могут быть осуществлены в вашем случае. Итак, теперь вам нужно только определить это конкретное соглашение об именах и добавить только те аргументы, которые проходят эту проверку:
var comments = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/g;
var parser = /^function[^\(]*\(([^)]*)\)/i;
var splitter = /\s*,\s*/i;
Function.prototype.adoptArguments = function(context, values) {
/// <summary>Injects calling constructor function parameters as constructed object instance members with the same name.</summary>
/// <param name="context" type="Object" optional="false">The context object (this) in which the the calling function is running.</param>
/// <param name="values" type="Array" optional="false">Argument values that will be assigned to injected members (usually just provide "arguments" array like object).</param>
"use strict";
// only execute this function if caller is used as a constructor
if (!(context instanceof this))
{
return;
}
var args;
// parse parameters
args = this.toString()
.replace(comments, "") // remove comments
.match(parser)[1].trim(); // get comma separated string
// empty string => no arguments to inject
if (!args) return;
// get individual argument names
args = args.split(splitter);
// adopt prefixed ones as object instance members
for(var i = 0, len = args.length; i < len; ++i)
{
if (args[i].charAt(0) === "$")
{
context[args[i].substr(1)] = values[i];
}
}
};
Готово. Работает и в строгом режиме. Теперь вы можете определить префиксные параметры конструктора и получить к ним доступ в качестве ваших созданных объектов.
Расширенная версия для сценария AngularJS
На самом деле я написал еще более мощную версию со следующей подписью, которая подразумевает ее дополнительные полномочия и подходит для моего сценария в моем приложении AngularJS, где я создаю контроллер/сервис/и т.д. конструкторы и добавить к нему дополнительные функции прототипа. Поскольку параметры в конструкторах вводятся AngularJS, и мне нужно получить доступ к этим значениям во всех функциях контроллера, я могу просто получить к ним доступ через this.injections.xxx
. Использование этой функции делает ее намного проще, чем писать несколько дополнительных строк, так как может быть много инъекций. Не говоря уже об изменениях в инъекциях. Мне нужно только отрегулировать параметры конструктора, и я сразу же их распространяю внутри this.injections
.
В любом случае. Обещанная подпись (исключение реализации).
Function.prototype.injectArguments = function injectArguments(context, values, exclude, nestUnder, stripPrefix) {
/// <summary>Injects calling constructor function parameters into constructed object instance as members with same name.</summary>
/// <param name="context" type="Object" optional="false">The context object (this) in which the calling constructor is running.</param>
/// <param name="values" type="Array" optional="false">Argument values that will be assigned to injected members (usually just provide "arguments" array like object).</param>
/// <param name="exclude" type="String" optional="true">Comma separated list of parameter names to exclude from injection.</param>
/// <param name="nestUnder" type="String" optional="true">Define whether injected parameters should be nested under a specific member (gets replaced if exists).</param>
/// <param name="stripPrefix" type="Bool" optional="true">Set to true to strip "$" and "_" parameter name prefix when injecting members.</param>
/// <field type="Object" name="defaults" static="true">Defines injectArguments defaults for optional parameters. These defaults can be overridden.</field>
{
...
}
Function.prototype.injectArguments.defaults = {
/// <field type="String" name="exclude">Comma separated list of parameter names that should be excluded from injection (default "scope, $scope").</field>
exclude: "scope, $scope",
/// <field type="String" name="nestUnder">Member name that will be created and all injections will be nested within (default "injections").</field>
nestUnder: "injections",
/// <field type="Bool" name="stripPrefix">Defines whether parameter names prefixed with "$" or "_" should be stripped of this prefix (default <c>true</c>).</field>
stripPrefix: true
};
Я исключаю инъекцию параметра $scope
, так как это должны быть данные только без поведения по сравнению с службами/поставщиками и т.д. В моих контроллерах я всегда назначаю $scope
в this.model
, хотя мне даже не понадобилось бы как $scope
автоматически открывается.