Как я могу лениво оценивать поля в объекте javascript?

#javascript #lazy-evaluation

#javascript #ленивая оценка

Вопрос:

Мне интересно, возможно ли сделать что-то вроде следующего:

 var obj = {
   counter: (function(){
                if(!this.val){ this.val = 0; }
                this.val  = 1;
                return this.val;
            })();
};

console.log(obj.counter); //should output 1
console.log(obj.counter); //should output 2
console.log(obj.counter); //should output 3
...
  

Есть ли способ получить поле из такого объекта, чтобы оно переоценивало функцию при каждом обращении к ней?

Комментарии:

1. Почему бы вам не использовать функцию вместо поля

2. Счетчик определяется как IIFE, поэтому ваша ссылка this теряется для самого IIFE. Таким образом, каждый раз, когда вы вызываете obj.counter , this.val будет неопределенным и, следовательно, будет установлено значение 0, затем 1. @ksven опередил меня с кодом ответа, поэтому отправляю в качестве комментария.

3. хорошо, могу ли я поместить фактическую переменную подсчета в другое место и при этом получить автоматическое увеличение?

Ответ №1:

Вы можете использовать геттер:

 var obj = {};
Object.defineProperty(obj,"counter",{
    get: function() {
        this.val = this.val || 0;
        this.val  ;
        return this.val;
    }
});

console.log(obj.counter); // 1
console.log(obj.counter); // 2
console.log(obj.counter); // 3
  

Комментарии:

1. Это здорово, я не знал, что он существует. Спасибо!

Ответ №2:

Это возможно с прокси-серверами, если ваша целевая платформа их поддерживает:

 var obj = Proxy.create({
    get: function(target, value) {
        if(value == 'counter')
            return this.val = (this.val || 0)   1;
    }
});

console.log(obj.counter); //should output 1
console.log(obj.counter); //should output 2
console.log(obj.counter); //should output 3
  

Другим вариантом может быть геттер:

 obj = Object.create({}, {
    counter: {
        get: function() {
            return this.val = (this.val || 0)   1;
        }
    }
})
  

или valueOf объект (это не работает с console.log , но работает с арифметикой):

 var obj = {
    counter: {
        valueOf: function() {
            return this.val = (this.val || 0)   1;
        }
    }
};

console.log(obj.counter 5); // 6
console.log(obj.counter 5); // 7
console.log(obj.counter 5); // 8
  

Ответ №3:

Использование Proxy класса, а также варианта использования для оценки требований для оценки свойств

 /* https://github.com/hack2root/lazyeval */

let lazy = (eval) => ((data) => new Proxy(data, {
  set(obj, key, val) {
  obj[key] = val;
  eval(obj);
  }
}))({});

// 1. ARRANGE
let a = 1;
let b = 2;
let c;

// 2. ACT
let func = lazy((f) => {
  if (f.a amp;amp; f.b) { 
    c = f.a   f.b 
  }
});

func.a = a;
func.b = b;

// 3. ASSERT
console.log("c is", c);

let lazy_require = (requre) => (eval) =>  ((data) => new Proxy(data, {
    set(obj, key, val) {
        obj[key] = val;
        if (requre(obj)) {
            eval(obj);
        }
    }
}))({});

// 1. ARRANGE
let a_f = 1;
let b_f = 2;
let c_f;

// 2. ACT
let func_reqire = lazy_require((f) => f.a amp;amp; f.b);

let lazy_func = func_reqire((f) => {
  c_f = f.a   f.b
});

lazy_func.a = a_f;
lazy_func.b = b_f;

// 3. ASSERT
console.log('c_f is', c_f);

let lazy_require_data = (requre) => (eval) => (data) => new Proxy(data, {
  set(obj, key, val) {
    obj[key] = val;
    if (requre(obj)) {
      eval(obj);
    }
  }
});

// 1. ARRANGE
let a_data = 1;
let b_data = 2;
let c_data;

// 2. ACT
let func_require_data = lazy_require_data((f) => f.a amp;amp; f.b);

let func_data = func_require_data((f) => {
    c_data = f.a   f.b
});

let func_json = func_data({
    a: a_data,
    b: b_data
});

func_json.a = a;
func_json.b = b;

// 3. ASSERT
console.log('c_data is', c_data);