質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

2回答

2909閲覧

関数定義時に exist チェックする必要性

lazex

総合スコア604

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

2グッド

1クリップ

投稿2016/08/24 14:06

関数定義をするときに存在するかチェックして、存在しなければ定義するというコードをたまに見かけます。
タグは PHP と JavaScript ですが、私がよく見かけると思っただけで他の言語でもあると思います。

すでにある関数を上書き・もしくは上書きできないことによるエラーを防ぐためなのかと思うのですが、そのエラーを防いだところで定義する関数が polyfill でもなければムダだと思います。
必要なケースってありますでしょうか?

以下私の考えです。

チェックあり+存在する場合

関数は定義されない。
polyfill ならば、元からある関数と同じ動きなので問題ないが、そうでない場合は別の動きとなるため、自分が定義した関数のつもりで引数を渡しても引数の数や渡すデータが既存の関数とは全然違うのでエラー、もしくは運良く数や型があっていてエラーが起きなくても返り値は期待通りにならないはず。

チェックする+存在しない場合

普通に定義される。
自分で定義した関数に引数を渡すだけなので意図した動きになっているはず。

チェックしない+存在する場合

上書きできる場合は上書きされる。
既存の関数として呼び出している部分(ライブラリなど)は動かなくなるが、自分で書いた部分は少なくとも自分で定義するはずの関数に合わせた引数なので動くはず。

上書きできない場合はそこでエラーが起きる。

チェックしない+存在しない場合

チェックして存在しない時と同様で普通に定義される。
自分で定義した関数に引数を渡すだけなので意図した動きになっているはず。


存在する場合にチェックするということは、自分定義した関数で実行されるか元からある関数で実行されるかわからない状態になります。
エラーが起きずに返り値が返ってきてしまうとどこが原因なのかわかりづらく探すのにこまると思います。

チェックしない場合は上書きできない言語ではエラーが出るのでどこが原因かすぐにわかると思います。
JavaScript のような上書きできる場合は自分で書いたコード外でエラーが出るかもしれないですが、それをチェックするなら「関数がなければ定義する」ではなく「関数があればエラー出す」とすべきだと思います。

昔、関数あるかチェックしてるコード書いてる人に聞いてみた時は、たしか「将来的に同じ名前の関数が出てきても大丈夫なようにかいたほうがいいらしい」、のような感じのことを行ってた記憶がありますが、同じ名前の関数で同じ引数をとり、同じ返り値を返す仕様な確率なんてほぼありえないと思います。

長くなりましたが、関数がなければ定義するというチェックが必要な場合はありますか?

コードはたしかこういう感じのものです。

php

1if (!function_exists('debug_print')) { 2 function debug_print(){ 3 // 4 } 5}

javascript

1if(typeof window["debug_print"] !== "function"){ 2 debug_print = function(){ 3 // 4 } 5}
attakei, matobaa👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

javascriptでは動作環境によって用意されているオブジェクトが異なるため利用することがあります。
たとえばブラウザによってはconsoleがありませんでした。
なので、consoleが無い環境でもエラーが発生しないように定義することがあります。

似たような例でまだサポートしていないオブジェクトの拡張を早く取り入れたい場合にも利用します。
たとえば以下はmap関数の説明ですが、互換性の欄にあるコードはif (!Array.prototype.map) {で元々実装されているmapを上書きしない様にしつつ、mapの機能を古いブラウザに付与できるようになっています。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/map

最近だとNode.js側でブラウザで動作するコードの実装時にwindowオブジェクトがなければダミーのオブジェクトを用意するといった事もあります。

phpだとマイナーチェンジした新しい関数で実装したいが旧バージョンでも動くように、新バージョンの関数の存在チェックをしてなければ同じように振る舞う実装を用意してあげるといった事はやったことがあります。

というように、環境変更に対応するために行う事はあると思います。
たまたま同名の目的の異なるメソッドが存在するかどうかをチェックする意味はないという点は同意ですね(その時点でスコープに問題がある可能性もありますし)

投稿2016/08/24 14:31

flied_onion

総合スコア2604

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

lazex

2016/08/25 13:28

mapなど一部の(主に新しい)環境にあるものを他の環境でも使えるようにするのはpolyfillですよね。 それは質問に書いたとおり必要だと思います。 そうではなく、現在実在するどの環境でも定義されてない関数名です。 質問にあげた `debug_print` というのもそのひとつです。
lazex

2016/08/25 13:32

他に回答もなく、flied_onionさんの回答に+が多いので、やはり polyfill などの特別な意味がある時しか必要なくて、初心者が意図を理解せずとりあえずグローバルの関数定義には必要だと思い込んで広まったものなのですかね。
guest

0

ベストアンサー

少し独断と偏見が混じっています。C/C++のincludeガードに近い物では無いかと思っています。

###PHPについて

PHPで外部ファイルの読み込みにはincluderequireinclude_oncerequire_onceの4つがありますが、前二つと後二つで大きな違いがあります。毎回読むのか、一回だけなのかです。PHPは、その昔は、テンプレートエンジンとして使うのが主流でしたので、ヘッダ部分を出すだけのファイルを分離して、そこだけincludeするなんてことがありました。ヘッダだと2回出すことはありませんが、より汎用的なのものであれば、一つのページに2回出したいときもあるでしょう。そういったときはinclude_oncerequire_onceは使用できないので、includerequireを使うという文化だったようです。

そこで昔の慣習を引きずってしまったのか、外部ファイルを読み込むのはinclude(またはrequire)という定説(?)がまかり通ってしまいました。こうなると問題になるのはその外部ファイルで関数を定義している場合です。PHPでは関数を二重定義しようとするとエラーになりますので、関数定義が含まれる外部ファイルをincludeするとエラーになってしまいます。では、include_onceを使えば良いだけだろう…という発想にはならず、どれだけincludeされても大丈夫なようにしよう!ということとなって、関数の存在チェックを入れるようになった…と推測されます。

モダンなPHPなら名前空間を使ってuseを使うのが主流になっていくような気がしますので、そのうち廃れると思います。

###JavaScriptについて

もともとECMAScript5以前のJavaScriptには外部ファイルを読み込むという仕様は言語自体にありませんでした。外部ファイルのライブラリを使う場合はHTMLの技術である<script>タグを使ってずらずらと並べるものでした。そこで問題になるのは、間違って同じライブラリのファイルを2回呼び出してしまった場合、あるいは、あるJavaScriptの中にライブラリがそのままくっつけてあって、実質同じライブラリを2回呼びしてしまった場合です。JavaScriptはPHPと違って関数定義が二重になっても上書きされるだけでエラーになりませんが、処理の無駄が発生してしまいますので、ページの表示が遅くなってしまいます。あとは同じ発想です。無駄を少なくするために、すでに定義済みなら、再度定義しないと言うことです。

ECMAScript2015からは外部ライブラリの呼び出しも言語仕様として加わってますし、それ以前にそういった仕組みが揃ってきていますので、こちらもそのうち廃れると思います。


以上ですが、上のようなことはC/C++のincludeガードでも見られることですので、外部ファイル呼び出しにおいて、多重に呼び出されるのが一般的な言語では当たり前なのかも知れません(C/C++の#includeも何度も呼び出しできちゃいます)。逆に、Rubyのrequireのように1回キリが一般的な言語では見られらないと思います。実際、Rubyでそのようなコードを見たことは私はありません。

投稿2016/08/25 14:25

raccy

総合スコア21735

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

lazex

2016/08/26 13:45

自分で書くときは 関数定義などは once 付き、テンプレートや処理の部分は once なし、としていたのでinclude だけでも OK なようにという考えは思いつきませんでした。 歴史的なものがあるのですね。 「Ruby でみたことがない」ですが、そういえば私も見たことがない気がします。(Ruby はほとんど見ることがないのであてにならないですけど。。。) 納得できました。ありがとうございます!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問