Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/373.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何为嵌套对象使用javascript代理_Javascript_Proxy_Ecmascript 6_Es6 Proxy - Fatal编程技术网

如何为嵌套对象使用javascript代理

如何为嵌套对象使用javascript代理,javascript,proxy,ecmascript-6,es6-proxy,Javascript,Proxy,Ecmascript 6,Es6 Proxy,我在js bin中有以下代码: var validator = { set (target, key, value) { console.log(target); console.log(key); console.log(value); if(isObject(target[key])){ } return true } } var person = { firstName: "alfred", lastNa

我在js bin中有以下代码:

var validator = {
  set (target, key, value) {
    console.log(target);
    console.log(key);
    console.log(value);
    if(isObject(target[key])){

    }
    return true
  }
}


var person = {
      firstName: "alfred",
      lastName: "john",
      inner: {
        salary: 8250,
        Proffesion: ".NET Developer"
      }
}
var proxy = new Proxy(person, validator)
proxy.inner.salary = 'foo'
如果我做了
proxy.inner.salary=555它不工作

但是,如果我使用proxy.firstName=“Anne”
,那么它的效果会非常好

我不明白为什么它不能递归地工作


您可以添加一个
get
陷阱,并返回一个新的代理,其中
验证程序作为处理程序:

var验证程序={
获取(目标、关键){
if(目标[键]==='object'&&target[键]!==null的类型){
返回新代理(目标[key],验证程序)
}否则{
返回目标[键];
}
},
设置(目标、键、值){
控制台日志(目标);
控制台日志(键);
console.log(值);
返回真值
}
}
个人变量={
名字:“阿尔弗雷德”,
姓:“约翰”,
内部:{
工资:8250,
声明:“.NET开发者”
}
}
var proxy=新代理(人员、验证人)
proxy.inner.salary='foo'
我发布了一个也可以这样做的。它还将向回调函数报告所做的修改及其完整路径


Michal的答案很好,但每次访问嵌套对象时,它都会创建一个新的
代理。根据您的使用情况,这可能会导致非常大的内存开销。

我还创建了一个库类型函数,用于观察深度嵌套代理对象的更新(我创建它是为了用作单向绑定数据模型)。与Elliot的库相比,在<100行时更容易理解。此外,我认为Elliot对正在生成的新代理对象的担心是一种过早的优化,因此我保留了该功能以简化对代码功能的推理

observable model.js

希望这是有用的

对示例稍作修改,这种方法的好处是只创建一次嵌套代理,而不是每次访问一个值

如果要访问的代理的属性是对象或数组,则该属性的值将替换为另一个代理。getter中的
isProxy
属性用于检测当前访问的对象是否为代理。您可能需要更改isProxy的名称,以避免与存储对象的属性发生命名冲突

注意:嵌套代理是在getter中定义的,而不是在setter中定义的,因此仅当数据实际在某个地方使用时才创建它。这可能适合您的用例,也可能不适合您的用例

const处理程序={
获取(目标、关键){
如果(键=='isProxy')
返回true;
常量属性=目标[键];
//如果未找到属性,则返回
if(typeof prop==“未定义”)
返回;
//将值设置为代理(如果是对象)
如果(!prop.isProxy&&typeof prop==='object')
target[key]=新代理(prop,handler);
返回目标[键];
},
设置(目标、键、值){
log('Setting',target,'.${key}等于',value);
//todo:呼叫回调
目标[键]=值;
返回true;
}
};
常数测试={
字符串:“数据”,
电话:231321,
对象:{
字符串:“数据”,
电话:32434
},
数组:[
1, 2, 3, 4, 5
],
};
const proxy=新代理(测试,处理程序);
console.log(代理);
console.log(proxy.string);//“数据”
proxy.string=“Hello”;
console.log(proxy.string);//“你好”
console.log(proxy.object);//{“字符串”:“数据”,“数字”:32434}
proxy.object.string=“World”;

console.log(proxy.object.string);//“World”
Nested表示“多个对象”,这意味着您需要多个代理来检测每个对象上的所有属性访问,而不仅仅是根对象。谢谢,如果target[key]是一个对象数组呢?我想我们可以映射validator?@robertking数组也是一个对象,所以它是对象中的一个对象,这段代码应该适用于深度嵌套的对象。日期和数组对我来说不太合适,可能是因为angular ngFor和datePipes使用了原型属性。我已经在下面发布了我的修改后的解决方案,看起来很有效,谢谢。但这样每次它都会返回一个新的代理实例。如果创建了同一个代理实例,是否还可以返回它?如果我执行了
const-inner=person.inner,这是否有效;内部薪酬=1000?(编辑:哦,我明白了。代理将从第一个get now返回,因此inner将是一个代理。很酷。这是否还有其他缺点或漏洞,或者它是否可以100%用于任何数量的深度嵌套对象?),..  我解决这个问题的方法是跟踪
WeakMap
中的代理。除非您硬编码所有对象路径,否则这个库实际上不会进行深度复制。@HDog-Hmm,您指的是哪个库?Observable Slim的目的不是深度复制对象——它的目的是观察对象上的更改以及任何深度嵌套子对象上的更改。我尝试访问proxy.inner.salary 1亿次,没有看到任何内存增加。我认为这个答案是不正确的,这让迈克尔的答案名声扫地。垃圾收集似乎在这种情况下起作用。@KilianHertel看一下Michal回答中的第一个
if
语句。如果访问的属性是非空的
对象
,它将创建一个新的
代理。因此,当然,根据您的使用情况,创建一组新的代理对象很可能会增加内存使用。根据垃圾收集情况,您的里程数会有所不同。詹姆斯提供的另一个答案也解决了这个问题。为什么我的回答会给迈克尔的回答加上“坏名声”?我说他的答案很好,甚至我自己也投了更高的票……我相信。iBindingProxy应该是iProxy?如果您使用的是节点v10+,您也可以使用iProxy,而不是手动“设置”iProxy
let ObservableModel = (function () {
    /*
    * observableValidation: This is a validation handler for the observable model construct.
    * It allows objects to be created with deeply nested object hierarchies, each of which
    * is a proxy implementing the observable validator. It uses markers to track the path an update to the object takes
    *   <path> is an array of values representing the breadcrumb trail of object properties up until the final get/set action
    *   <rootTarget> the earliest property in this <path> which contained an observers array    *
    */
    let observableValidation = {
        get(target, prop) {
            this.updateMarkers(target, prop);
            if (target[prop] && typeof target[prop] === 'object') {
                target[prop] = new Proxy(target[prop], observableValidation);
                return new Proxy(target[prop], observableValidation);
            } else {
                return target[prop];
            }
        },
        set(target, prop, value) {
            this.updateMarkers(target, prop);
            // user is attempting to update an entire observable field
            // so maintain the observers array
            target[prop] = this.path.length === 1 && prop !== 'length'
                ? Object.assign(value, { observers: target[prop].observers })
                : value;
            // don't send events on observer changes / magic length changes
            if(!this.path.includes('observers') && prop !== 'length') {
                this.rootTarget.observers.forEach(o => o.onEvent(this.path, value));
            }
            // reset the markers
            this.rootTarget = undefined;
            this.path.length = 0;
            return true;
        },
        updateMarkers(target, prop) {
            this.path.push(prop);
            this.rootTarget = this.path.length === 1 && prop !== 'length'
                ? target[prop]
                : target;
        },
        path: [],
        set rootTarget(target) {
            if(typeof target === 'undefined') {
                this._rootTarget = undefined;
            }
            else if(!this._rootTarget && target.hasOwnProperty('observers')) {
                this._rootTarget = Object.assign({}, target);
            }
        },
        get rootTarget() {
            return this._rootTarget;
        }
    };

    /*
    * create: Creates an object with keys governed by the fields array
    * The value at each key is an object with an observers array
    */
    function create(fields) {
        let observableModel = {};
        fields.forEach(f => observableModel[f] = { observers: [] });
        return new Proxy(observableModel, observableValidation);
    }

    return {create: create};
})();
// give the create function a list of fields to convert into observables
let model = ObservableModel.create([
    'profile',
    'availableGames'
]);

// define the observer handler. it must have an onEvent function
// to handle events sent by the model
let profileObserver = {
    onEvent(field, newValue) {
        console.log(
            'handling profile event: \n\tfield: %s\n\tnewValue: %s',
            JSON.stringify(field),
            JSON.stringify(newValue));
    }
};

// register the observer on the profile field of the model
model.profile.observers.push(profileObserver);

// make a change to profile - the observer prints:
// handling profile event:
//        field: ["profile"]
//        newValue: {"name":{"first":"foo","last":"bar"},"observers":[{}
// ]}
model.profile = {name: {first: 'foo', last: 'bar'}};

// make a change to available games - no listeners are registered, so all
// it does is change the model, nothing else
model.availableGames['1234'] = {players: []};