Use an anonymous function that's invoked immediately to create a new scope.
This way the anonymous function of the change event becomes a closure.
for (var i = 0; i < products.length; i++) {
for (var j = 0; j < products.length; j++) {
//anonymous function (outer function)
(function() {
var product = products[i][j];
//now the anonymous function of the change event will be called
//within this new scope, with each unique product in it
$(document).on('change', function () {
//this is function is now a closure with scope of the outer function
product.checked ? product.checked = false : product.checked = true;
};
})();
}
}
The scope of the product variable will fall inside the anonymous function closure and each change event will have a unique product value associated.
Fiddle: http://jsfiddle.net/mattdlockyer/t2h50aun/1/
Closures: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures
Immediately Invoked Functions: http://en.wikipedia.org/wiki/Immediately-invoked_function_expression
There seems to be another issue with your code, why are you checking document for change?