RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 860858
Accepted
UModeL
UModeL
Asked:2020-07-27 07:04:22 +0000 UTC2020-07-27 07:04:22 +0000 UTC 2020-07-27 07:04:22 +0000 UTC

Array.prototype.concat() 是如何工作的

  • 772

对于 Adob​​e Photoshop 中的脚本,有一个带有选择和控件的对话框:

var dlg = new Window('dialog{text: "Выбор размеров", bounds: [' + fBounds(150,200,370,150) + '], \
            panel_s: Panel{text: "Стандартные:", bounds: [' + fBounds(5,5,203,85) + '], \
                checkbox_16:  Checkbox{bounds: [' + fBounds(5,13,55,14) + '],   text: "16 x 16"},   \
                checkbox_24:  Checkbox{bounds: [' + fBounds(5,33,55,14) + '],   text: "24 x 24"},   \
                checkbox_32:  Checkbox{bounds: [' + fBounds(5,53,55,14) + '],   text: "32 x 32"},   \
                checkbox_48:  Checkbox{bounds: [' + fBounds(67,13,55,14) + '],  text: "48 x 48"},   \
                checkbox_64:  Checkbox{bounds: [' + fBounds(67,33,55,14) + '],  text: "64 x 64"},   \
                checkbox_96:  Checkbox{bounds: [' + fBounds(67,53,55,14) + '],  text: "96 x 96"},   \
                checkbox_128: Checkbox{bounds: [' + fBounds(129,13,65,14) + '], text: "128 x 128"}, \
                checkbox_256: Checkbox{bounds: [' + fBounds(129,33,65,14) + '], text: "256 x 256"}, \
                checkbox_512: Checkbox{bounds: [' + fBounds(129,53,65,14) + '], text: "512 x 512"}  \
            }, \
            panel_o: Panel{text: "Другие:", bounds: [' + fBounds(206,5,69,64) + '],\
                checkbox_19: Checkbox{bounds: [' + fBounds(5,13,55,14) + '], text: "19 x 19"}, \
                checkbox_38: Checkbox{bounds: [' + fBounds(5,33,55,14) + '], text: "38 x 38"}  \
            }, \
            panel_u: Panel{text: "Свои:", bounds: [' + fBounds(273,5,92,85) + '],\
                checkbox_u1: Checkbox{bounds:   [' + fBounds(5,13,14,14) + ']},                                                 \
                static_u1:   StaticText{bounds: [' + fBounds(21,13,31,14) + '], text: ". . . x", justify: "right"},             \
                edittext_u1: EditText{bounds:   [' + fBounds(53,11,30,19) + '], text: ". . .", properties: {multiline: false}}, \
                \
                checkbox_u2: Checkbox{bounds:   [' + fBounds(5,33,14,14) + ']},                                                 \
                static_u2:   StaticText{bounds: [' + fBounds(21,33,31,14) + '], text: ". . . x", justify: "right"},             \
                edittext_u2: EditText{bounds:   [' + fBounds(53,31,30,19) + '], text: ". . .", properties: {multiline: false}}, \
                \
                checkbox_u3: Checkbox{bounds:   [' + fBounds(5,53,14,14) + ']},                                                 \
                static_u3:   StaticText{bounds: [' + fBounds(21,53,31,14) + '], text: ". . . x", justify: "right"},             \
                edittext_u3: EditText{bounds:   [' + fBounds(53,51,30,19) + '], text: ". . .", properties: {multiline: false}}  \
            }, \
            button_all:    Button{bounds:       [' + fBounds(208,70,64,19) + '], text: "Все"},          \
            static_save:   StaticText{bounds:   [' + fBounds(5,99,75,14) + '],   text: "Сохранить в:"}, \
            drop_save:     DropDownList{bounds: [' + fBounds(78,95,287,23) + '], properties: {items: ["папку рядом с исходным файлом", "-", "Выбрать папку..."]}, helpTip:"Ooops!!!"}, \
            progress_save: Progressbar{bounds:  [' + fBounds(0,0,370,4) + '],    value:50},             \
            button_run:    Button{bounds:       [' + fBounds(4,122,70,22) + '],  text: "Создать"}       \
        };'
    );

    // -- Выбор всех значений

    dlg.show();

有一个按钮处理程序可以选择所有复选框:

// -- Выбор всех значений
dlg.button_all.addEventListener('click', function() {

    alert(dlg.panel_s.children + '\r\n' + dlg.panel_s.children.length);
    // Коллекция (9 элементов) и количество (9) отображаются верно

    alert(dlg.panel_o.children + '\r\n' + dlg.panel_o.children.length);
    // Коллекция (2 элемента) и количество (2) отображаются верно

    // Объединяем обе коллекции и три отдельных элемента
    var aPanel = [].concat(
        dlg.panel_s.children, 
        dlg.panel_o.children, 
        dlg.panel_u.checkbox_u1, 
        dlg.panel_u.checkbox_u2, 
        dlg.panel_u.checkbox_u3
    );

    alert(aPanel + '\r\n' + aPanel.length);
    // Массив отображается верно (14 элементов), но количество не совпадает (5) !
});



从处理程序代码中的注释可以看出问题出在哪里,问题如下... Array.prototype.concat()方法,根据文档,应该连接数组,将它们扩展到第一级的嵌套。实际上,根据最后一个弹出窗口(请参阅处理程序代码),这就是发生的情况 - 所有元素都按照它们添加的顺序显示在那里。
但是,在访问时aPanel[0],不是第一个元素 ( checkbox_16),而是返回整个集合dlg.panel_s.children。因此,数组的长度aPanel为 5,您只能通过指定集合中的嵌套来获得所需的元素,即 aPanel[0][0].

这是一个错误还是棘手的逻辑?使用concat()连接数组的正确方法是什么?

javascript
  • 1 1 个回答
  • 10 Views

1 个回答

  • Voted
  1. Best Answer
    Kir_Antipov
    2020-07-27T08:55:21Z2020-07-27T08:55:21Z

    修正案

    确实,根据MDN 帮助

    该方法concat创建一个新数组,该数组由调用它的对象中的元素组成,然后依次为每个参数、其所有元素(如果参数是数组)或参数本身(如果它不是大批)

    первый уровень вложенности如您所见,只有 about ,这里没有一个字。
    这是一个很好的例子:

    let x = [].concat(1, 2, [3, 4], [5, 6, [7, 8]]);
    console.log(x);        // Выведем полученный массив
    console.log(x.length); // Длина равняется не 8, как если бы все раскрывалось 
                           // до первого уровня вложенности, а 7!
    console.log(x[6]);     // 6-ой элемент является массивом

    但是,这不会改变您的问题,因为在您的情况下,如果元素dlg.{foo}.children是数组,那么它们的展开仍然会发生


    那么有什么关系呢?

    我不会折磨你,最后告诉你问题是什么。再次,注意这一行:

    如果参数是一个数组

    我在说什么?您指定的元素不是数组!是的,它们是集合,类似于数组,但规范仍然很严格。

    让我们看看该方法如何处理concat参数object,就像您的元素一样,它本质上是一个集合,而不是一个数组:

    function test() {
        console.log(Array.isArray(arguments)); // false
        return [].concat(arguments, arguments);
    }
    
    console.log(test(1, 2, 3).toString()); // [object Arguments], [object Arguments]

    如您所见,concat我不知道它是集合还是常规对象。对他来说,唯一重要的是,这个构造不是数组。

    PS - 为了能够检查一个对象是否是一个数组,我建议你看看Array.isArray()函数


    该怎么办?

    最好的选择是不要偷懒并手动添加所有内容:

    var aPanel = [];
    for (let i = 0; i < dlg.panel_s.children.length; i++)
        aPanel.push(dlg.panel_s.children[i]);
    for (let i = 0; i < dlg.panel_o.children.length; i++)
        aPanel.push(dlg.panel_o.children[i]);
    aPanel.push(dlg.panel_u.checkbox_u1, dlg.panel_u.checkbox_u2, dlg.panel_u.checkbox_u3);
    

    另一种变体

    您可以在原型级别定义您的方法,这会将数组和集合扩展到第一级:

    Array.prototype.fullConcat = function() {
        const exclude = ["string"]; // Пусть строка и является коллекцией, но мы хотим считать ее цельным объектом
        function getElements(item) { // Рекурсивно получим все элементы внутри заданного (если таковые есть)
            let result = [];
            if (item.length === undefined || exclude.includes(typeof item))
                result.push(item);
            else {
                for (let i = 0; i < item.length; i++) {
                    let add = getElements(item[i]);
                    for (let j = 0; j < add.length; j++)
                        result.push(add[j]);
                }
            }
            return result;
        }
    
        let concated = [];
        for (let i = 0; i < arguments.length; i++) { // Начнем объединение
            let item = arguments[i];
            if (item.length === undefined || exclude.includes(typeof item))
                concated.push(item);
            else {
                for (let i = 0; i < item.length; i++) {
                    let add = getElements(item[i]);
                    for (let j = 0; j < add.length; j++)
                    concated.push(add[j]);
                }
            }
        }
        // Не забудем и про контекст this, который может оказаться совсем не пустым
        return this.length == 0 ? concated : [].fullConcat(this, concated);
    };
    
    function testArguments() {
        return [].fullConcat(arguments, arguments);
    }
    
    // Проверим, как метод расправится со вложенностью
    console.log([1, 2, 3].fullConcat([4], [5, 6, [7, 8, [9, 10]]], "hello", "world")); // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, "hello", "world"
    
    // Проверим, как метод расправится с коллекцией в лице arguments
    console.log(testArguments(1, 2, 3, [4, 5])); // 1, 2, 3, 4, 5, 1, 2, 3, 4, 5


    升级版:

    非常感谢@Grundy让我注意到以下几点:

    让我们看看不是MDN 帮助,而是ECMAScript 规范。让我们注意这些行:

    1. 重复whileitems不为空
      a。从 items 中删除第一个元素,并让E成为元素的值。
      湾。让spreadable?IsConcatSpreadable(E).
      C。如果spreadable是true,那么

         // copy items from within the object
      

      d。别的:

         // copy the object
      

    或俄语:

    1. 重复直到items空
      一个。从 中删除第一个元素items,分配E元素的值。
      湾。赋值。spreadable_ C。如果是,那么IsConcatSpreadable (E)
      spreadabletrue

         // копировать элементы изнутри объекта
      

      d。否则:

         // копировать сам объект
      

    也就是说,该元素是否会显式添加到结果数组中,或者是否将其先前扩展为集合,是执行某个IsConcatSpreadable(O)函数的结果。让我们来看看它是什么,和它一起吃什么:

    1. 如果Type(O)不是Object,则返回false。
    2. 让spreadable?Get(O, @@isConcatSpreadable).
    3. 如果spreadable不是undefined,则返回ToBoolean(spreadable)。
    4. 返回?IsArray(O).

    或俄语:

    1. 如果тип O没有Object,返回false。
    2. 为对象spreadable的属性赋值。@@isConcatSpreadableO
    3. 如果spreadable没有undefined,返回ToBoolean (spreadable)。
    4. 返回IsArray(O)。

    也就是说,检查一个对象是否是一个数组是最后的。在此之前,检查对象是否具有属性Symbol.isConcatSpreadable,如果有,则其布尔值将指示它是否会在何时展开concat。

    现在让我们将其付诸实践:

    // Создадим собственную коллекцию
    var userCollection = {
    0: 'a', 1: 'b', 2: 'c', length: 3
    };
    
    // Убедимся, что она не является массивом
    console.log(Array.isArray(userCollection)); // false
    
    // Посмотрим, что вернет нам concat
    console.log([].concat(userCollection, userCollection)); // Array [ Object(3), Object(3) ]
    
    // Теперь маленькая хитрость:
    // Установим внутреннее свойство, отвечающее
    // за результат IsConcatSpreadable(O), равным true
    userCollection[Symbol.isConcatSpreadable] = true;
    
    // Посмотрим, что теперь вернет нам concat
    console.log([].concat(userCollection, userCollection)); // Array [ "a", "b", "c", "a", "b", "c" ]
    
    // Или же сразу создадим коллекцию с указанным свойством:
    // Создадим собственную коллекцию
    var userCollection_1 = {
    0: 'a', 1: 'b', 2: 'c', length: 3, [Symbol.isConcatSpreadable]: true
    };
    
    // Посмотрим, что вернет нам concat
    console.log([].concat(userCollection_1, userCollection_1)); // Array [ "a", "b", "c", "a", "b", "c" ]

    像发条一样工作。为了实验,让我们在现有的枚举类型上进行测试。再次回忆一下我们的论点:

    // arguments не указан, как spreadable
    function nonSpreadable() {
        return [].concat(arguments, arguments);
    }
    
    // arguments указан, как spreadable
    function spreadable() {
        arguments[Symbol.isConcatSpreadable] = true;
        return [].concat(arguments, arguments);
    }
    
    // Какой же массив вернется при объединении 2-х arguments?
    console.log(nonSpreadable(1, 2)); // Array [ Arguments, Arguments ]
    
    // А теперь?)
    console.log(spreadable(1, 2));    // Array [ 1, 2, 1, 2 ]
    console.log(spreadable(1, 2, 3)); // Array [ 1, 2, 3, 1, 2, 3 ]

    如您所见,我们设法使用以下方法concat将内部值合并arguments到一个数组中

    为了清白,让我提醒您:以这种方式进入对象的内部属性,改变其工作逻辑,是错误的和亵渎的!

    为什么?让我们想象一下这种情况:你看到一个数组。它应该如何处理该方法concat?没错,根据指定的证书不止一次,组合的时候一定要显露!但是,如果有人按如下方式使用您收到的数组怎么办:

    // Создадим обычные массивы
    var arr_0 = [1, 2];
    var arr_1 = [3, 4];
    
    // Убедимся, что это именно массивы, а не что-либо еще
    console.log(Array.isArray(arr_0)); // true
    console.log(Array.isArray(arr_1)); // true
    
    // Объединим их
    var arr_2 = [].concat(arr_0, arr_1);
    console.log(arr_2);        // Array [ 1, 2, 3, 4 ]
    console.log(arr_2.length); // 4, как и полагается
    
    // А теперь сделаем парочку богопротивных действий
    arr_0[Symbol.isConcatSpreadable] = arr_1[Symbol.isConcatSpreadable] = false;
    
    // Объединим их снова
    var arr_3 = [].concat(arr_0, arr_1);
    console.log(arr_3);        // Array [ Array(2), Array(2) ]
    console.log(arr_3.length); // 2, что-то пошло не так)

    我们只是改变了标准类型的行为,这是非常非常错误的!所以尽量避免这种方法!

    一切,良心被清除,所以我们可以进一步滥用:)


    您的问题的另一种解决方案:

    基于IsConcatSpreadable(O)执行结果的操作,您的任务可以通过以下方式解决:

    // Колдуем над isConcatSpreadable:
    dlg.panel_s.children[Symbol.isConcatSpreadable] = dlg.panel_0.children[Symbol.isConcatSpreadable] = true;
    
    // Объединяем обе коллекции и три отдельных элемента
    var aPanel = [].concat(
        dlg.panel_s.children, 
        dlg.panel_o.children, 
        dlg.panel_u.checkbox_u1, 
        dlg.panel_u.checkbox_u2, 
        dlg.panel_u.checkbox_u3
    );
    

    为了清楚起见,还有一个活生生的例子:

    // Сформируем свои коллекции
    var userCollection_0 = {
        0: 'a', 1: 'b', 2: 'c', length: 3
    };
    var userCollection_1 = {
        0: 'd', 1: 'e', 2: 'f', 3: 'g', length: 4
    };
    
    // Колдуем над isConcatSpreadable
    userCollection_0[Symbol.isConcatSpreadable] = userCollection_1[Symbol.isConcatSpreadable] = true;
    
    // Объединяем обе коллекции и три отдельных элемента
    var aPanel = [].concat(
        userCollection_0, 
        userCollection_1, 
        'h', 
        'i', 
        'j'
    );
    
    // Посмотрим, что внутри
    console.log(aPanel);        // Array [ "a", "b", "c", "d", "e", "f", "g", "h", "i", "j" ]
    
    // Убедимся, что длина также совпадает
    console.log(aPanel.length); // 10 из 10)


    希望我的回答能帮助您解决问题!祝你工作顺利!

    • 6

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    是否可以在 C++ 中继承类 <---> 结构?

    • 2 个回答
  • Marko Smith

    这种神经网络架构适合文本分类吗?

    • 1 个回答
  • Marko Smith

    为什么分配的工作方式不同?

    • 3 个回答
  • Marko Smith

    控制台中的光标坐标

    • 1 个回答
  • Marko Smith

    如何在 C++ 中删除类的实例?

    • 4 个回答
  • Marko Smith

    点是否属于线段的问题

    • 2 个回答
  • Marko Smith

    json结构错误

    • 1 个回答
  • Marko Smith

    ServiceWorker 中的“获取”事件

    • 1 个回答
  • Marko Smith

    c ++控制台应用程序exe文件[重复]

    • 1 个回答
  • Marko Smith

    按多列从sql表中选择

    • 1 个回答
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Suvitruf - Andrei Apanasik 什么是空? 2020-08-21 01:48:09 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5