2014-09-09 2 views
0

Следующий пример основан на Mike Bostock's reusable charts proposal.Обмен функциями геттера/сеттера по Javascript-коду

Указанные две функций (bar() и pie()), которые каждый генерируют различный вид диаграммы:

function bar() { 
    var width = 720, // default width 
     height = 80; // default height 

    function my() { 
    console.log('bar created: ' + width + ' ' + height); 
    } 

    my.width = function(value) { 
    if (!arguments.length) return width; 
    width = value; 
    return my; 
    }; 

    my.height = function(value) { 
    if (!arguments.length) return height; 
    height = value; 
    return my; 
    }; 

    my.render = function() { 
    my(); 

    return my; 
    }; 

    return my; 
} 

function pie() { 
    var width = 720, // default width 
     height = 80; // default height 

    function my() { 
    console.log('pie created: ' + width + ' ' + height); 
    } 

    my.width = function(value) { 
    if (!arguments.length) return width; 
    width = value; 
    return my; 
    }; 

    my.height = function(value) { 
    if (!arguments.length) return height; 
    height = value; 
    return my; 
    }; 

    my.render = function() { 
    my(); 

    return my; 
    }; 

    return my; 
} 

я могу назвать эти функции с помощью метода цепного как таковые:

bar().width(200).render(); // bar created: 200 80 
pie().height(300).render(); // pie created 720 300 

Есть способ кодировать их, когда мои методы getter и setter одинаковы? Например, я планирую, что функции width() в каждом из них будут такими же. Как я могу заставить функции и pie() наследовать общие функции, такие как width(), height(), render()?

+0

Theres старое сообщение, написанное john resig о наследовании в javascript, которое может служить тому, что вы пытаетесь сделать. http://ejohn.org/blog/simple-javascript-inheritance/. Также может стоить искать обновленную/адаптированную версию. Вот один, который я нашел с быстрым поиском Google http://stackoverflow.com/questions/15050816/is-john-resigs-javascript-inheritance-snippet-deprecated – user1318677

ответ

2

Правильно ... Дело в том, что этот особый стиль создания экземпляров и отключение дополнительных методов от них состоит в том, что переменные должны быть частью области действия, которая возвращает возвращаемую функцию (bar или pie). Поскольку эти переменные являются внутренними и недоступными вне этой области, нет способа получить их при расширении экземпляра.

Прежде чем идти дальше, обратите внимание, что ваша реализация немного выключена. Во-первых, где вы console.log('bar created: ' + width + ' ' + height);, семантически это неверно. На самом деле это не то место, где оно создано, а именно то, где оно отображается. Он создается, когда вы звоните bar().

Затем, когда вы делаете эту диаграмму вместо bar().width(200).render(), то, что вы должны делать, например,

var barChart = bar().width(200); 

d3.select('svg') 
    .append('g') 
    .call(barChart) 

Для этого не требуется render(). Вместо этого тело my() - это ваш рендер. Но, согласно предложению mbostocks, my() должен принять выбор d3 как param, который в этом примере будет добавлен элемент g. Это относится к руководству по многоразовым диаграммам, к которому вы привязались; перечитайте его, и вы поймете, что я имею в виду.

И наконец, в ответ на ваш вопрос. То, как я это делаю, - d3.rebind. Во-первых, вы должны создать общий базовый класс, который имеет внутреннюю width переменную и width() геттер/сеттер, например так:

function base() { 
    var width; 

    function my(selection) { 
    console.log('base: ' + width); 
    } 

    my.width = function(value) { 
    if (!arguments.length) return width; 
    width = value; 
    return my; 
    }; 

    return my; 
} 

Далее, вы хотите сделать barpie) существенно расширить базу, как это:

function bar() { 
    var _base = base(); // instantiate _base, which has width() method 

    function my(selection) { 
    // In here, if you want to get width, you call `_base.width()` 
    // and, if there's some shared rendering functionality you want 
    // to put in base, you can run it with `selection.call(_base)` 
    } 

    d3.rebind(my, _base, 'width'); // make width() a function of my 
    return my; 
} 

последняя строка, копии width() в my, так что вы можете позвонить bar().width(). Here's a fiddle that works.

+0

Отличный ответ! Например, пример Майка Бостока кажется немного неинтуитивным, чтобы создать базовую функцию и наследовать разные типы графов ... ответ, который вы предоставляете, верен, но предложите ли вы другой шаблон? – cereallarceny

+0

@cereallarceny Несмотря на то, что я указал на недостаток, мне нравится этот шаблон d3/mbostock, и я сам использую его сам. Но, как правило, я использую его только при написании многоразовых диаграмм с d3 - не для других вещей, таких как модели данных или что-то еще. Он отлично работает в d3 из-за 'идиома' select.call (myInstance) d3 и того факта, что 'myInstance' является функцией со своими собственными геттерами/сеттерами. – meetamit

+0

Кроме того, в исходном коде d3 есть примеры 'd3.rebind', используемые для расширения экземпляров. 'd3.layout.hierarchy' - это базовый класс' d3.layout.treemap', 'd3.layout.cluster' и других макетов. – meetamit

Смежные вопросы