<!DOCTYPE html>
<html>
<body>
<script>
var s = {},
defineProperty = Object.defineProperty,
needFix = false;
if (!Array.prototype.forEach) {
Array.prototype.forEach = function (fn, scope) {
'use strict';
var i, len;
for (i = 0, len = this.length; i < len; ++i) {
if (i in this) {
fn.call(scope, this[i], i, this);
}
}
};
}
if (!Object.keys) {
Object.keys = function (o) {
if (o !== Object(o)) {
throw new TypeError('Object.keys called on a non-object');
}
var k = [],
p;
for (p in o) {
// Use direct access, just in case this object has overridden the hasOwnProperty method which does something different.
// Also as it wont go through the prototype chain to find the method, it should be quicker
// Finally hasOwnProperty is set on the lowest level prototype 'Object' which all object derive from. However Object.create(null) can be used to
// create an object that has no base prototype, meaning hasOwnProperty would not exist (however this usage of Object.create is not recommended)
if (Object.prototype.hasOwnProperty.call(o, p)) {
k.push(p);
}
}
return k;
}
}
if (defineProperty) {
try {
defineProperty({}, '_', {
value: 'x'
});
} catch (e) {
needFix = true;
}
if (needFix) {
if ('__defineGetter__' in s) {
defineProperty = function (obj, prop, desc) {
if ('value' in desc) {
obj[prop] = desc.value;
}
'get' in desc &&
obj.__defineGetter__(prop, desc.get);
'set' in desc &&
obj.__defineSetter__(prop, desc.set);
};
} else {
defineProperty = undefined;
}
}
}
function _createIEBindData(data, cb) {
var res = document.createElement('fake');
document.body.appendChild(res);
Object.keys(data).forEach(function (key) {
typeof data[key] === 'object' ?
(res[key] = _createIEBindData(data[key], function (subKey, value, oldVal) { cb([key, subKey].join('.'), value, oldVal); })) :
(res[key] = data[key]);
});
res.attachEvent('onpropertychange', function (e) {
var name = e.propertyName,
value = res[name];
cb(name, value, data[name]);
data[name] = value;
});
return res;
}
function _makedesc(data, prop, cb) {
var value = data[prop];
return {
get: function () {
return value;
},
set: function (s) {
cb(prop, s, value);
value = s;
}
}
}
function _createBindData(data, cb) {
Object.keys(data).forEach(function (key) {
if (typeof data[key] === 'object') {
_createBindData(data[key], function (subKey, value, oldVal) { cb([key, subKey].join('.'), value, oldVal); });
}
defineProperty(data, key, _makedesc(
data, key, cb
));
})
return data;
}
function bindData(data, cb) {
return defineProperty ?
_createBindData(data, cb) :
_createIEBindData(data, cb);
}
var data = bindData({ test: 'test', obj: { msg: 'hello' } }, function (key, value, oldVal) {
console.log(
'key === ' + key + '; ',
'value === ' + value + '; ',
'oldVal === ' + oldVal + '; '
);
});
data.test = 123;
data.obj.msg = 'nihao';
</script>
</body>
</html>