在数组值属性中查找对象,包括原型

时间:2014-09-06 10:14:06

标签: javascript indexof

我有一个对象数组作为我的对象的属性:

myObject.columns = [{} , {} , ...];

MyObject在基础原型中也有这个属性(列)。

我希望像indexOf()这样的东西迭代列属性(对象及其所有原型,如hasOwnProperty()功能),如果对象存在则返回index。我怎么能这样做?

4 个答案:

答案 0 :(得分:2)

您无法同时访问原型中的本地值和值。局部值阴影(隐藏)原型中的值。换句话说,没有在对象上自动组合或连接数组值属性的元素和在其原型上使用相同名称的数组值属性的概念。你必须自己做。要将属性的本地值及其值组合在原型上:

var combined_columns = this.columns.concat(this.constructor.prototype.columns);

现在您可以在连接数组中查找值:

var find_object = find_object_in_array(combined_columns, object);

写作find_object_in_array留作练习。

一路上行

但这仅涉及即时原型。如果您想从链中的所有原型中获取columns属性的串联:

function concat_values_from_proto_chain(obj, prop) {
    var result = [];
    while (obj) {
        result = Array.prototype.concat.apply(result, obj[prop]);
        obj = Object.getPrototypeOf(obj);
    }
    return result;
}

现在,您可以在concat_values_from_proto_chain(this, 'columns')中查找您的对象了。 (注意,看起来很笨拙的Array.prototype.concat是一种处理未定义(缺席)和空值的方法,而不必对它们进行特殊测试。)

concat_values_from_proto_chain结合了两个不同的问题:走原型链,并从每个问题中提取值。将这两个问题分开是更好的做法,首先编写一个只获得原型链的例程:

function get_proto_chain(obj) {
    var result = [];
    while (obj) {
        result.push(obj);
        obj = Object.getPrototypeOf(obj);
    }
    return result;
}

但即使这个例程也会交织两个不同的方面:在最近的值上重复调用一个函数,并找到一个对象的原型。为了分离这两个方面,并创建我们可以在将来使用的通用的,有用的例程,我们编写一个函数,它接受一个函数,其中唯一的工作就是在最近的值上调用该函数并返回数组中的一系列结果:

function get_function_series(fn, value) {
    var result = [];
    while (value) {
        result.push(value);
        value = fn(value);
    }
 }

如果我们处于ES6状态,我们可能希望将其写为生成器:

function* get_function_series(fn, value) {
    while (value) { yield value = fn(value); }
}

我们现在可以简洁地重写get_proto_chain

function get_proto_chain(obj) {
    return get_function_series(Object.getPrototypeOf, obj);
}

然后连接原型链的每个成员的属性值只是

function concat_values_from_proto_chain(obj, prop) {
    return get_proto_chain(obj)                // loop over prototypes
        .map(function(o) { return o[prop]; })  // pull out the prop we want
        .reduce(Function.prototype.apply.bind(Array.prototype.concat), []);
                                               // concat them all down into one big array
}

如果reduce行太多而无法处理,则可以将其替换为

.reduce(function(result, val) {
    return val ? result.concat(val) : result;
}, [])

实施例

function A () { }
var a = new A;
A.prototype.columns = [3, 4];
a.columns = [1, 2];

concat_values_from_proto_chain(a, 'columns)
> [1, 2, 3, 4]

Object.prototype.columns = [5, 6];
concat_values_from_proto_chain(a, 'columns')
> [1, 2, 3, 4, 5, 6]

使用getter自动连接链上的属性值

我们希望this.columns在所有原型上自动返回columns属性的连接值?我们可以使用getter来做到这一点。我们需要引入另一个名为_columns的属性来保存实际值。

object.defineProperty('columns', {
    columns: {
        get: function() { 
            return this._columns.concat(
                concat_values_from_proto_chain(this.getPrototypeOf(this), 'columns'); 
        },
        set: function(v) { this._columns = v; }
    }
});

如果我们希望能够轻松地为其他属性指定此行为:

function define_concatenated_property(obj, prop) {
    return object.defineProperty(prop, {
        columns: {
            get: function() { 
                return this['_'+prop].concat(
                    concat_values_from_proto_chain(this.getPrototypeOf(this), 'columns'); 
            },
            set: function(v) { this['_'+prop] = v; }
        }
    });
}

现在:

function A()
var a = new A;
define_concatenated_property(a, 'columns');
a.columns = [1, 2]; // sets _columns property
A.prototype.columns = [3, 4]
a.columns // [1, 2, 3, 4]

答案 1 :(得分:1)

在你的代码columns中很明显是一个数组,所以你可以迭代它:

function indexOfCompared(array, compareObj) {
  var i = 0,
    len = array.length,
    item,
    prop,
    found = false;

  for (; i < len; i++) {

    item = array[i];

    if (typeof item !== 'object') {
      continue;
    }

    for (prop in compareObj) {
      if (compareObj.hasOwnProperty(prop)) {

        //this simply walks through all the prototype chain
        //and will find the key if the main object
        //or any of its base prototype objects have it
        found = item[prop] === compareObj[prop];
        if (!found) {
          break;
        }
      }
      if (found) {
        return i;
      }
    }
  }

  return -1;
}

myObject.columns = [{x:1, y:2, z:3} , {x:11, y:20, z:30}];

console.log(indexOfByKey(myObject.columns, {x:11}));

通过这种方式,您可以根据多个条件找到所需的列。

console.log(indexOfByKey(myObject.columns, {x:11, y:20}));//1
console.log(indexOfByKey(myObject.columns, {x:11, y:2}));//-1

答案 2 :(得分:0)

MyObject.prototype.indexOf = function (o) {
    var key = Object.keys(o)[0], /// ES5 only
        val = o[key],
        ret = -1;
    this.columns.some( function (el,i) {
        if (key in el) {
            if (el[key] == val) {
                ret = i;
                return true;
            }
        }
        return false;
    });
    return ret;
};

http://jsfiddle.net/1j4a3c6d/

这是一个仅限ES5的解决方案,但仅仅因为它使用了Object.keys(),它可以快速确定indexOf()参数中的键。还有其他一些更为苛刻的方法。

答案 3 :(得分:-1)

我不确定您要做什么,但如果您尝试执行以下评论建议的内容,那么此代码将对您有所帮助:

var myObject = {};
myObject.columns = [
    {name: "joe", lastName: "Boe"} ,
    {color: "blue", height: "100"}
];
function returnIndex(propertyName) {
    for (var i = 0; i < myObject.columns.length; i += 1) {
        for (var property in myObject.columns[i]) {
            if (myObject.columns[i].hasOwnProperty(property) && property === propertyName) {
            return i;
            }
        }
     }
 }

alert(returnIndex("height")); //1