在 ES6 中自动将参数设置为实例属性 [英] Automatically set arguments as instance properties in ES6

查看:18
本文介绍了在 ES6 中自动将参数设置为实例属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

CoffeeScript 会自动将参数设置为构造函数中的实例属性,如果您为参数添加 @ 前缀.

CoffeeScript automatically sets the arguments as instance properties in the constructor if you prefix the arguments with @.

有什么技巧可以在 ES6 中实现相同的功能吗?

Is there any trick to accomplish the same in ES6?

推荐答案

Legacy support script

我已经扩展了 Function 原型,以允许所有构造函数访问参数自动采用.我知道我们应该避免向全局对象添加功能,但如果你知道你在做什么可以没问题.

Legacy support script

I've extended Function prototype to give access to parameter auto-adoption to all constructors. I know we should be avoiding adding functionality to global objects but if you know what you're doing it can be ok.

所以这是 adoptArguments 函数:

var comments = /((//.*$)|(/*[sS]*?*/))/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];
    }
};

采用所有构造函数调用参数的结果调用现在如下:

The resulting call that adopts all constructor call arguments is now as follows:

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 标识符是 按规范非法.但是您可以在它们前面加上诸如 $_ 之类的前缀,这在您的情况下可能是可行的.所以现在你要做的就是检测这个特定的命名约定,并只添加那些通过这个检查的参数:

Adopting only specific arguments

My upper solution adopts all function arguments as instantiated object members. But as you're referring to CoffeeScript you're trying to adopt just selected arguments and not all. In Javascript identifiers starting with @ are illegal by specification. But you can prefix them with something else like $ or _ which may be feasible in your case. So now all you have to do is detect this specific naming convention and only add those arguments that pass this check:

var comments = /((//.*$)|(/*[sS]*?*/))/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];
        }
    }
};

完成.也适用于严格模式.现在您可以定义带前缀的构造函数参数并将它们作为实例化的对象成员进行访问.

Done. Works in strict mode as well. Now you can define prefixed constructor parameters and access them as your instantiated object members.

实际上,我编写了一个更强大的版本,带有以下签名,这意味着它具有额外的功能,并且适用于我在我的 AngularJS 应用程序中创建控制器/服务/等的场景.构造函数并为其添加额外的原型函数.由于构造函数中的参数是由 AngularJS 注入的,我需要在所有控制器函数中访问这些值,我可以通过 this.injections.xxx 简单地访问它们.使用此函数比编写几行额外要简单得多,因为可能有许多注入.更不用说注射的变化了.我只需要调整构造函数参数,然后立即将它们传播到 this.injections 中.

Actually I've written an even more powerful version with following signature that implies its additional powers and is suited for my scenario in my AngularJS application where I create controller/service/etc. constructors and add additional prototype functions to it. As parameters in constructors are injected by AngularJS and I need to access these values in all controller functions I can simply access them, via this.injections.xxx. Using this function makes it much simpler than writing several additional lines as there may be many many injections. Not to even mention changes to injections. I only have to adjust constructor parameters and I immediately get them propagated inside this.injections.

无论如何.承诺的签名(不包括实现).

Anyway. Promised signature (implementation excluded).

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 可在视图中自动访问.

I exclude $scope parameter injection as it should be data only without behaviour compared to services/providers etc. In my controllers I always assign $scope to this.model member even though I wouldn't even have to as $scope is automatically accessible in view.

这篇关于在 ES6 中自动将参数设置为实例属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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