【问题标题】:Javascript "pop" from objectJavascript从对象“弹出”
【发布时间】:2011-10-01 15:02:25
【问题描述】:

我编写了以下代码来从对象中“弹出”一个属性,就好像它是一个数组一样。这看起来像是会让我被更严肃的程序员打的那种代码,所以我想知道这样做的正确方法是什么:

// wrong way to pop:
for( key in profiles ){
    var profile = profiles[key];  // get first property
    profiles[key] = 0;            // Save over property just in case "delete" actually deletes the property contents instead of just removing it from the object
    delete profiles[key];         // remove the property from the object
    break;                        // "break" because this is a loop
}

我应该在上面提到,与真正的“流行音乐”不同,我不需要对象以任何特定的顺序出现。我只需要取出一个并将其从其父对象中删除即可。

【问题讨论】:

  • if (profiles.hasOwnProperty(key) {.. 包裹身体并松开= 0
  • 你到底想做什么?删除添加到对象的第一个属性?这不一定有效,因为不能保证在for..in 循环中检索属性的顺序。除此之外,您应该使用hasOwnProperty 来确保它不是原型的一部分,并且您不需要执行除delete 之外的任何操作来从对象中删除属性。
  • 我认为您的意思是模仿 shift 数组方法,该方法删除(并返回)数组中的 first 元素。 pop 删除 last 元素,因为 push/pop 数组方法充当 FILO。

标签: javascript object iterator


【解决方案1】:

对于for in 循环,浏览器之间没有“正确”的顺序。有些是按照放入的顺序来做的,有些是先做数字索引的。所以如果不创建自己的自定义对象,真的没有办法做到这一点

【讨论】:

    【解决方案2】:
    for( key in profiles ){
    

    您确实应该将key 声明为var

    profiles[key] = 0;            // Save over property just in case "delete" actually deletes the property contents instead of just removing it from the object
    

    是不必要的。删除不会触及属性的值(或者对于有setter但没有getter的属性,甚至要求它有一个值)。

    如果对象在其原型上有任何可枚举的属性,那么这会做一些奇怪的事情。 考虑

    Object.prototype.foo = 42;
    
    function take(obj) {
      for (var key in obj) {
        // Uncomment below to fix prototype problem.
        // if (!Object.hasOwnProperty.call(obj, key)) continue;
        var result = obj[key];
        // If the property can't be deleted fail with an error.
        if (!delete obj[key]) { throw new Error(); }
        return result;
      } 
    }
    
    var o = {};
    alert(take(o));  // alerts 42
    alert(take(o));  // still alerts 42
    

    【讨论】:

    • 这真的应该叫做shift,因为它删除了第一个元素而不是最后一个元素。我了解 OP 错误地命名了他的请求 - 也许您可以在回答中指出这一点。
    • @jbyrd per “不需要对象以任何特定顺序出现”也许是“采取”?
    • 噢!我想我应该更仔细地阅读那部分。说得通。在这种情况下,“采取”并不是一个坏主意!
    【解决方案3】:

    对象中的属性不存储在堆栈中,因此基本概念无法可靠地工作(除了上面 cmets 中提到的其他问题)。

    如果你真的需要这样的构造,试试这样的。

    var ObjectStack = function(obj) {
        this.object = obj;
        this.stack=[];
    };
    ObjectStack.prototype.push = function(key,value) {
        this.object[key]=value;
        this.stack.push(key);
    };
    ObjectStack.prototype.pop = function() {
        var key = this.stack.pop();
        var prop = this.object[key];
        delete this.object[key];
        return prop;
    };
    
    var my_obj = {};
    var my_stack = new ObjectStack(my_obj);
    my_stack.push("prop1",val1);
    my_stack.push("prop2",val2);
    
    var last_prop = my_stack.pop(); //val2
    

    演示:http://jsfiddle.net/a8Rf6/5/

    【讨论】:

    • 这个实现的一个问题是我们有一个堆栈,但内部数组是公共的。无论如何,任何人都可以更改它,甚至用其他根本不是数组的东西替换它。
    • Javascript 从根本上说是动态的。如果我愿意,我可以更改Array.prototype.length。虽然可以使用闭包创建更复杂的实现来隐藏内部堆栈,但这是一个学习示例,在实践中,这是很多额外的代码来保护您免受自己的伤害,而本地语言无论如何都不会这样做。
    【解决方案4】:

    您可以像这样创建 pop 方法:.

    Object.defineProperty(Object.prototype, 'pop',{
        writable: false
        , configurable: false
        , enumerable: false
        , value: function (name) {
            var value = this[name];
            delete this[name];
            return value;
        }
    });
    

    出于某种原因,仅使用 Object.prototype.pop = function ... 会破坏 JQuery

    【讨论】:

      【解决方案5】:

      一种更好的方法,而不是直接修改输入数组。 例如。

      let arr = [{label: "a"}, {label: "b"}, {label: "p"}, {label: "c"}] 
      let newArr = arr.filter(p => { return p.label !== "p";});
      

      【讨论】:

        【解决方案6】:

        现在您可以简单地使用扩展运算符及其 Rest 方式:

        const { key, ...profilesWithoutKey } = profiles;
        

        感谢this blog post

        【讨论】:

        • 这会提取第一个条目,但不会提取profiles[key]
        • @Friedrich 它确实提取了key。请再次检查。
        • 哦,确实。但是key 是静态的,而在问题中它是一个变量。一个例子肯定会有所帮助。此外,真正的 pop 应该传递值,这也是可能的 var {b: b, ...rest} = {a:1, b:2, c:3},返回 b = 1rest = {a:1, c:3}
        • @Friedrich 我的代码就是这样做的,你不需要指定{key: key, ...rest}。如果profiles = {key: 0, a: 1, b: 2},则在执行解决方案后你会得到:key = 0 & profilesWithoutKey = {a: 1, b: 2}
        • 我认为正确的语法应该类似于 const {[key]: valueOfKey, ...profileWithoutKey } = profiles;,因为在 OP 的问题中,profiles 对象不一定有一个名为 key 的键,而是他实际上是在迭代对象和“ pops" 对象的第一个键。
        【解决方案7】:

        研究了上面所有的 cmets 和解决方案,我可以提供一个基于它们的完全现成的解决方案:

        Object.prototype.pop = function() {
            for (var key in this) {
                if (!Object.hasOwnProperty.call(this, key)) continue;
                var result = this[key];
                if (!delete this[key]) throw new Error();
                return result;
            }
        };
        
        var obj = {
            a: '1',
            b: '2',
            c: '3'
        };
        
        console.log(obj.pop()); // 1
        console.log(obj.pop()); // 2
        console.log(obj.pop()); // 3
        console.log(obj); // Object {  }
        

        也许有人会派上用场; )


        附言。如果您将我建议的代码与 jQuery 库一起使用,您可能会在控制台中遇到错误。在这种情况下,下面给出的选项是合适的:

        function _pop() {
            for (var key in this) {
                if (!Object.hasOwnProperty.call(this, key)) continue;
                if (key === 'pop') continue;
                var result = this[key];
                if (!delete this[key]) throw new Error();
                return result;
            }
        }
        
        var obj = {
            a: '1',
            b: '2',
            c: '3'
        };
        
        obj.pop = _pop;
        
        console.log(obj.pop()); // 1
        console.log(obj.pop()); // 2
        console.log(obj.pop()); // 3
        console.log(obj); // Object { pop: _pop() }
        

        【讨论】:

          【解决方案8】:

          因此没有循环和“功能”噪音,简单

          key = 'b'
          profile = {a:1, b:2, c:3}
          
          val = profile[key] 
          delete profile[key]
          
          console.log(obj, key, val)   // Object { a: 1, c: 3 } b 2
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2016-09-23
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-08-24
            • 1970-01-01
            相关资源
            最近更新 更多