2017-04-25 156 views
0

我使用d3.js很多,並且最近一直在思考他們的linearScale函數。如何使用可調用方法創建對象,但我也可以像函數一樣調用對象

它可以讓你做到這一點:

var x = d3.scaleLinear() 
    .domain([10, 130]) 
    .range([0, 960]); 

x(20); // 80 
x(50); // 320 

至於我可以告訴大家:

  • X裝d3.scaleLinear的返回值()
  • x是對象樣,因爲我們可以調用方法的'範圍'和'域'
  • x也是函數式的,因爲我們可以稱它爲'x(20)'並得到返回值

我不需要知道任何關於d3如何實現這一點的具體內容,d3僅僅是讓我想到API的第一個例子,它允許我這樣做。

我有興趣在javascript中實現類似的東西,並希望知道最簡單形式的必要設計模式。

我曾試着尋找d3的源代碼,但有很多額外的功能,它分裂成多個文件 - 這使我很難找到我正在尋找的東西,尤其是當我不喜歡不知道那是什麼。任何幫助將非常感激。

+1

_ 「X擁有d3.scaleLinear的)的返回值(」 _:不。 'x'實際上是無論'.range'返回。 – Cerbrus

+0

@Cerbrus我想.range返回'this',因爲您可以按任意順序鏈接.domain和.range方法。 – Martha

+1

你應該看看[JavaScript關閉](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures)。 –

回答

2

首先要注意的是,在JavaScript函數中只是可以附加屬性的對象,這些屬性可以包含其他函數。

基本上scaleLinear()返回一個函數,該函數又被用作API對象。

附加到API對象/函數的函數用於獲取或設置由封裝閉包創建的內部狀態。

附加到API對象/函數的函數通常返回API實例以允許調用鏈接。

希望下面的例子將闡明如何開發類似的東西。

function Greet() { 
 
    var person = ''; 
 
    var message = 'Hello'; 
 

 
    var api = function() { 
 
    console.log(message + ' ' + person); 
 
    return api; 
 
    }; 
 

 
    api.person = function (value) { 
 
    // if nothing is passed into function act as getter 
 
    if (!arguments.length) return person; 
 
    // otherwise set the value 
 
    person = value; 
 
    // return the api function for more chaining or calling 
 
    return api; 
 
    }; 
 

 
    api.message = function (value) { 
 
    if (!arguments.length) return message; 
 
    message = value; 
 
    return api; 
 
    }; 
 

 
    return api; 
 
} 
 
    
 
var greeter = Greet().person('Daniel'); 
 

 
greeter(); // 'Hello Daniel' 
 

 
greeter.message('Whaddup').person('Martha')(); // 'Whaddup Martha'

我只記得邁克·博斯托克(D3的創造者)寫了一篇博客文章,他在相當多的細節涵蓋了此模式:https://bost.ocks.org/mike/chart/

0

我發現this tutorial很實用

我有這種方法的樣板

function getChart(params) { 
 
    // exposed variables 
 
    var attrs = { 
 
     svgWidth: 400, 
 
     svgHeight: 400, 
 
     marginTop: 5, 
 
     marginBottom: 5, 
 
     marginRight: 5, 
 
     marginLeft: 5, 
 
     data: null 
 
    }; 
 

 

 
    /*############### IF EXISTS OVERWRITE ATTRIBUTES FROM PASSED PARAM ####### */ 
 

 
    var attrKeys = Object.keys(attrs); 
 
    attrKeys.forEach(function (key) { 
 
     if (params && params[key]) { 
 
      attrs[key] = params[key]; 
 
     } 
 
    }) 
 

 

 
    //innerFunctions 
 
    var updateData; 
 

 

 
    //main chart object 
 
    var chart = function (selection) { 
 
     selection.each(function() { 
 

 
      //calculated properties 
 
      var calc = {} 
 

 
      calc.chartLeftMargin = attrs.marginLeft; 
 
      calc.chartTopMargin = attrs.marginTop; 
 

 
      calc.chartWidth = attrs.svgWidth - attrs.marginRight - calc.chartLeftMargin; 
 
      calc.chartHeight = attrs.svgHeight - attrs.marginBottom - calc.chartTopMargin; 
 

 
      //drawing 
 
      var svg = d3.select(this) 
 
       .append('svg') 
 
       .attr('width', attrs.svgWidth) 
 
       .attr('height', attrs.svgHeight) 
 
      // .attr("viewBox", "0 0 " + attrs.svgWidth + " " + attrs.svgHeight) 
 
      // .attr("preserveAspectRatio", "xMidYMid meet") 
 

 

 
      var chart = svg.append('g') 
 
       .attr('width', calc.chartWidth) 
 
       .attr('height', calc.chartHeight) 
 
       .attr('transform', 'translate(' + (calc.chartLeftMargin) + ',' + calc.chartTopMargin + ')') 
 

 

 

 

 

 
      // smoothly handle data updating 
 
      updateData = function() { 
 

 

 
      } 
 

 

 
     }); 
 
    } 
 

 

 
    //exposed variable funcs 
 
    chart.data = function (value) { 
 
     if (!arguments.length) return attrs.data; 
 
     attrs.data = value; 
 
     if (typeof updateData === 'function') { 
 
      updateData(); 
 
     } 
 
     return chart; 
 
    } 
 

 
    chart.width = function (value) { 
 
     if (!arguments.length) return attrs.svgWidth; 
 
     attrs.svgWidth = value; 
 
     return chart; 
 
    } 
 

 
    chart.height = function (value) { 
 
     if (!arguments.length) return attrs.svgHeight; 
 
     attrs.svgHeight = value; 
 
     return chart; 
 
    } 
 

 

 
    return chart; 
 
}

然後就可以調用圖像