@aono1234

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

chart.jsのクラス継承

chartクラスをオリジナルサブクラスに継承してアレンジしたい。

以前質問させて頂きchart.jsがクラスだとわかりました。
これを継承し、サブクラスを作って自分で使い勝手の良いオリジナルクラスを
作りたいと思ったのですが、
うまく継承ができずエラーが発生してしまいます。
何が問題でしょうか?そもそもChartは継承ができないのでしょうか?
(オリジナルのスーパークラスはサブクラスに継承できました)

もし継承ができないのであれば、なぜできないのか理由も併せて回答いただけると幸いです。

発生している問題・エラー

Chart.min.js:10 Uncaught TypeError: Cannot read properties of undefined (reading 'length')
    at Object.acquireContext (Chart.min.js:10:122977)
    at edit.construct (Chart.min.js:10:53292)
    at new t (Chart.min.js:10:76902)
    at new edit (individual_analysis.php:85:9)
    at individual_analysis.php:89:11

ソースコード

class edit extends Chart {
    constructor(){
        super()
    }
}

var test= new edit;



var ctx = document.getElementById('myChart');
var myChart = new test(ctx, {
	type: "bar",
	data:{
		labels:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],
		datasets:[
			{
				label:'line1',
				type:'line',
				data:[6,3,2,2,4,4,3],
				backgroundColor: "red",
				borderColor : "rgba(254,97,132,0.8)",
				fill: 'false',
				yAxisID: 'y-axis-2',
			},
			{
				label:'line2',
				type:'line',
				data:[2,3,4,5,6,7,8],
				backgroundColor: "blue",
				borderColor : "rgba(254,97,132,0.8)",
				fill: false,
				yAxisID: 'y-axis-2',
			},
			{
				label:'line3',
				type:'line',
				data:[5,5,5,5,5,5,5,5,5,5,5,5],
				backgroundColor: "red",
				borderColor : "red",
				fill: false,
				yAxisID: 'y-axis-2',
			},
			{
				label:'数値A',
                type:'bar',
				data:[0,0,1,1,1,1,1,3,2,1,],
				backgroundColor: "#00ff7f",
				yAxisID: 'y-axis-sales',
			},
			{
				label:'数値B',
                type:'bar',
				data:[3,3,3,3,3,3,3],
				backgroundColor: "#008000",
				yAxisID: 'y-axis-sales'
			},
            {
				label:'数値C',
                type:'bar',
				data:[3,3,3,3,3,3,3],
				backgroundColor: "skyblue",
				yAxisID: 'y-axis-sales'
			},
		]
	},
	options:{
		title: {                           //タイトル設定
			display: true,                 //表示設定
			fontSize: 18,                  //フォントサイズ
			text: '月別の数値'                //ラベル//
		},
		legend: {
			display: true,
			position: 'left',
			label:'test', 
		},
		scales:{
			xAxes:[{stacked:true,}],
			yAxes:[{
				id: 'y-axis-sales',//積み上げるY軸
				stacked: true,
				type: 'linear',
				display: true,
				position: 'left',
				ticks: {
					beginAtZero: true,
					min: 0,
					max: 10,
					stepSize: 2,
				},
			},{
				id: 'y-axis-2',//積み上げないY軸
				stacked: true,
				type: 'linear',
				display: true,//隠します
				position: 'right',
				ticks: {
					beginAtZero: true,
					min: 0,
					max: 20,
					stepSize: 5,
				},
			}]
		},
	}
});
0 likes

4Answer

@kazukinagata さんの回答には

classではないのでextendsによる継承もできないはずです。

とありますが、実際にはコンストラクタ関数ベースの「クラス」も extends できます。

よって

class edit extends Chart {
    constructor(){
        super()
    }
}

ここまでは構文としては合っています。間違っているのは

var test= new edit;
...
var myChart = new test(ctx, {

で、 new edit して作ったオブジェクトをさらに new しているところです。正しくは

var myChart = new edit(ctx, {

です。

さらに、 edit に自前のコンストラクタを定義しているのに引数を取らず、 super() に渡してないせいで、 new edit(...) の引数が捨てられているのも問題です。以下の部分は消してください。

    constructor(){
        super()
    }

まとめると、

class edit extends Chart {}
var ctx = document.getElementById('myChart');
var myChart = new edit(ctx, {

とすれば正しく動きます。

2Like

Comments

  1. 質問者の方へ、この辺のややこしい議論はこの記事を読むと理解が進むかもしれません。

    https://www.yunabe.jp/docs/javascript_class_in_google.html

    要するに、jsには他の言語のclassに相当する概念が元来なく、ES6以降の新世代でようやくclassという糖衣構文が用意されて他の言語ライクに書けるようになりました。
    しかし、この糖衣構文を正しく解釈してくれるかはブラウザ次第で、未だ全体の7%はclassやextendsといった構文を理解してくれない、ということです。

    通常、ブラウザで使用するライブラリは、classなど比較的新しい構文で書いたjsを、わざわざES5という古いタイプの書き方に変換(トランスパイル)して公開する、というのが実情です。

    https://www.yunabe.jp/docs/javascript_class_in_google.html

前回回答したものです。
補足でコメントしたものは読んでいただけましたか?
jsに、厳密にclassといった概念はなく、ここでのChartは正確にはclassではなくコンストラクタ関数です。classではないのでextendsによる継承もできないはずです。

ややこしいことにES6という世代からclass構文が書けるようになりましたが、これも実際にはコンストラクタ関数の糖衣構文にすぎません。
前回の他の回答者がclassです、と明言していたので気になっていたのですが、やはり混乱しますよね。

0Like

Comments

  1. @aono1234

    Questioner

    再度ご回答頂き、大変ありがたく思います。すみません、つい先ほど補足を読ませていただきました。
    厳密にはclassではないこと承知しました。
    そもそも、継承したいと思ったのはクラスの中に追加でメソッドを記載し、グラフ表示をボタンによって変えたいというのが発端でした。

    回答者様のおっしゃる通り、コンストラクタ関数での継承はできないので、インスタンスを何個か作ってifやswitchでうまくグラフ表示が切り替えるようにしてみます。ほかにいい方法があれば、恐縮ですがご教授いただけると幸いです。
  2. インスタンスのメソッドを増やしたいのであれば、前回の補足に書いたように、Chart.prototypeに追加のメソッドを生やしてやることで実現できるかとは思います。

    一方、仰るように表示したいチャートごとにチャート表示用のモジュールを作って、各モジュールがChartのインスタンスを一つ持つ、というような書き方もできます。

    個人的には何か具体的な不都合がない限り、後者の方法で書き始めます。前者の方はjs特有のthisの扱いで混乱するでしょうし、大本のライブラリの仕様変更に追従するコストが増えるからです。なるべくライブラリを素直に使いたい考えです。

@kazukinagata 非対応になっているのはそもそも class 構文に対応していない古いブラウザですね。 class 構文に対応しているブラウザならコンストラクタ関数も extends できます。それがクラスの仕様なので。

The Function constructor [...] may be used as the value of an extends clause of a class definition.

追記:↑引用元を間違えました。これは Function オブジェクトを extends して class Foo extends Function と書けることの説明ですね。今正しい説明を探しています……

追記2: https://tc39.es/ecma262/#sec-runtime-semantics-classdefinitionevaluation の8.h にコンストラクタ関数を extends する処理が書いてありました。

0Like

Comments

  1. @aono1234

    Questioner

    お二人ともご回答ありがとうございます。具体的なコードの提示も大変助かりました。
    おっしゃる通りにコーディングしたら動くようになりました。
    また
    edit.prototype.test3 = function(){
    console.log("test")
    }
    と書けばメソッドの追加もできました。ありがとうございます!

Your answer might help someone💌