Плагин jQuery - глубокая модификация опции - PullRequest
6 голосов
/ 07 декабря 2011

В настоящее время я работаю над плагином с достаточно глубокой переменной настроек (3-4 уровня в некоторых местах). Следуя общепринятому шаблону jQuery Plugin, я реализовал простой способ для пользователей изменять настройки на лету, используя следующую запись:

$('#element').plugin('option', 'option_name', 'new_value');

Вот код, аналогичный тому, который я сейчас использую для метода параметров.

option: function (option, value) {
    if (typeof (option) === 'string') {
        if (value === undefined) return settings[option];

        if(typeof(value) === 'object')
            $.extend(true, settings[option], value);
        else
            settings[option] = value;
    }

    return this;
}

Теперь посмотрим, что у меня есть переменная настроек, например:

var settings = {
    opt: false,
    another: {
        deep: true
    }
 };

Если я хочу изменить настройки deep, я должен использовать следующую запись:

$('#element').plugin('option', 'another', { deep: false });

Однако, поскольку на практике мои настройки могут иметь глубину 3-4 уровня, я считаю, что следующие обозначения были бы более полезными:

$('#element').plugin('option', 'another.deep', false);

Однако я не уверен, насколько это возможно, и как это сделать. В качестве первой попытки я попытался «перейти» к рассматриваемому варианту и установить его, но если я установлю свою переменную перемещения, она не установит то, на что она ссылается в исходной переменной настроек.

option: function (option, value) {
    if (typeof (option) === 'string') {
        if (value === undefined) return settings[option];

        var levels = option.split('.'),
            opt = settings[levels[0]];
        for(var i = 1; i < levels.length; ++i)
            opt = opt[levels[i]];

        if(typeof(value) === 'object')
            $.extend(true, opt, value);
        else
            opt = value;
    }

    return this;
}

Скажем по-другому: установив opt после обхода, параметр, на который он фактически ссылается в переменной settings, не изменяется после выполнения этого кода.

Прошу прощения за длинный вопрос, любая помощь приветствуется. Спасибо!


EDIT

В качестве второй попытки я могу сделать это, используя eval() примерно так:

option: function (option, value) {
    if (typeof (option) === 'string') {
        var levels = option.split('.'),
            last = levels[levels.length - 1];

        levels.length -= 1;

        if (value === undefined) return eval('settings.' + levels.join('.'))[last];

        if(typeof(value) === 'object')
            $.extend(true, eval('settings.' + levels.join('.'))[last], value);
        else 
            eval('settings.' + levels.join('.'))[last] = value;
    }

    return this;
}

Но я бы очень хотел узнать, может ли кто-нибудь показать мне способ , а не использовать eval. Так как это строка ввода пользователя, я бы не стал запускать eval(), потому что это может быть что угодно. Или дайте мне знать, если я параноик, и это вообще не должно вызывать проблем.

Ответы [ 3 ]

2 голосов
/ 08 декабря 2011

Проблема, с которой вы здесь сталкиваетесь, заключается в разнице между переменными, указывающими на объекты, и переменными для других типов, таких как строки. 2 переменные могут указывать на один и тот же Object, но не на один и тот же String:

  var a = { foo: 'bar' };
  var b = 'bar';
  var a2 = a;
  var b2 = b;
  a2.foo = 'hello world';
  b2 = 'hello world';
  console.log(a.foo); // 'hello world'
  console.log(b); // 'bar'

Ваш код обхода прекрасно работает до последней итерации цикла, при которойточка opt - это переменная, содержащая то же значение , что и deep внутри объекта settings.opt.another.Вместо этого обрежьте цикл и используйте последний элемент levels в качестве key, например

  var settings = {
    another: {
      deep: true
    }
  };

  var levels = 'another.deep'.split('.')
    , opt = settings;

  // leave the last element
  var i = levels.length-1;

  while(i--){
    opt = opt[levels.shift()];
  }
  // save the last element in the array and use it as a key
  var k = levels.shift();
  opt[k] = 'foobar'; // settings.another.deep is also 'foobar'

. На этом этапе opt является указателем на тот же Object, что и settings.anotherk представляет собой String со значением 'deep'

0 голосов
/ 08 декабря 2011

Не будет ли встроенный jQuery $().extend() точно тем, что вам нужно?

http://api.jquery.com/jQuery.extend/

* обратите внимание, что сигнатура второго метода с первым аргументом true выполняет глубокоеслияния ...

0 голосов
/ 07 декабря 2011

Как насчет использования eval, а не обхода?

var settings = {
opt: false,
   another: {
 deep: true,

   }
};

var x = "settings.another";
eval(x).deep = false;
alert(settings.another.deep);
...