C# lambda expressions as array extensions in JavaScript
Looks OK to me. I don't know that you could do anything to really squeeze more performance out of this. In the end you're mostly dependent on how $.grep performs.

I set up a jsperf test for your own alternatives and the code below, and constructing a function is by far the fastest (I put the function construction into where, and avoided using reduce since you said you'd need IE7+ support).

But! Constructing a function from text is just tremendously fragile:


Quoting a string is a pain. If the string contains single or double quotes, you can't properly quote it without a lot of parsing and escaping. You just really shouldn't try.
Your 'x.' + prop accessing can easily fail too, if the key isn't a proper "word". Any string can be a property name, including strings with spaces, reserved words, operators, etc.. You end up with code like x.hey look a string key === ...., which won't get you very far.
Using subscripting access, i.e. 'x["' + prop + '"]', will get you a bit further, but you're just back to the issue of quoting strings again.
Only primitive values can be easily compared; you'd still need a custom function for everything else.
You're changing the object that's been passed in; specifically, string values get quoted. I assume that's partly just because it's an example, but still. (I avoided this in the jsperf code.)


So yeah, it's much faster to use a function, but I'd still very much avoid constructing one on the fly. Better to just write one when necessary, or accept the performance hit otherwise.

As for the existing code, I like the iterative way of reducing the matches if the argument is an object. But I wanted to try turning the loops inside out, as I find that more straightforward:

return $.grep(this, function (item) {
  for(var prop in func) {
    if(item[prop] !== func[prop]) {
      return false;
    }
  }
  return true;
});


However, doing so is slower (somewhat to my surprise).

Otherwise, the code's fine, I only have a couple of notes. 


The that variable would usually imply that you're dealing with scopes, but that isn't the case here; You can use plain this anywhere in your function. You do need a separate variable for the object-based filtering, but I'd just do something like var filtered = this.
You should throw a TypeError instead of a ReferenceError. And I'd write a slightly nicer and more helpful error message, such as "func must be either a function or an object of properties and values to filter by".
You'll probably want to add a func.hasOwnProperty(prop) check to avoid accidentally looping through a bunch of inherited properties. It's not an issue for object literals, but it can be for other objects.
You'll probably want to ensure that a copy of the original array in returned, even if nothing's been filtered. This isn't a problem if you pass in a function, but could happen if you pass in an empty object.
It's a tad heavy on the whitespace, if you ask me, but to each their own.


In the end, I get this

Array.prototype.where = function (filter) {
    switch(typeof filter) { 
        case 'function': 
            return $.grep(that, filter); 

        case 'object':
            var filtered = this;
            for(var prop in filter) {
              if(!filter.hasOwnProperty(prop)) {
                  continue; // ignore inherited properties
              }
              filtered = $.grep(filtered, function (item) {
                  return item[prop] === filter[prop];
              });
            }
            return filtered.slice(0); // copy the array

        default: 
            throw new TypeError("func must be either a function or an object of properties and values to filter by"); 
    }
};