Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/459.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 保持原始变量和解构同时进行的干净方法_Javascript_Ecmascript 6_Babeljs_Ecmascript Next_Ecmascript 2016 - Fatal编程技术网

Javascript 保持原始变量和解构同时进行的干净方法

Javascript 保持原始变量和解构同时进行的干净方法,javascript,ecmascript-6,babeljs,ecmascript-next,ecmascript-2016,Javascript,Ecmascript 6,Babeljs,Ecmascript Next,Ecmascript 2016,是否有一种更干净的方法来做到这一点(使用任何至少是ES草稿并具有babel插件的东西,如ES6、ES7等): 在这里,我希望将整体结果保持为一个单一对象,但同时还要对其进行分解。它在技术上是有效的,但是result是隐式声明的(使用隐式var),而我真的希望它也是一个常量 我目前正在这样做: const result = doSomething(); const { a, b } = result; 这同样有效,但有点冗长,因为我需要重复这个模式几十次 理想情况下,我想要的东西大致如下: co

是否有一种更干净的方法来做到这一点(使用任何至少是ES草稿并具有babel插件的东西,如ES6、ES7等):

在这里,我希望将整体结果保持为一个单一对象,但同时还要对其进行分解。它在技术上是有效的,但是
result
是隐式声明的(使用隐式
var
),而我真的希望它也是一个常量

我目前正在这样做:

const result = doSomething();
const { a, b } = result;
这同样有效,但有点冗长,因为我需要重复这个模式几十次

理想情况下,我想要的东西大致如下:

const { a, b } = const result = doSomething();
但这显然是一种无效的语法。

一种可能的方法:

const result = doSomething(), 
    { a, b } = result;
不过,您仍然需要复制名称<代码>常量令牌不太方便。)

想法1 创建此帮助器函数:

function use(input, callback) {
    callback(input, input);
}
const dup = input => [input, input];
然后像这样使用它:

use(doSomething(), (result, {a, b}) => {
    // Do something with result as a whole, or a and b as destructured properties.
});
例如:

use ({a: "Hello", b: "World", c: "!"}, (result, {a, b}) => {
  console.log(result);
  console.log(a);
  console.log(b);
});

// generates
// {a: "Hello", b: "World", c: "!"}
// Hello
// World
它们不是常量,但它们的作用域是有限的,不管是好是坏


想法2 组合
数组
对象
解构。创建此帮助器函数:

function use(input, callback) {
    callback(input, input);
}
const dup = input => [input, input];
然后像这样解构:

const [result, {a, b}] = dup(doSomething());
现在,您的
结果
a
b
都是
const
s


在@raina77ow的回答中,他们哀叹
const
代币不太方便
;但是如果您使用冒号(并重复关键字)而不是逗号,您的答案就在这里。
但是您已经提到了
const result=doSomething();常数{a,b}=结果在你的问题中,我看不出它有什么更糟的,它是如何工作的

但是从中,你可以看到,
let something=x;让另一个=y
让[某物,另一物]=[x,y]相同
因此,一个真正优雅的解决方案实际上很简单

const [result, {a, b}] = [,,].fill(doSomething());
您需要额外的



除此之外(为了让它成为自己的答案,而不是只值得评论),这种复制也可以在解构语法中完成(这就是我遇到这个问题的原因)。
结果中说
b
本身有一个
c
;您希望对其进行分解,但同时保留对
b
的引用

但实际上你可以

const [result, {a, b, b:{c}}] = [,,].fill(doSomething());
现在您有了
result
a
b
,&
c
,即使a&b在result中,c在b中。
如果您实际上不需要
result
,这尤其方便,看起来
fill()
仅对根对象是必需的:
const{a,b,b:{c}}=doSomething()

这似乎对数组不起作用,因为语法中的位置是关键

但是,数组是对象,因此您可以将索引用作键并复制索引引用:

const [result, {0:a, 1:b, 1:{c}}] = [,,].fill(doArrayThing());
这也意味着您可以对数组进行类似的解构,而通常它会抱怨对象不可编辑,您可以通过使用更高的键而不是数组语法跳过索引,因为数组语法必须写入空逗号。
也许最棒的是,
{0:a,1:b,…c}
仍然像
[a,b,…c]
那样工作,因为数组的
Object.keys()
会提取其索引(但生成的
c
不会有
.length



但我并不满足于此,我真的很喜欢@Arash在idea#2中的做法,但它不够通用,不足以帮助消除上例中的
b
,而且它复制了
const

所以…我写了我自己的:|(ctrl+F代表
goodluck

您使用相同的常规语法,但有一些例外:

  • 您的分解结构是用模板文字编写的,输入对象显示为插值
    例如
    [,]=input
    变成
    `[,]=${input}`
  • equals实际上是可选的
  • 您从未在销毁中重命名输出
    例如
    [a,b,…c]=input
    变成
    `[,…]${input}`
  • 此模板的输出运行于
    μ
    (您可以将其命名为任何名称)是您按顺序指定的元素数组
    例如
    const{a:a,b:b}=input变成
    const[A,B]=μ`{A,B}${input}`
    注意:重命名是如何在输出端出现的。即使输入是一个对象,输出也总是一个平面数组
  • 可以使用数字而不是重复的逗号跳过迭代器中的元素
    例如
    const[a,d]=输入
    const[a,d]=μ`[,2,]`
  • 最后,这一点的要点;进入对象时,在其前面加上冒号将其保存到输出中
乙二醇

变成

const [result, a, b] = μ`:{a, b::{c}} ${doSomething()}`;

因此,除了上述,优点是:

  • 我根本没有运行eval,而是对输入进行解析并应用逻辑,
    因此,我可以在运行时为您提供更好的错误消息
ES6甚至都不关心这个问题:

_ = {a:7, get b() {throw 'hi'}};
console.warn('ES6');
out(() => {
    const {a, b} = _;
    return [a, b];
});
console.warn('hashbrown');
out(() => {
    const {a,b} = μ`{a,...} ${_}`;
    return [a, b];
});

Eg2这里ES6说是罪魁祸首。我不仅正确地说它是
1
的错误,而且我还告诉您它发生在分解结构中的什么地方:

_ = [1];
console.warn('ES6');
out(() => {
    const [[a]] = _;
    return [a];
});
console.warn('hashbrown');
out(() => {
    const [a] = μ`[[]] ${_}`;
    return [a];
});

  • 如果您需要跳过大型数组或保留大量内部变量,则非常方便
乙二醇


好吧,有什么问题吗?缺点:

  • 即使是最后一个pro,丢失所有名称的销毁语法也很难阅读。实际上,我们在语言中需要这种语法,因此名称在语言内部,而不是在语言外部出现的
    const[
  • 编译器不知道该怎么办,语法错误是在运行时发生的(而您在前面使用ES6时会被告知),如果您使用某种类型的类型检查,IDE可能无法分辨出哪些内容被吐出(我拒绝为它编写一个正确完成的模板
    .d.ts
  • 正如所暗示的
    _ = {a:7, get b() {throw 'hi'}};
    console.warn('ES6');
    out(() => {
        const {a, b} = _;
        return [a, b];
    });
    console.warn('hashbrown');
    out(() => {
        const {a,b} = μ`{a,...} ${_}`;
        return [a, b];
    });
    
    _ = [1];
    console.warn('ES6');
    out(() => {
        const [[a]] = _;
        return [a];
    });
    console.warn('hashbrown');
    out(() => {
        const [a] = μ`[[]] ${_}`;
        return [a];
    });
    
    const [[a,,,,,,,,,j], [[aa, ab], [ba]]] = [,,].fill(_);
    const [a, aa, ab, ba, j] = μ`[:[ , ], [ ], 7, ] ${_}`;
    
    _ = [1, 2, 3, 4];
    console.warn('ES6');
    out(() => {
        eval(`const [a, ...betwixt, b] = _`);
        return [a, betwixt, b];
    });
    console.warn('hashbrown');
    out(() => {
        const [a, betwixt, b] = μ`[, ..., ] ${_}`;
        return [a, betwixt, b];
    });
    
    window.μ = (() => {
        //build regexes without worrying about
        // - double-backslashing
        // - adding whitespace for readability
        // - adding in comments
        let clean = (piece) => (piece
            .replace(/(?<=^|\n)(?<line>(?:[^\/\\]|\/[^*\/]|\\.)*)\/\*(?:[^*]|\*[^\/])*(\*\/|)/g, '$<line>')
            .replace(/(?<=^|\n)(?<line>(?:[^\/\\]|\/[^\/]|\\.)*)\/\/[^\n]*/g, '$<line>')
            .replace(/\n\s*/g, '')
        );
        let regex = ({raw}, ...interpolations) => (
            new RegExp(interpolations.reduce(
                (regex, insert, index) => (regex + insert + clean(raw[index + 1])),
                clean(raw[0])
            ))
        );
    
        let start = {
            parse : regex`^\s*(?:
                //the end of the string
                //I permit the equal sign or just declaring the input after the destructure definition without one
                (?<done>=?\s*)
                |
                //save self to output?
                (?<read>(?<save>:\s*|))
                //opening either object or array
                (?<next>(?<open>[{[]).*)
            )$`
        };
        let object = {
            parse : regex`^\s*
                (?<read>
                    //closing the object
                    (?<close>\})|
    
                    //starting from open or comma you can...
                    (?:[,{]\s*)(?:
                        //have a rest operator
                        (?<rest>\.\.\.)
                        |
                        //have a property key
                        (?<key>
                            //a non-negative integer
                            \b\d+\b
                            |
                            //any unencapsulated string of the following
                            \b[A-Za-z$_][\w$]*\b
                            |
                            //a quoted string
                            (?<quoted>"|')(?:
                                //that contains any non-escape, non-quote character
                                (?!\k<quoted>|\\).
                                |
                                //or any escape sequence
                                (?:\\.)
                            //finished by the quote
                            )*\k<quoted>
                        )
                        //after a property key, we can go inside
                        \s*(?<inside>:|)
                    )
                )
                (?<next>(?:
                    //after closing we expect either
                    // - the parent's comma/close,
                    // - or the end of the string
                    (?<=\})\s*(?:[,}\]=]|$)
                    |
                    //after the rest operator we expect the close
                    (?<=\.)\s*\}
                    |
                    //after diving into a key we expect that object to open
                    (?<=:)\s*[{[:]
                    |
                    //otherwise we saw only a key, we now expect a comma or close
                    (?<=[^:\.}])\s*[,}]
                ).*)
            $`,
            //for object, pull all keys we havent used
            rest : (obj, keys) => (
                Object.keys(obj)
                    .filter((key) => (!keys[key]))
                    .reduce((output, key) => {
                        output[key] = obj[key];
                        return output;
                    }, {})
            )
        };
        let array = {
            parse : regex`^\s*
                (?<read>
                    //closing the array
                    (?<close>\])
                    |
                    //starting from open or comma you can...
                    (?:[,[]\s*)(?:
                        //have a rest operator
                        (?<rest>\.\.\.)
                        |
                        //skip some items using a positive integer
                        (?<skip>\b[1-9]\d*\b)
                        |
                        //or just consume an item
                        (?=[^.\d])
                    )
                )
                (?<next>(?:
                    //after closing we expect either
                    // - the parent's comma/close,
                    // - or the end of the string
                    (?<=\])\s*(?:[,}\]=]|$)
                    |
                    //after the rest operator we expect the close
                    (?<=\.)\s*\]
                    |
                    //after a skip we expect a comma
                    (?<=\d)\s*,
                    |
                    //going into an object
                    (?<=[,[])\s*(?<inside>[:{[])
                    |
                    //if we just opened we expect to consume or consume one and close
                    (?<=\[)\s*[,\]]
                    |
                    //otherwise we're just consuming an item, we expect a comma or close
                    (?<=[,[])\s*[,\]]
                ).*)
            $`,
            //for 'array', juice the iterator
            rest : (obj, keys) => (Array.from(keys))
        };
    
        let destructure = ({next, input, used}) => {
    //for exception handling
    let phrase = '';
    let debugging = () => {
        let tmp = type;
        switch (tmp) {
        case object: tmp = 'object'; break;
        case array : tmp = 'array'; break;
        case start : tmp = 'start'; break;
        }
        console.warn(
            `${tmp}\t%c${phrase}%c\u2771%c${next}`,
            'font-family:"Lucida Console";',
            'font-family:"Lucida Console";background:yellow;color:black;',
            'font-family:"Lucida Console";',
    //input, used
        );
    };
    debugging = null;
            //this algorithm used to be recursive and beautiful, I swear,
            //but I unwrapped it into the following monsterous (but efficient) loop.
            //
            //Lots of array destructuring and it was really easy to follow the different parse paths,
            //now it's using much more efficient `[].pop()`ing.
            //
            //One thing that did get much nicer with this change was the error handling.
            //having the catch() rethrow and add snippets to the string as it bubbled back out was...gross, really
            let read, quoted, key, save, open, inside, close, done, rest, type, keys, parents, stack, obj, skip;
    try {
            let output = [];
            while (
                //this is the input object and any in the stack prior
                [obj, ...parents] = input,
                //this is the map of used keys used for the rest operator
                [keys, ...stack] = used,
                //assess the type from how we are storing the used 'keys'
                type = (!keys) ? start : (typeof keys.next == 'function') ? array : object,
    phrase += (read || ''),
    read = '',
    debugging && debugging(),
                //parse the phrase, deliberately dont check if it doesnt match; this way it will throw
                {read, quoted, next, key, save, open, inside, close, done, rest, skip} = next.match(type.parse).groups,
                done == null
            ) {
                if (open) {
                    //THIS IS THE EXTRA FUNCTIONALITY
                    if (save)
                        output.push(obj);
                    switch (open) {
                    case '{':
                        used = [{}, ...stack];
                        break;
                    case '[':
                        used = [obj[Symbol.iterator](), ...stack];
                        input = [null, ...parents];
                        break;
                    default:
                        throw open;
                    }
                    continue;
                }
    
                if (close) {
                    used = stack;
                    input = parents;
                    continue;
                }
                //THIS IS THE EXTRA FUNCTIONALITY
                if (skip) {
                    for (skip = parseInt(skip); skip-- > 0; keys.next());
                    continue;
                }
    
                //rest operator
                if (rest) {
                    obj = type.rest(obj, keys);
                    //anticipate an immediate close
                    input = [null, ...parents];
                }
                //fetch the named item
                else if (key) {
                    if (quoted) {
                        key = JSON.parse(key);
                    }
                    keys[key] = true;
                    obj = obj[key];
                }
                //fetch the next item
                else
                    obj = keys.next().value;
    
                //dive into the named object or append it to the output
                if (inside) {
                    input = [obj, ...input];
                    used = [null, ...used];
                }
                else
                    output.push(obj);
            }
            return output;
    }
    catch (e) {
        console.error('%c\u26A0 %cError destructuring', 'color:yellow;', '', ...input);
        console.error(
            `%c\u26A0 %c${phrase}%c${read || '\u2771'}%c${next || ''}`,
            'color:yellow;',
            'font-family:"Lucida Console";',
            'font-family:"Lucida Console";background:red;color:white;',
            'font-family:"Lucida Console";'
        );
        throw e;
    }
    return null;
        };
        //just to rearrange the inputs from template literal tags to what destructure() expects.
        //I used to have the function exposed directly but once I started supporting
        //iterators and spread I had multiple stacks to maintain and it got messy.
        //Now that it's wrapped it runs iteratively instead of recursively.
        return ({raw:[next]}, ...input) => (destructure({next, input, used:[]}));
    })();
    
    let out = (func) => {
        try {
            console.log(...func().map((arg) => (JSON.stringify(arg))));
        }
        catch (e) {
            console.error(e);
        }
    };
    let _;
    
    //THE FOLLOWING WORK (AND ARE MEANT TO)
    _ = {a:{aa:7}, b:8};
    out(() => {
        const [input,{a,a:{aa},b}] = [,,].fill(_);
        return [input, a, b, aa];
    });
    out(() => {
        const [input,a,aa,b] = μ`:{a::{aa},b}=${_}`;
        return [input, a, b, aa];
    });
    
    _ = [[65, -4], 100, [3, 5]];
    out(() => {
        //const [[aa, ab], , c] = input; const [ca, cb] = c;
        const {0:{0:aa, 1:ab}, 2:c, 2:{0:ca, 1:cb}} = _;
        return [aa, ab, c, ca, cb];
    });
    out(() => {
        const [aa,ab,c,ca,cb] = μ`{0:{0,1}, 2::{0,1}}=${_}`;
        return [aa, ab, c, ca, cb];
    });
    
    _ = {a:{aa:7, ab:[7.5, 7.6, 7.7], 'a c"\'':7.8}, b:8};
    out(() => {
        const [input,{a,a:{aa,ab,ab:{0:aba, ...abb},"a c\"'":ac},b,def='hi'}] = [,,].fill(_);
        return [input, a, aa, ab, aba, abb, ac, b, def];
    });
    out(() => {
        const [input,a,aa,ab,aba,abb,ac,b,def='hi'] = μ`:{a::{aa,ab::{0, ...},"a c\"'"},b}=${_}`;
        return [input, a, aa, ab, aba, abb, ac, b, def];
    });
    
    _ = [{aa:7, ab:[7.5, {abba:7.6}, 7.7], 'a c"\'':7.8}, 8];
    out(() => {
        const [input,[{aa,ab,ab:[aba,{abba},...abc],"a c\"'":ac}],[a,b,def='hi']] = [,,,].fill(_);
        return [input, a, aa, ab, aba, abba, abc, ac, b, def];
    });
    out(() => {
        const [input,a,aa,ab,aba,abba,abc,ac,b,def='hi'] = μ`:[:{aa,ab::[,{abba},...],"a c\"'"},]=${_}`;
        return [input, a, aa, ab, aba, abba, abc, ac, b, def];
    });
    
    _ = [[-1,-2],[-3,-4],4,5,6,7,8,9,0,10];
    out(() => {
        const [[a,,,,,,,,,j], [[aa, ab], [ba]]] = [,,].fill(_);
        return [a, aa, ab, ba, j];
    });
    out(() => {
        const [a, aa, ab, ba, j] = μ`[:[ , ], [ ], 7, ] ${_}`;
        return [a, aa, ab, ba, j];
    });
    
    
    //THE FOLLOWING FAIL (AND ARE MEANT TO)
    
    _ = [1];
    console.warn('ES6');
    out(() => {
        const [[a]] = _;
        return [a];
    });
    console.warn('hashbrown');
    out(() => {
        const [a] = μ`[[]] ${_}`;
        return [a];
    });
    
    
    _ = [1, 2, 3, 4];
    console.warn('ES6');
    out(() => {
        eval(`const [a, ...betwixt, b] = _`);
        return [a, betwixt, b];
    });
    console.warn('hashbrown');
    out(() => {
        const [a, betwixt, b] = μ`[, ..., ] ${_}`;
        return [a, betwixt, b];
    });
    
    
    _ = {a:7, get b() {throw 'hi'}};
    console.warn('ES6');
    out(() => {
        const {a, b} = _;
        return [a, b];
    });
    console.warn('hashbrown');
    out(() => {
        const {a,b} = μ`{a,...} ${_}`;
        return [a, b];
    });