Object.defineProperty(obj,prop,descriptor)使用

时间:2023-03-10 01:56:10
Object.defineProperty(obj,prop,descriptor)使用
初步实现了数据自动映射到html中,动态修改对象数据也很自动更新到html。提供addProps方法-添加新增属性并初始化自动监听
代码如下:

1、abserve.js:包含数据监听实现、类似jquery的find函数--querySelector实现等(数据深度迭代版)

(function(window) {
var methods = {
htmlToData: function(ctner) { // html反向映射到data
var pName = this.name,
vName = ctner ? ctner.getAttribute('varibleName') : null;
if (vName) {} else {
for (let key in window) {
if (typeof window[key] == 'object') {
try {
var rObj = methods.getPropByPath(window[key], pName);
if (rObj && rObj.v != undefined) {
vName = key;
if (ctner) ctner.setAttribute('varibleName', key);
break;
}
} catch (e) {}
}
}
}
var pPath = vName + '.' + pName + '=\'' + this.value + '\'';
console.log(pPath);
eval(pPath);
},
parseHtml: function({
key, value, container, fn
}) {
var inputs = methods.querySelector("[name=" + key + "]", container);
inputs.forEach(function(item, index, array) {
try { /* callback 提供设值接口 */
fn.apply(item, [value]);
} catch (e) {
item.value = value;
}
$(item).off("blur.render");
$(item).on("blur.render", function() {
methods.htmlToData.apply(this, [container]);
});
});
},
/*查找子节点,用法类似jquery的find函数,仅支持id,class,attr选择器,返回匹配的元素集*/
querySelector: function find(selector, el) {
var el = el || document;
var m = selector.match(/([#\.\[])([\w\W]+)/i);
var type, key, attrName, result = [];
if (m) { /* 正则判断选择器类型 */
if (m[1] == ".") {
type = "class";
key = m[2];
} else if (m[1] == "#") {
type = "id";
key = m[2];
}
if (m[1] == "[") {
type = "attr";
m = m[2].match(/(\w+)=((\w+(\[\d+\])*\.*)+)/i);
attrName = m[1];
key = m[2];
}
} else {
type = "tag";
key = selector;
} function findChild(node) {
var c;
for (var i = 0; i < node.childNodes.length; i++) {
c = node.childNodes[i];
if (type == "class" && c.className == key) result.push(c);
else if (type == "id" && c.id == key) {
result.push(c);
continue;
} else if (type == "attr" && c.getAttribute && c.getAttribute(
attrName) == key) {
result.push(c);
continue;
} else if (type == "tag" && c.tagName && c.tagName.toLowerCase() ==
key) {
result.push(c);
continue;
}
findChild(c);
}
}
findChild(el);
return result;
},
getPropByPath: function(obj, path) {
let tempObj = obj;
path = path.replace(/\[(\w+)\]/g, '.$1');
path = path.replace(/^\./, ''); let keyArr = path.split('.');
let i = 0; for (let len = keyArr.length; i < len - 1; ++i) {
let key = keyArr[i];
if (key in tempObj) tempObj = tempObj[key];
else {
throw new Error(
'[render warn]: please transfer a valid prop path to form item!'
);
}
}
return {
o: tempObj,
k: keyArr[i],
v: tempObj[keyArr[i]]
};
},
_copyProps: function(obj, target) {
for (var key in target) obj[key] = target[key];
},
_defProps: function(obj, ctx, key, value, props, path, bool) {
/* prop参数是为了解决二次赋值操作导致key值改变,parseHtml无法获得name名 */
(function(key, prop, props) {
Object.defineProperty(obj, key, {
set: function(newValue) {
if (bool)
methods._addProps(obj[key], newValue, ctx, path);
else key = props[prop] = newValue;
},
get: function() {
return props[prop];
}
});
})(key, key, props);
/* 实现obj.prop 和 obj.data.prop双向更新 */
(function(ctx, key, value, props, path) {
Object.defineProperty(props, key, {
set: function(newValue) {
if (key == newValue) return;
key = newValue;
path = path.replace(/\.*(undefined)*\.+/, '.')
.replace(/\.(\d+)\./g, '[+$1+].').replace(
/[+]/g,
'');
var propPath = path.slice(path.indexOf('.') + 1);
if (propPath.indexOf('data.') == 0)
propPath = propPath.replace('data.', '');
methods.parseHtml({
key: propPath,
value: newValue,
container: document.querySelector(ctx),
fn: Vue.prototype.setValue
});
},
get: function() {
return key;
}
});
props[key] = value;
})(ctx, key, value, props, path);
},
_addProps: function(obj, props, ctx, path) {
for (var key in props) {
if (typeof props[key] == 'object') {
methods._addProps(obj[key] = {}, props[key], ctx, path +
'.' +
key);
methods._defProps(obj, ctx, key, props[key], props, path +
'.' +
key, true);
} else if (typeof props[key] == 'function') obj[key] = props[
key];
else methods._defProps(obj, ctx, key, props[key], props, path +
'.' + key);
}
return obj;
},
/* obj:目标对象;props:json格式属性;path:属性路径;el:渲染域容器 */
addProps: function(obj, props, path) {
var target = obj;
if (path) target = methods.getPropByPath(obj, path).v;
methods._addProps(target, props, obj.el || 'document', '.' +
path);
}
};
_ = {
addProps: methods.addProps,
getPropByPath: methods.getPropByPath,
querySelector: methods.querySelector,
parseHtml: methods.parseHtml,
htmlToData: methods.htmlToData
};
Vue = function(param) {
methods._copyProps(this, param);
if (this.data) methods._addProps(this, this.data, this.el);
}
Vue.prototype = {
addProps: function(props, path) {
methods.addProps(this, props, path);
}
};
}(window));

2、html代码:(实现对easyui组件的设值,其他组件也可以通过设值接口实现对应设值操作)

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>javascript test</title>
<link rel="stylesheet" type="text/css" href="css/metro/easyui.css">
<link rel="stylesheet" type="text/css" href="css/icon.css">
<link rel="stylesheet" type="text/css" href="css/demo.css">
<script type="text/javascript" src="js/jquery.min.js"></script>
<script type="text/javascript" src="js/jquery.easyui.min.js"></script>
</head>
<body>
<div class="easyui-panel" title="数据驱动dom更新" style="width:500px">
<div style="padding:10px 60px 20px 60px">
<h1>F12 console 中输入:a.user.name = 'jack'; <br>
可以看到界面也会更新数据,其他更新参考脚本。</h1>
<form id="ff" class="easyui-form" method="post" data-options="novalidate:true" action="save.do">
<table cellpadding="5">
<tr>
<td>对象的名称:</td>
<td><input type="text" name="user.name" class="easyui-textbox" ></input></td>
</tr>
<tr>
<td>对象的年龄:</td>
<td><input name="user.age" class="easyui-numberbox"></td>
</tr>
<tr>
<td>对象的描述:</td>
<td>
<input name="user.addr" class="easyui-textbox" data-options="height:60,multiline:true">
</td>
</tr> <tr>
<td>纯变量数量:</td>
<td><input name="count" class="easyui-numberspinner"></td>
</tr>
<tr>
<td>纯变量选项:</td>
<td>
<select class="easyui-combobox" name="dept" style="width:150px;">
<option value="1">aitem1</option>
<option value="2">bitem2</option>
<option value="3">bitem3</option>
<option value="4">ditem4</option>
<option value="5">eitem5</option>
<option value="6">eitem6</option>
<option value="7">eitem7</option>
<option value="8">eitem8</option>
<option value="9">eitem9</option>
</select>
</td>
</tr> <tr>
<td>menber-0-name:</td>
<td><input name="menbers[0].id.code" class="easyui-textbox"></td>
</tr>
<tr>
<td>menber-0-age:</td>
<td>
<input name="menbers[0].age" class="easyui-numberbox">
</td>
</tr>
<tr>
<td>menber-1-name:</td>
<td><input name="menbers[1].name" class="easyui-textbox"></td>
</tr>
<tr>
<td>menber-1-age:</td>
<td>
<input name="menbers[1].childs[0].name" class="easyui-textbox">
</td>
</tr>
<tr>
<td>danomick:</td>
<td>
<input name="menbers[0].addrs" class="easyui-textbox">
</td>
</tr>
<tr>
<td>danomick user:</td>
<td>
<input name="user.email" class="easyui-textbox">
</td>
</tr>
<tr>
<td>danomick a:</td>
<td>
<input name="phones" class="easyui-numberbox">
</td>
</tr>
</table>
</form>
</div>
</div>
<script type="text/javascript" src="render.js"></script>
<script>
"use strict"
/* 扩展对easyui组件设值支持 */
Vue.prototype.setValue = function (value) {
var $ctn = $(this).parent().prev(), ctnClass = $ctn.attr("class"),
itemFn = ctnClass.match(/easyui-(\w+)/),itemFn = itemFn[1];
$ctn[itemFn]('setValue',value);
};
/* 组件值反射到data中机制构建 */
$.parser.onComplete = function(context){
try {
$('[type=hidden].textbox-value').each(function(){
var prev = $(this).prev(), $ctn = $(this).parent().prev(), ctnClass = $ctn.attr("class"),
itemFn = ctnClass.match(/easyui-(\w+)/),itemFn = itemFn[1];
if (prev) {
prev.off("blur.prev");
prev.on("blur.prev", function() {
var item = $(this).next()[0];
setTimeout(function(){
item.value = $ctn[itemFn]('getValue');
_.htmlToData.apply(item);
},150);
});
}
});
} catch (e) {}
}
/* 模仿Vue */
var a = new Vue({
el: '#ff', /* 指定扫描的容器 */
data:{ /* 要处理的数据 */
user:{
name: 'json',
age: 26,
addr: 'usa'
},
menbers:[],
count: 10,
dept: 6
}
});
/* 覆盖原有属性会自动建立映射关系 */
a.menbers = [
{
mName: 'jack',
age: 26,
id: {
code: 'idCard'
}
},
{
name: 'timy',
age: 20,
childs:[
{name: 'kimi'},
{name: 'coco'}
]
}
];
/* 针对没有的属性提供属性添加API(API会建立映射关系) */
a.addProps({email:'czm@139.com'},'user');
a.addProps({addrs:'lin ken street'},'data.menbers[0]');
a.addProps({phones:13900139000});
$("#scriptContent").text(JSON.stringify(a.data));
/* 模拟数据更新 */
// var int = setInterval(function () {
// a.user.age = Math.round(Math.random()*100);
// a.data.user.addr = new Date();
// a.count = Math.round(Math.random()*10000);
// a.data.dept = Math.round(Math.random()*8 + 1);
// },1500);
</script>
</body>
</html>

后续会逐步更新 ... ...