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

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

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

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

Q&A

10回答

8493閲覧

JavaScriptの綺麗なコードの書き方

the

総合スコア112

JavaScript

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

0グッド

14クリップ

投稿2015/06/22 02:41

JavaScriptの綺麗なコードの書き方がわかりません。
例えば下記のような関数があった場合、皆さんならどのようにまとめますか?

lang

1 function userLogin() { 2 FB.api('/me', function(response) { 3 document.getElementById('name').value = response.name; 4 document.getElementById('email').value = response.email; 5 function calculateAge(birthday) { 6 var birth = birthday.split('/'); / 7 var _birth = parseInt("" + birth[2] + birth[0] + birth[1]); 8 var today = new Date(); 9 var _today = parseInt("" + today.getFullYear() + affixZero(today.getMonth() + 1) + affixZero(today.getDate())); 10 return parseInt((_today - _birth) / 10000); 11 } 12 13 function affixZero(int) { 14 if (int < 10) int = "0" + int; 15 return "" + int; 16 } 17 var birthday = calculateAge(response.birthday); 18 document.getElementById('age').value = birthday; 19 20 if(response.gender === "male") { 21 document.getElementsByName("gender")["1"].checked = true; 22 }else if(response.gender === "female") { 23 document.getElementsByName("gender")["2"].checked = true; 24 } 25 document.getElementById('address').value = response.location.name; 26 27 for(ed in response.education) { 28 var school = response.education[ed].school; 29 var schoolName = school.name; 30 } 31 32 document.getElementById('education').value = schoolName; 33 }); 34 } 35

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

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

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

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

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

guest

回答10

0

動作が質問文のコードと同じかは検証していませんが、
書き方の方針として、次のように ほとんどのロジックは関数に抜きだし、
関数からの返り値を DOM に設定するようにしてみては如何でしょう。

lang

1function userLogin() { 2 FB.api('/me', function(response) { 3 4 function calculateAge(birthday) { 5 var birth = birthday.split('/'); 6 var _birth = parseInt('' + birth[2] + birth[0] + birth[1]); 7 var today = new Date; 8 var _today = parseInt('' + today.getFullYear() + affixZero(today.getMonth() + 1) + affixZero(today.getDate())); 9 return parseInt((_today - _birth) / 10000); 10 }; 11 12 function affixZero(int_v) { 13 var prefix = ''; 14 if (0 < int_v && int_v < 10) { 15 prefix = '0' 16 } 17 return prefix + int_v; 18 }; 19 20 function getSchoolName(education) { 21 var schoolName = ''; 22 for (var ed in response.education) { 23 schoolName = ed.school; 24 } 25 return schoolName; 26 }; 27 28 function getCBIndex(gender) { 29 var idx = ''; 30 if (gender === "male") { 31 idx = "1" 32 } else if (gender === "female") { 33 idx = "2" 34 } 35 return idx; 36 } 37 38 document.getElementById('name').value = response.name; 39 document.getElementById('email').value = response.email; 40 document.getElementById('age').value = calculateAge(response.birthday); 41 document.getElementById('education').value = getSchoolName(response.education); 42 var cb_index = getCBIndex(response.gender); 43 if (cb_index !== '') { 44 document.getElementsByName('gender')[cb_index].checked = true; 45 } 46 }); 47};

投稿2015/06/22 12:07

katoy

総合スコア22324

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

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

0

lang

1function calculateAge(birthday) { 2 var birth = birthday.split('/'); 3 var _birth = parseInt("" + birth[2] + birth[0] + birth[1]); 4 var today = new Date(); 5 var _today = parseInt("" + today.getFullYear() + affixZero(today.getMonth() + 1) + affixZero(today.getDate())); 6 return parseInt((_today - _birth) / 10000); 7}

ここはかなりひどいです…。普通に算術演算すればいいだけです。数値を無理やり文字列として結合して"0"を足したりそれを数値化したりを繰り返すのは計算コストが非常に高いですし、人間が見ても分かりにくいでしょう。以下でこれで十分です。

あと、parseIntは文字列を数値に変換するための関数ですので、「parseInt((_today - _birth) / 10000);」のような使い方をすると、数値を文字列に変換し、文字列を数値に変換という、不要な型変換が2度発生します。数値の端数の切捨てならMath.floor()を使いましょう。

lang

1function calculateAge(birthday) { 2 var birth = birthday.split('/'); 3 var _birth = birth[2] * 10000 + birth[0] * 100 + birth[1]; 4 5 var today = new Date(); 6 var _today = today.getFullYear() * 10000 + (today.getMonth() + 1) * 100 + today.getDate(); 7 return Math.floor(_today - _birth) / 10000); 8}

次にこの部分。

lang

1if (response.gender === "male") { 2 document.getElementsByName("gender")["1"].checked = true; 3} else if (response.gender === "female") { 4 document.getElementsByName("gender")["2"].checked = true; 5}

「maleでもfemaleでもない場合にはチェックしない」という意図なのか(そんなことある?)
「maleならば、1にチェック、そうでなければ2にチェック」でいいのかどちらでしょうか?
後者なら、私ならこう書いてしまいます。

lang

1document.getElementsByName("gender")[(response.gender == "female") + 1].checked = true;

これだと「(response.gender == "female") + 1」の部分が嫌だと言う人がいるかもしれません。そういう人はそこの部分を書き換えていいと思います。しかし以下の部分は共通しており「…」の部分のみ異なるのです。重複した記述を避けるのがきれいなプログラミングの基本です。無駄な繰り返しは行わないようにしましょう。

document.getElementsByName("gender")[…].checked = true;

投稿2015/06/22 12:36

miu_ras

総合スコア902

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

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

sounisi5011

2015/06/22 14:17

> (response.gender == "female") + 1 実際に動かしてみたのですが、これでは"female"ならば2、そうでなければ1、となってしまいます。 var response = {gender: ''}; response.gender = 'male'; console.log((response.gender == "female") + 1); // 1 response.gender = 'female'; console.log((response.gender == "female") + 1); // 2 response.gender = 'unknown'; console.log((response.gender == "female") + 1); // 1 > 「maleならば、1にチェック、そうでなければ2にチェック」 とはならないものと考えます。
miu_ras

2015/06/22 14:54

あぁ…そうですね…。私の表現が間違っていました。すみません。 意味合いとしてはmaleとfemaleしか存在しないことを前提として書いています。なので、「femaleではない」==「maleである」という意味だと理解してください。 もしも男でも女でもない状態を許すのであれば「other」などという選択肢があるべきだと思います。しかしそうではないし、そういうシステムは普通はないよねということで、コードが間違っている可能性の方が高いと思って、「maleとfemaleしか存在しない」ことを前提としています。
guest

0

綺麗なコードにしたいだって?そりゃ、CoffeeScriptにするのに限るよ。プログラマーの友達は紅茶よりも珈琲だしね。ついでに、日付計算は面倒だからMoment.jsに任せることにするよ。Dateは使いにくいからね。

CoffeeScript

1moment = require 'moment' 2 3class Me 4 constructor: ({@name, @email, birthday, @gender, location, education}) -> 5 @birthday = moment birthday, 'MM/DD/YYYY' 6 @age = moment().startOf('day').diff @birthday, 'years' 7 @address = location.name 8 @schoolList = education.map (ed) -> ed.school.name 9 10userLogin = -> 11 FB.api '/me', (response) -> 12 me = new Me response 13 document.getElementById('name').value = me.name 14 document.getElementById('email').value = me.email 15 document.getElementById('age').value = me.age 16 [['mail', '1'], ['femail', '2']] 17 .filter (pair) -> me.gender == paire[0] 18 .forEach (pair) -> 19 document.getElementsByName('gender')[pair[1]].checked = true 20 document.getElementById('address').value = me.address 21 if me.schoolList > 0 22 document.getElementById('education').value = me.schoolList[0]

え、課題は「JavaScriptの綺麗なコードの書き方」だからCoffeeScriptを使うのはレギュレーション違反だって?仕方が無いなー。JavaScriptでも書いてみたよー。

JavaScript

1'use strict'; 2import moment from 'moment'; 3 4class Me { 5 constructor(response) { 6 this.name = response.name; 7 this.email = response.email; 8 this.birthday = moment(response.birthday, 'MM/DD/YYYY'); 9 this.age = moment().startOf('day').diff(this.birthday, 'years'); 10 this.gender = response.gender; 11 this.address = response.location.name; 12 this.schoolList = response.education.map((ed) => ed.school.name); 13 } 14} 15 16function userLogin() { 17 FB.api('/me', (response) => { 18 let me = new Me(response); 19 document.getElementById('name').value = me.name; 20 document.getElementById('email').value = me.email; 21 document.getElementById('age').value = me.age; 22 [['mail', '1'], ['femail', '2']] 23 .filter((pair) => me.gender == paire[0]) 24 .forEach((pair) => { 25 document.getElementsByName('gender')[pair[1]].checked = true; 26 }); 27 document.getElementById('address').value = me.address; 28 if (me.schoolList.length > 0) { 29 document.getElementById('education').value = me.schoolList[0]; 30 } 31 }); 32}

なに動かないって?ECMAScript 6に対応していない古いブラウザは知らないよ!

ついカッとなってやった、今は反省している。

投稿2015/10/17 08:03

raccy

総合スコア21735

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

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

0

lang

1function userLogin() { 2 FB.api('/me', function(response) { 3 if(!response){ 4 return; 5 } 6 7 document.getElementById('name').value = response.name; 8 document.getElementById('email').value = response.email; 9 document.getElementById('address').value = response.location.name; 10 if(response.gender==="female" || response.gender==="male"){ 11 document.getElementsByName("gender")[1+(response.gender==="female")].checked = true; 12 } 13 14 //calculateAge 15 document.getElementById('age').value = (function (birthday) { 16 function affixZero(n) { 17 if (n < 10){ 18 return "0" + n; 19 } 20 return "" + n; 21 } 22 23 var DATE_OBJ = new Date(); 24 var _Date=affixZero(DATE_OBJ.getDate(),_Month=affixZero(DATE_OBJ.getMonth() + 1; 25 26 var birth = birthday.split('/'); 27 birth = birth[2]+birth[0]+birth[1]; 28 29 var today = (DATE_OBJ.getFullYear() + _Month) + _date)-0); 30 31 return ((today - birth) / 10000) | 0; 32 33 }(response.birthday)); 34 35 //getSchoolName 36 document.getElementById('education').value = (function (_schoolObject){ 37 var _schoolName=""; 38 for(_key in _schoolObject) { 39 _schoolName = (_schoolObject[_key].school).name; 40 } 41 return _schoolName; 42 }(response.education)); 43 }); 44}

綺麗かと言われると微妙かもしれませんが元のコードを意識しつつ、改変してみました。
コードの整理と変数の削減(説明用の変数という観点から見るとこれはしない方が良いようにも思えますが……)を意識して記述しました。

投稿2015/06/22 14:01

Cf_cwd

総合スコア730

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

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

0

私なら以下のようにします。
本当なら、userLogin関数内に別の関数を定義するコードそのものを控えたいのですが…

lang

1function userLogin() { 2 /** 3 * "/me"用の処理 4 * @param {Object} res レスポンスオブジェクト…? 5 */ 6 function meCallback(res) { 7 var nameE = getId('name'); 8 nameE.value = res.name; 9 10 var emailE = getId('email'); 11 emailE.value = res.email; 12 13 var birthday = calculateAge(res.birthday); 14 var ageE = getId('age'); 15 ageE.value = birthday; 16 17 var gender = res.gender; 18 var genderEs = getNames('gender'); 19 var genderE = 20 (gender === 'male') ? genderEs[1] 21 : (gender === 'female') ? genderEs[2] 22 : null; 23 if (genderE) { 24 genderE.checked = true; 25 } 26 27 var addressE = getId('address'); 28 addressE.value = res.location.name; 29 30 var education = res.education; 31 var schoolName = ''; 32 forObject(education, function (_, education) { 33 schoolName = education.school.name; 34 }); 35 var educationE = getId('education'); 36 educationE.value = schoolName; 37 } 38 39 /** 40 * 現在の日付と誕生日との差を取得する 41 * @param {string} birthday 誕生日を示す日付文字列。形式は"mm/dd/yyyy" 42 * @return {number} 43 */ 44 function calculateAge(birthday) { 45 var birth = dateToInt(birthday); 46 var today = getTodayInt(); 47 48 return parseInt((today - birth) / 10000, 10); 49 } 50 51 /** 52 * 現在の日付の数値を取得する 53 * @return {number} 日付を数値に変換した値。"yyyymmdd"形式の文字列を数値に変換したもの。 54 */ 55 function getTodayInt() { 56 var today = new Date(); 57 58 var year = today.getFullYear() + ''; 59 var month = affixZero(today.getMonth() + 1); 60 var date = affixZero(today.getDate()); 61 62 return parseInt(year + month + date, 10); 63 } 64 65 /** 66 * 日付文字列を数値に変換する 67 * @param {string} date_str 日付文字列。形式はmm/dd/yyyy 68 * @return {number} 日付を数値に変換した値。"yyyymmdd"形式の文字列を数値に変換したもの。 69 */ 70 function dateToInt(date_str) { 71 var date_params = date_str.split('/'); 72 return parseInt(date_params[2] + date_params[0] + date_params[1], 10); 73 } 74 75 /** 76 * オブジェクトに対するfor 77 * @param {Object} obj 対象のオブジェクト 78 * @param {function(string=, ?*=)} callback コールバック関数。第一引数にプロパティ、第二引数に値が代入される。 79 */ 80 function forObject(obj, callback) { 81 var supportHasOwnProperty = obj.hasOwnProperty !== void 0; 82 var p; 83 for (p in obj) { 84 if (!supportHasOwnProperty || obj.hasOwnProperty(p)) { 85 callback(p, obj[p]); 86 } 87 } 88 } 89 90 /** 91 * 数値のゼロ埋めを行う 92 * @param {number} num ゼロ埋めを行う数値。1桁の数値の先頭に0を追加し2桁にする。 93 * @return {string} ゼロ埋めを行った数値文字列 94 */ 95 function affixZero(num) { 96 return ('0' + num).slice(-2); 97 } 98 99 /** 100 * `document.getElementById`のエイリアス 101 * @param {string} id 取得する要素のid属性値 102 * @return {?HTMLElement} 取得した要素 103 */ 104 function getId(id) { 105 return document.getElementById(id); 106 } 107 108 /** 109 * `document.getElementsByName`のエイリアス 110 * @param {string} name 取得する要素のname属性値 111 * @return {NodeList} 取得した要素を含むNodeList 112 */ 113 function getNames(name) { 114 return document.getElementsByName(name); 115 } 116 117 FB.api('/me', meCallback); 118}

元のコードにあった、以下の問題を修正しています。

  • parseInt関数の第二引数を指定し、過去のブラウザにおいて問題が発生する可能性を回避しました。
  • affixZero関数の引数として定義された変数intは、"int"が予約語のため利用できません。このため、変数名をnumに書き換えました。
  • for inは、オブジェクトのループとしてそのまま利用するには不適切です。このため、for inの処理をforObject関数にまとめ、書き換えました。

parseInt関数について:

"int"が予約語であるという出典:

for inについて:

投稿2015/06/22 11:23

編集2015/06/22 11:30
sounisi5011

総合スコア697

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

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

nanndemoiikara

2015/06/22 12:01

粒度が細かい 宣言位置が多い なぜ三項演算子にしたのかわからない > var genderE = > (gender === 'male') ? genderEs[1] > : (gender === 'female') ? genderEs[2] > : null; 上記の理由から可読性が悪く感じます。 getId、getNamesメソッド等はこの規模の物に必要ないかと思うのですが いかがでしょうか?
sounisi5011

2015/06/22 14:05

> 粒度が細かい 単機能に分割した方が、個別の処理を把握しやすくなるものと考えました。 meCallback関数の中身もより細かい部分に分割したほうが良かったかもしれません。 > 宣言位置が多い 宣言を多くしているのは、可読性を向上させるためです。 各変数に代入する式を簡潔にすれば、それだけ理解しやすくなります。 また値を変更する度に別の変数名を付けたほうが、その値が何を担っているのか読み解きやすくなります。 > なぜ三項演算子にしたのかわからない 条件文を1つの変数に集約させることで、より分かりやすく、尚且つバグを減らせるものと考えます。 > 上記の理由から可読性が悪く感じます。 私にはこちらの方が読みやすいです。 > getId、getNamesメソッド等はこの規模の物に必要ないかと思うのですが getNames関数に関しては、完全に助長で余分ではあります。 が、getIdに関しては、長い`document.getElementById`という文字列を見なくて済む分、入力の手間も減り、可読性も向上するものと考えます。 最も、関数名に関してはもうすこしどうにかしたほうが良いかもしれません。
Cf_cwd

2015/06/22 14:16

三項演算子について意図(分岐用のコードをまとめることを目的にしていること)は分かりますが私も少し読みにくい印象を受けました。 説明用変数の考え方には同意します。 ですがElementを省略するならElmのような省略の方がわかりやすいかもしれませんね。 emailEやaddressEといった名前が合わせて使用されていると「Eメールアドレス」との誤読を招く、もしくは認識の妨げになるかもしれません。
sounisi5011

2015/06/22 14:26

> ですがElementを省略するならElmのような省略の方がわかりやすいかもしれませんね。 > emailEやaddressEといった名前が合わせて使用されていると「Eメールアドレス」との誤読を招く、もしくは認識の妨げになるかもしれません。 var nameElem = getId('name'); var emailElem = getId('email'); var ageElem = getId('age'); var genderElems = getNames('gender'); var genderElem = (gender === 'male') ? genderElems[1] : (gender === 'female') ? genderElems[2] : null; var addressElem = getId('address'); var educationElem = getId('education'); 一理あると考えます。 こうして書いてみると、こちらの方が若干分かりやすいように思います。
nanndemoiikara

2015/06/22 15:16

回答頂きありがとうございます。 > また値を変更する度に別の変数名を付けたほうが、その値が何を担っているのか読み解きやすくなります。 その値が何を担っているかという部分に関してはsounisi5011様のコードは EやEs等でElementなのかNodeListなのか命名規則が明確で非常にわかりやすく思います。 ただ2度も使用しないgetId('address')等を変数に入れる必要はございますでしょうか? > 条件文を1つの変数に集約させることで、より分かりやすく、尚且つバグを減らせるものと考えます。 説明不足で申し訳ございません。 sounisi5011様の主張で同意して頂いている http://qiita.com/tanakahisateru/items/4fd82e03c90dc130329a のURLから抜粋させて頂きます。 > あらゆる変数から、一時的にですら null である可能性を完全に排除できた。何より素晴らしいのは、変数の値を決定するのが「1行」になったことだ。変数の妥当化までに他の手続きが介入する余地がいっさいない。 ここで「」で表示強調されているように1行になる事が重要なのではないかなと思います。 その為、改行して三項演算子を使うぐらいならifで分けたほうが良いのでは?と思った為です。 そちらについては参考で記述頂いております下記URLコメント欄でも記述されております。 http://qiita.com/chokuryu@github/items/8b9cff5ea92be245b026 > getIdに関しては、長い`document.getElementById`という文字列を見なくて済む分、入力の手間も減り、可読性も向上するものと考えます。 このコードを読む人はJSが少なくとも読める人かと思います。 その為、getIdと書かれるよりはdocument.getElementByIdと書いて頂いた方がわかりやすいのではないかなと思うのですが いかがでしょうか?
sounisi5011

2015/06/22 15:50

> ただ2度も使用しないgetId('address')等を変数に入れる必要はございますでしょうか? この点については私自身助長であると理解しています。 が、このような最適化を行い続ければ読みにくくなります。 例えば、この関数が多数の引数を持つ関数の場合、または関数/メソッド名が長い場合に可読性が大幅に減少します。 このコードでは`document.getElementById`を`getId`に短縮しているためその利点を把握しにくいですが、これを元に戻すとその読みにくさが顕在化します。 document.getElementById('address').value = response.location.name; 以上のコードは、要素の取得を意味する`document.getElementById`メソッドに34文字も使用しています。また代入する値も、22文字と長いものです。 これを読むためには、視界を左から右へと、長いこのコードを読み解くために移動させる必要があります。それは可読性の減少に繋がります。 続いて、変数に代入した以下はどうでしょうか。 > var addressE = document.getElementById('address'); > addressE.value = res.location.name; 変数名の長さにも影響されますが、この方が可読性は向上します。 何故なら、要素を意味する`document.getElementById('address')`が34文字なのに対し、変数`addressE`は僅か8文字です。 全体的な文字数は増えるものの、「addressE = document.getElementById('address')」という事を意識してしまえば、肝心の処理部分は「addressE.value = res.location.name」だけとなります。 圧倒的に読みやすいです。少なくとも私はそのように感じています。 --- > ここで「」で表示強調されているように1行になる事が重要なのではないかなと思います。 行数は問題ではありません。 三項演算子が式であるため、結果がどうであれ確実に変数genderEが定義され、以降の処理ではこの変数に対する簡潔な処理のみを記述すれば良い点が大きな利点です。 > こう書くことで、$data は条件付きで変化する状態変数ではなく、必ず再定義されるイミュータブルな変数(とみなして扱うことが可能)であることが、機械的に保証できた。このほうが関数型言語でいう変数(Scalaのval)に近い。 私は、上記の「必ず再定義されるイミュータブルな変数」に条件を集約してしまうこの記述が最も適切であると考えています。 --- > このコードを読む人はJSが少なくとも読める人かと思います。 > その為、getIdと書かれるよりはdocument.getElementByIdと書いて頂いた方がわかりやすいのではないかなと思うのですが 成程…それは確かに一理あるかもしれません。 prototypeの宣言に関する質問でも、そのような観点からわざわざ変数に格納しない、等の意見が見られました。 https://teratail.com/questions/4919
Cf_cwd

2015/06/23 17:11

本題とは異なりますがdocument.getElementByIdを変数に代入することによりキャッシュが効いてほんの少し処理が早くなりそうな気がしないでもないですね。どうなんでしょう…… 三項演算子を使用することのメリットであると私が考えるのは代入の式を何度も書く必要がない点でしょうか。if-elseですと代入式を何度か書く必要がありますからね。 ですから複雑でないのであれば三項演算子を使う意味やある種のメリットがあると考えます。 (DRYの原則的にもその方がいいのですかね?) ですが今回の例ですと三項演算子がネストされていて、一瞬ですが関係を把握するのに思考を必要とするように思います。この点に関してどう解釈すべきかが問題ではないでしょうか。 今回の例を読みやすさという点から見るとif-elseの方が私は読みやすいと思います。 これは処理の流れをそのまま文に起こしているからですね。 ですがある特定の処理とその記述といった点から見ると三項演算子を使い、重複部分を省いたコードは綺麗だと思いました。 つまり処理を追って読む場合はif-elseの方が読みやすく、それらを関数かなにかのように一連の塊としてみる際には三項演算子の方がまとまっている印象を受けました。
sounisi5011

2015/06/24 03:07

> 本題とは異なりますがdocument.getElementByIdを変数に代入することによりキャッシュが効いてほんの少し処理が早くなりそうな気がしないでもないですね。 肝心のdocument.getElementByIdに関しては、関数内でこの記述をそのまま呼んでいるので、むしろ関数処理のオーバーヘッドがかかって遅くなるかもしれません。(未検証) ただ、別の箇所にて、この考え方は考慮していました。 今までのコメントで触れてはいなかったのですが、一々変数に代入しているのは、これを意識している事も理由として挙げられます。 var education = res.education; 例えば上記の文ですが、こうすればres.educationが変数educationにキャッシュされるので、この後のfor文で一々resオブジェクトを読みにいく必要が無くなります。 確かwindowオブジェクトも、ローカルの変数にキャッシュすればパフォーマンスが向上するらしいです。 > ですが今回の例ですと三項演算子がネストされていて、一瞬ですが関係を把握するのに思考を必要とするように思います。この点に関してどう解釈すべきかが問題ではないでしょうか。 その点について考慮が足りませんでした。 私は三項演算子のこの記述法に慣れきっていて、switch文と同じような感覚で利用しています。 なので、極めて簡潔で見やすいです。 しかしこの記述を知らない人間にとって、三項演算子を解釈する必要性があり、それにより読みにくくなっている点があります。 それについて考慮できていませんでした。
Cf_cwd

2015/06/24 07:41

私も自作のライブラリ内で三項演算子を用いて似たような記述をしているのですが周囲の友人からの反応は微妙ですね。 代入のための文を一つにまとめることができて場合によってはスマートだと思うんですがね…… 以前可読性を保つために三項演算子の乱用は避けた方が良いという記述をいくつかのサイトで見かけた記憶があります。(情報元を提示できなくて申し訳ないです) 書いた本人は対応関係をあっさり追えますがそのコードを初めて読む人はその対応を一度整理する必要があり、若干ながら読む際のコストを必要とするからでしょうか
guest

0

私も綺麗な書き方が気になります。
私の場合この様に記述します。

lang

1var doc = document; 2 function userLogin(res) { 3 4 this.gender = res.gender || 'male'; 5 this.birth_day = res.birthday || '01/01/1970'; 6 this.name = res.name || 'sample'; 7 this.email = res.email || 'test@test.com'; 8 this.location_name = res.location.name || 'test'; 9 this.education = res.education || {}; 10 11 this.Controller(); 12} 13 14userLogin.prototype = { 15 Controller : function() 16 { 17 doc.getElementById('name').value = this.name; 18 doc.getElementById('email').value = this.email; 19 doc.getElementById('age').value = this.calculateAge(); 20 var genderDOMs = doc.getElementsByName('gender'); 21 22 switch ( this.gender ) 23 { 24 case 'male': 25 genderDOMs[1].checked = true; 26 break; 27 case 'female': 28 genderDOMs[2].checked = true; 29 break; 30 } 31 doc.getElementById('address').value = this.location_name; 32 //TODO:ちょっとここ元のソースが何がしたいのかわんかんない 33 var schoolName = ''; 34 for ( var ed in this.education ) schoolName = this.education[ed].school.name; 35 36 doc.getElementById('education').value = schoolName; 37 }, 38 calculateAge : function() 39 { 40 var ymd = parseInt(this.birth_day.replace(/(\d{2}).(\d{2}).(\d{4})/, '$3$1$2'), 10), 41 date = new Date(), 42 today = [date.getFullYear(), 43 ('0' + (date.getMonth() + 1) ).slice(-2), 44 ('0' + date.getDate()).slice(-2)].join(''); 45 46 if ( isNaN(ymd) ) throw Error('Exception birthDay Format mm/dd/yyyy'); 47 48 return parseInt( ( (today - ymd) / 10000), 10); 49 } 50}; 51FB.api('/me', function(res){new userLogin(res);});

投稿2015/06/22 04:50

編集2015/06/22 14:55
nanndemoiikara

総合スコア775

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

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

sounisi5011

2015/06/22 09:29

元のコードを良く読むと、`response.birthday`の形式は「月/日/年」のようです。 > var _birth = parseInt("" + birth[2] + birth[0] + birth[1]); このため、`response.birthday`に対する処理は"/"を削除するだけでは駄目です。 > var ymd = parseInt(this.birth_day.replace(/\u002f/g, '')), また`gender`に関する処理も、"male"でも"female"でもない場合はそもそもチェックボックスをチェックしないような処理ではないかと思われます。 > if(response.gender === "male") { > document.getElementsByName("gender")["1"].checked = true; > }else if(response.gender === "female") { > document.getElementsByName("gender")["2"].checked = true; > } "male"以外の場合に`document.getElementsByName('gender')[2]`をチェックしてしまうような処理は誤りではないでしょうか。 >this.gender = ( gender === 'male' )? genderDOM[1] : genderDOM[2] ;
sounisi5011

2015/06/22 09:34

さらに、parseInt関数は第二引数を指定しないと、問題を起こす可能性があります。 これについては、質問のコードでも同じですが… http://liginc.co.jp/web/js/31018
nanndemoiikara

2015/06/22 11:44

コードレビュー頂きありがとうございます。 > 元のコードを良く読むと、`response.birthday`の形式は「月/日/年」のようです。 > このため、`response.birthday`に対する処理は"/"を削除するだけでは駄目です。 > また`gender`に関する処理も、"male"でも"female"でもない場合はそもそもチェックボックスをチェックしないような処理ではないかと思われます。 > さらに、parseInt関数は第二引数を指定しないと、問題を起こす可能性があります。 ご指摘頂きありがとうございます。勉強になります。 また、DOMの取得部分もバラバラでしたので可読性が悪いコードでしたので修正いたしました。 問題がございましたらご指摘頂けますと幸いでございます。
sounisi5011

2015/06/22 11:55

`response.birthday`の形式について、 初期値と例外のメッセージの形式が「年/月/日」のままです。 > this.birth_day = res.birthday || '1970/01/01'; > if ( isNaN(ymd) ) throw Error('Exception birthDay Format yyyy/mm/dd');
nanndemoiikara

2015/06/22 11:56

ご指摘頂きありがとうございます。 修正致しました。
sounisi5011

2015/06/22 14:31

例外のメッセージの形式が"yyyy/mm/dd"のまま直っていません。 > throw Error('Exception birthDay Format yyyy/mm/dd'); また、calculateAge関数内の宣言部にて、変数ymdの代入文の後ろが`;`で終わっています。 このため、変数date, todayがvar文に含まれておらず、グローバル変数として宣言されてしまいます。 > var ymd = parseInt(this.birth_day.replace(/(\d{2}).(\d{2}).(\d{4})/, '$3$1$2'), 10); > date = new Date(), > today = [date.getFullYear(), > ('0' + (date.getMonth() + 1) ).slice(-2), > ('0' + date.getDate()).slice(-2)].join('');
nanndemoiikara

2015/06/22 14:55

ご指摘頂きありがとうございます。 修正いたしました。
guest

0

さくっとこんな具合ですかね。

javascript

1var userLogin = function (){ 2 function userLogin(response){ 3 var option = $.extentd(response); // jQuery 使っていれば… 4 5 var userLogin = {}; 6 userLogin.name = option.name || ''; 7 userLogin.email = option.email || ''; 8 userLogin.address = option.location.name || ''; 9 userLogin.age = this.calculationAge(option.barthday || '' ); 10 userLogin.gender = this.distinctionGender(option.gender || ''); 11 userLogin.education = this.odistinctionSchool(option.education || []); 12 13 } 14}; 15userLogin.prototype = { 16 GENDER_MALE:'male', 17 GENDER_FEMALE'female'. 18 19 getName:function(){return this.name;}, 20 getEmail:function(){return this.email;}, 21 getAddress:function(){return this.address;}, 22 getAge:function(){return this.age;}, 23 getGender:function(){return this.gender;}, 24 getEducation : function(){return this.education;}, 25 26 calculationAge:function(barthday){ 27 var barth = new Date(barthDay); 28 29 // 日付に変換できなければ 空文字列 30 if (isNan(barth)) return ''; 31 32 var toDay = new Date().toISOString().substring(0,10).replace(/-/g,''); 33 barth = barth.toISOString().substring(0,10).replace(/-/g,''); 34 35 return Math.floor((toDay -barth) / 10000); 36 }, 37 distinctionGender:function(gender){ 38 39 switch(gender){ 40 case GENDER_MALE: 41 case GENDER_FEMALE: 42 // 正しい値だったからそのまま 43 break; 44 default: 45 // 初期値は男 46 gender = GENDER_MALE; 47 } 48 return gender; 49 }, 50 distinctionSchool:function(education){ 51 var schoolName = []; 52 for (var i = 0,len = education.length ; i < len; len++){ 53 schoolName.push(education[i].school.name || ''); 54 } 55 return schoolName; 56 } 57 58};

javascript

1 2 3FB.api('/me', function(response) { 4 5 var formItem = new userLogin(response); 6 var elements = form.elements; 7 8 elements['name'].value = formItem.getName(); 9 elements['email'].value = formItem.getEmail(); 10 elements['address'].value = formItem.getAddress(); 11 elements['age'].value = formItem.getAge(); 12 getElementByValue(elements['gender'],formItem.getGender()).checked = true; 13 elements['#education'].value = formItem.getName().join(); // 引数なしjoinはカンマ区切り 14}

javascript

1// レスポンス サンプル 2var response = { 3 "id": "123456789012345", 4 "name": "Tarou Tanaka", 5 "education": [ 6 { 7 "school": { 8 "id": "000000000000001", 9 "name": "○○高等学校" 10 }, 11 "type": "High School", 12 "year": { 13 "id": "000000000000002", 14 "name": "2012" 15 } 16 }, 17 { 18 "school": { 19 "id": "000000000000003", 20 "name": "○○専門学校" 21 }, 22 "type": "College", 23 "year": { 24 "id": "000000000000004", 25 "name": "2014" 26 } 27 } 28 ], 29 "birthday": "01/21/1987", 30 "email":'samplemail@samplemail.co.jp', 31 "address":"Japan Tokyo", 32 "barthday":"1987/1/1", 33 "gender":"Okama", // 男女以外の想定 実際は、空なのか、keyが無いのかは未検証 34};

投稿2015/10/17 05:55

編集2015/10/18 19:44
Ryo

総合スコア507

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

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

think49

2015/10/18 02:17

どうやら私のコードがベースになっているようですが、いくつか ReferenceError が発生します。 ReferenceError: form is not defined ReferenceError: getElementByValue is not defined > if (isNan(barth)) return ''; 何もしなければ(この一文を削除すれば)、input要素に反映された時点で age === "NaN" になり、エラーである事が明確になります。 age === '' にすると処理が出来なかったのか、処理して空文字なのかが分からなりませので、NaN を返すのがベストだと個人的には思います。 > case this.GENDER_MALE: > case this.GENDER_FEMALE: いずれもコンストラクタ内のローカル変数なので undefined になるのでは。 > elements['#education'].value = formItem.getName().join(); // 引数なしjoinはカンマ区切 好みの問題ですが、"SchoolA,SchoolB" はセパレータが分かりづらいと思ったので "SchoolA, SchoolB" にしてみました。 > distinctionSchool:function(education){ FB.api の callback 返り値オブジェクトの仕様が不明だったので質問者さんのコードが for-in であることから Object,keys を使いましたが、配列で良かったのでしょうか。 例えば、education = {a: {school: {name: 'schoolA'}}} である場合に期待通りに動作しません。
think49

2015/10/18 02:25

> var option = response.slice(); response は new Object だと思っていましたが、Array.prototype.slice を持っているのでしょうか。 このコードの意図が分かりません。
Ryo

2015/10/18 19:41 編集

> コンストラクタ内のローカル変数なので undefined …あ、prototypeへの引っ越しで漏れてますね。お恥ずかしい… 机上だけで、実行もしてなかったです。 > education = {a: {school: {name: 'schoolA'}}} 本物のレスポンス気になったんで後述のページでデータ見ると education:[{school:{id:"",name:"",},type:"",year:{id:"",name:""}}] になってたので、問題ないかと。 > var option = response.slice(); slice持って無いですね。期待したのはjQueryの $.extend() の動作です。 この時点で、参照を切っておきたいので素直にそっちを使えばよかったかなと 思っています。 投稿したあとFB.api()のリファレンス覗いたんですが、 response帰ってきた直後に if (response && !response.error) { /* 正常時の処理群 */ } で、「エラー出たらerrorって返すからよろしく」だそうで 実際のresponseは Facebook にログインした状態で https://developers.facebook.com/tools/explorer/ にアクセスし、「Get Token」で取得したい情報を選択 遷移したページ右下の「OK」で取れるみたいです。 レスポンスサンプル更新しときます。
Ryo

2015/10/18 19:45

think49 さんレビューありがとうございました。
guest

0

個人的には getElementById が並ぶコードは美しくないと思うので、HTML から作り直したくなります。
JavaScript コードの側面では下記変更を加えています。

  • form関連要素の参照に getElementById ではなく、HTMLFormElement#elements を使用するようにした
  • String.prototype.padLeft を使った(ES7 提案)
  • school.name が最後の名前だけ取得する仕様だった為、リストで取得するようにした
  • useLogin 内で関数定義しない為にクロージャに閉じ込めた
  • parseIntNumber に変更
  • 数値切捨てを parseInt から Math.floor に変更(parseInt は内部処理で String 型にキャストするので数値切り捨てに使うべきではありません)

HTML

1<form id="sample"> 2 <p><label>name <input type="text" name="name" value=""></label></p> 3 <p><label>email <input type="text" name="email" value=""></label></p> 4 <p>gender 5 <label><input type="radio" name="gender" value="male">male</label> 6 <label><input type="radio" name="gender" value="female">female</label> 7 </p> 8 <p><label>age <input type="text" name="age" value=""></label></p> 9 <p><label>address <input type="text" name="address" value=""></label></p> 10 <p><label>education <input type="text" name="education" value=""></label></p> 11</form> 12 13<script> 14'use strict'; 15var userLogin = (function (){ 16 function userLogin (form) { 17 FB.api('/me', function (response) { 18 var elements = form.elements; 19 20 elements['name'].value = response.name; 21 elements['email'].value = response.email; 22 getElementByValue(elements['gender'], response.gender).checked = true; 23 elements['age'].value = calculateAge(response.birthday); 24 elements['address'].value = response.location.name; 25 elements['education'].value = getSchoolNames(response.education).join(', '); 26 form = null; 27 }); 28 } 29 30 function calculateAge (birthday) { 31 var today = new Date; 32 33 today = Number([today.getFullYear(), padLeft(today.getMonth() + 1, 2, '0'), padLeft(today.getDate(), 2, '0')].join('')); 34 birthday = birthday.split('/'); 35 birthday = Number([birthday[2], padLeft(birthday[0], 2, '0'), padLeft(birthday[1], 2, '0')].join('')); 36 37 return Math.floor((today - birthday) / 10000); 38 } 39 40 function getSchoolNames (education) { 41 return Object.keys(education).map(function (key) { 42 return education[key].school.name; 43 }); 44 } 45 46 function getElementByValue (elements, value) { 47 var i = elements.length, 48 element; 49 50 while (i--) { 51 element = elements[i]; 52 53 if (element.value === value) { 54 return element; 55 } 56 } 57 58 return null; 59 } 60 61 /** 62 * ES7 String.prototype.padLeft 63 * @url https://github.com/ljharb/proposal-string-pad-left-right 64 **/ 65 var padLeft = (function (){ 66 if (typeof String.prototype.padLeft == 'function') { 67 return function padLeft (string, maxLength /*, fillString*/) { 68 return arguments.length > 2 ? String.prototype.padLeft.call(string, maxLength, arguments[2]) : String.prototype.padLeft.call(string, maxLength); 69 }; 70 } 71 72 return function padLeft (maxLength /*, fillString*/) { 73 var string = String(this), 74 fillString = arguments.length < 2 ? ' ' : String(arguments[1]), 75 stringLength = string.length, 76 diffLength = maxLength - stringLength; 77 78 return diffLength < 1 ? string : Array(ceil(diffLength / fillString.length) + 1).join(fillString).slice(0, diffLength) + string; 79 }; 80 }()); 81 82 return userLogin; 83}()); 84 85userLogin(document.getElementById('sample')); 86</script>

投稿2015/10/14 08:57

think49

総合スコア18162

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

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

0

時間が経っていますが、面白い課題だと思ったので回答します。

元のコードは、無駄に複雑なロジックが多いという印象です。そのため方針としては、特に構造化や複雑化はせず、読みやすくすっきりさせる方向で考えました。

  • 年齢計算: 愚直なロジックの方が、分かりやすく処理も早いと考えました
  • 性別処理: もともと .checked=false であったという前提を含んでいます
  • 学校名処理: 速度は落ちるかもしれませんが、「最終」学歴ということを見やすくしたつもりです

懸案事項として一点、、学校名処理に関して、そもそもJavaScriptのobjectにorderは存在しないので、前提のロジックを考え直した方が良いように思いました。

javascript

1function userLogin() { 2 FB.api('/me', function(response) { 3 document.getElementById('name').value = response.name; 4 document.getElementById('email').value = response.email; 5 document.getElementById('age').value = (function getAgeOf(birthday_string) { 6 var birthday = new Date(birthday_string); 7 var today = new Date(); 8 var differenceOfYear = today.getFullYear() - birthday.getFullYear(); 9 if (today.getMonth() <= birthday.getMonth() 10 && today.getDate() < birthday.getDate()) 11 return differenceOfYear - 1; 12 else 13 return differenceOfYear; 14 })(response.birthday); 15 document.getElementById('address').value = response.location.name; 16 document.getElementsByName("gender")[1].checked = (response.gender === "male"); 17 document.getElementsByName("gender")[2].checked = (response.gender === "female"); 18 document.getElementById('education').value = (function getSchoolNameOf(educations) { 19 var keys = Object.keys(educations); 20 return educations[keys.pop()].school.name; 21 })(response.education); 22 }); 23}

投稿2015/10/09 07:01

編集2015/10/13 12:30
.tkn

総合スコア10

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

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

0

初めて投稿させて頂きます。
私自身完全な独学のみなので、キレイなのかどうか判断しかねる部分がありますが
自分ならこう書くかなという感じで投稿させて頂きました。
私も本来であればuserLogin内に沢山巻数を置きたくはないのですが、かと言ってglobal直下にも置きたくはありませんので、他の部分がどうなっているか判断できませんことからuserLogin内に書く方法にしています。
折角なので指摘などして頂けますと幸いです。

lang

1function userLogin() { 2 // "01", "12" のような 2桁の文字列にして返す 3 var affixZero = function(n) { 4 return ('0' + n).slice(-2).toString(); 5 }; 6 7 var calculateAge = function(birthday) { 8 // birthday は 月/日/年 の文字列形式で渡ってくる 9 var birthDayArg = birthday.split('/'), 10 _birthDay = (birthDayArg[2] + affixZero(birthDayArg[0]) + birthDayArg[1]) - 0, 11 today = new Date(), 12 _today = ( today.getFullYear() + affixZero(today.getMonth() + 1) + affixZero(today.getDate()) ) - 0; 13 return _birthDay - _today; 14 }; 15 16 var checkGenderId = function(genderStr) { 17 var genderList = ['', 'male', 'female']; 18 return genderList.indexOf(genderStr); 19 // 別言語でも gender 判定をするなら素直にif文の方がメンテが楽かと思います。 20 /* 21 var gId = 0; 22 if(genderStr === 'male' || genderStr === 'maschio') { 23 gId = 1; 24 } else if(genderStr === 'female' || genderStr === 'femminile') { 25 gId = 2; 26 } 27 return gId; 28 */ 29 }; 30 31 var getSchoolName = function(educations) { 32 var schoolName = '', 33 keys = Object.keys(educations); 34 for( var i=0, l=keys.length; i<l; i+=1) { 35 var key = keys[i]; 36 if(educations[key].school && educations[key].school.name) { 37 schoolName = educations[key].school.name; 38 break; 39 } 40 } 41 return schoolName; 42 }; 43 44 FB.api('/me', function(response) { 45 var d = document, 46 name = response.name, 47 email = response.email, 48 age = calculateAge(response.birthday), 49 gender = checkGenderId(response.gender), 50 address = response.location && response.location.name || '', 51 schoolName = getSchoolName(response.education); 52 53 /* 54 他にセットする値の処理や整形をするのであればココに記述していく。 55 */ 56 57 // 値をセットしていく 58 d.getElementById('name').value = name; 59 d.getElementById('email').value = email; 60 d.getElementById('age').value = age; 61 if(gender) { 62 d.getElementsByName("gender")[gender].checked = true; 63 } 64 d.getElementById('address').value = address; 65 d.getElementById('education').value = schoolName; 66 }); 67}

投稿2015/06/30 07:38

KiKiKi_KiKi

総合スコア596

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.49%

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

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

質問する

関連した質問