Jqueryにおいて、Jqueryオフジェクトの要素の個数を取得するのにlengthを使いますが、なぜlength()でないのかを教えていただきたいです。調べてみると"メソッドではなくプロパティだから"とでてきましたが、納得がいきません。
javascript
1$('.hoge').length;
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

退会済みユーザー
2019/05/14 02:25

回答4件
0
最終的にプロパティだからで終わりますが、
JavaScriptのArray.lengthがちょっと珍しいgetter, setterのプロパティとして実装しているからです。
イメージとしてはこんな感じ
JavaScript
1var obj = { 2 get length () { return 10; } 3} 4console.log(obj.length); // 10 5obj.length = 100; 6console.log(obj.length); // 10 <- getterプロパティは代入しても変化しない
setterも定義すれば挙動を定義できます。
配列を司るArrayはlengthプロパティに無理やり数値をねじ込むことで配列の要素数を拡張したり、0を代入することで空配列に変更することが可能ですが、
数値以外を代入するとエラーになります。
JavaScript
1var arr = []; 2arr.length = "hoge"; 3// Uncaught RangeError: Invalid array length
このArrayがlengthプロパティを所持しているという仕様は、
「ふーん、配列はlengthをプロパティで作ったのね」だけでは収まらず、多くの場所で「前提」となっています。
配列は機能(メソッド・プロパティ)は、配列を便利に扱う為のものですから、
他のコレクションを扱うオブジェクト(クラスやインスタンス)を定義する時に足りないと感じる事もありますよね。
なのでビルトインのクラスとして配列に似た用途別のオブジェクトが多数存在します。
しかし、それらにはforEachやmapといった配列用のメソッドがなく、
「あ〜配列に変換したいな〜」というケースも多々あります。
その時に「配列っぽい」値を本物の配列と同じように扱う機能があります。
参考: 配列風オブジェクト
JavaScript
1// 配列に変換したい! 2var obj = {0: "taro", 1: "jiro", length: 2}; 3console.log(Array.prototype.slice.call(obj, 0)); 4// ["taro", "jiro"] 5 6// 配列っぽくforEachメソッドに放り込みたい 7Array.prototype.forEach.call(obj, function(it){ 8 console.log(it); 9}); 10// "taro" 11// "jiro"
こんな風にlengthを所持しているオブジェクトかつ、
数値の添字でアクセス可能なオブジェクトは、
Arrayの多くのメソッドに直接放り込んでメソッドを起動させることが出来ます。
Jqueryのlengthはなぜlength()ではないのか
jQueryは$(selector)
でjQueryオブジェクトを返すことで有名ですが、
こいつ、中に配列っぽいものを内包しているんですね。
例えばfindで子孫の要素を探しに行くときも配列の全てを探索しますし、
cssメソッドでcssプロパティを書き換える時も、掴んだ要素全てが対象です。
で、このjQueryも配列っぽいコレクションのオブジェクトを定義する時に、
「じゃあ、慣習に従って、jQueryオブジェクトにもlengthプロパティを所持させようか」
という決定がなされたはずです。
この慣習や、言語のルールに従うというのはとても重要な感覚の一つで、
私も仮にJavaScriptで何かしらのコレクションを作る事を決めてライブラリとして公開する際はlengthプロパティはつけて当然だなと考えています。
因みにjQueryを作ったジョン・レシグ氏はJavaScriptのウィザード的な存在として認知されており、JavaScript Ninjaの極意という有名な書籍を書いています。
投稿2019/05/14 03:58
総合スコア21397
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

退会済みユーザー
2019/05/14 07:53

退会済みユーザー
2019/05/14 21:41

0
getter/setter
オブジェクトのプロパティに設定されるgetter/setterは Object.getOwnPropertyDescriptor()
で確認することが出来ます。
JavaScript
1const arrayLike = {}; 2Object.defineProperty(arrayLike, 'length', { 3 enumerable: true, configurable: true, 4 get: function get () { 5 return Object.keys(this).length; 6 }, 7 set: function set (length) { 8 length = Math.abs(length) || 0; 9 length -= length % 1; 10 11 for (let i of Object.keys(this)) { 12 if ((+i).toString() === i && i >= length) { 13 delete this[i]; 14 } 15 } 16 17 return length; 18 } 19}); 20 21console.log(Object.getOwnPropertyDescriptor(arrayLike, 'length')); // {get: ƒ, set: ƒ, enumerable: false, configurable: false}
余談(arrayLike
は配列ライクな何か)。
JavaScript
1console.log(arrayLike.length); // 0 2arrayLike[0] = 'a'; 3arrayLike[1] = 'b'; 4console.log(arrayLike.length); // 2 5console.log(arrayLike); // {0: "a", 1: "b"} 6arrayLike.length = 0; 7console.log(arrayLike.length); // 0 8console.log(arrayLike); // {}
jQuery#length
jQuery#length と Array#length の挙動は、似て非なるものです。
jQuery#length は getter 及び setter ではなく、ただの静的プロパティとして定義されています。
大雑把に疑似コードを書くなら、こうです。
JavaScript
1function jQuery (selectorText) { 2 const nodeList = document.querySelectorAll(selectorText); 3 const jqueryObjects = {}; 4 5 nodeList.forEach((element, index) => jqueryObjects[index] = element); 6 jqueryObjects.length = nodeList.length; // 要素数に変動があった時、lengthプロパティに要素数を代入しておく 7 8 return jqueryObjects; 9} 10 11console.log(jQuery('body').length); // 1
jQuery#length
が参照された時、予め代入された数値を呼び出しています。
ですので、[質問への追記・修正、ベストアンサー選択の依頼] で質問者さんが想像されたように、
lengthは要素数を調べるという操作ではないかと考えたとき、プロパティよりもメソッドという単語の方が似合うと思ったからです。
length
プロパティを参照した時に要素数を計算して返しているわけではありません。
getter/setterではない事は、Object.getOwnPropertyDescriptor()
で確認できます。
(以降、jquery-3.4.1.min.js
が読み込まれている前提のコードを書きます)
HTML
1<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> 2<script> 3'use strict'; 4const jquery1 = jQuery(document); 5 6console.log(jquery1.length); // 1 7console.log(Object.getOwnPropertyDescriptor(jquery1, 'length')); // {value: 1, writable: true, enumerable: true, configurable: true} 8</script>
jQuery()
と new jQuery()
は等価であり、要素が存在しない時には jQuery.prototype.length
が参照される挙動をします(Array#length は違います)。
JavaScript
1const jquery2 = new jQuery; 2console.log(jquery2.length); // 0 3console.log(Object.getPrototypeOf(jquery2).length); // 0 4console.log(Object.getOwnPropertyDescriptor(jQuery.prototype, 'length')); // {value: 0, writable: true, enumerable: true, configurable: true} 5console.log(Object.getOwnPropertyDescriptor(jquery2, 'length')); // undefined
jQuery#length は「ただの代入されたプロパティ」なので、他所のコードから任意の数値に変更する事が出来てしまいます(Array#length は違います)。
JavaScript
1const jquery3 = jQuery('html'); 2 3console.log(jquery3.length); // 1 4jquery3.length = 4; // 不正値を代入してみる 5console.log(jquery3.length); // 4 (不正値に書き換わってしまう)
Array#length
Array#length
は、getter/setter ではありません。
JavaScript
1console.log(Object.getOwnPropertyDescriptor([], 'length')); // {value: 0, writable: true, enumerable: false, configurable: false}
Array#length
がsetterのような挙動を示すのは、配列がExotic Objectsとして内部メソッド [[DefineOwnProperty]]
を書き換えている為です。
- 9.4.2 Array Exotic Objects - ECMAScript® 2018 Language Specification
- 9.4.2.1 DefineOwnProperty - ECMAScript® 2018 Language Specification
JavaScript
1const array2 = [1,2,3,4,5,6,7,8,9]; 2 3array2.length = 3; // 要素数を3つに設定する 4console.log(array2); // [1,2,3]; (4つ目以降の要素が削除されている)
Array#length
はgetterでもありませんので、「要素数が確定するタイミング」は jQuery#length と基本的に同じです。
Array#length
を参照した時に要素数を計算しているわけではなく、要素数の変動が起こる処理(Array#push, Array#filter等)の中で予め、length値を確定させています。
JavaScript
1const array1 = [1,2]; 2 3array1.push(3); // Array#push は length プロパティに 3 を代入する 4console.log(array1.length); // 3
Array#length は要素数ではない
便宜上、「Array#length = 配列の要素数」の体で説明しましたが、厳密には要素数ではありません。
JavaScript
1const array3 = [1,,,4,5,6,,8,,10]; 2const values = Object.values(array); 3 4console.log(array3.length); // 10 5console.log(values.length); // 6 6console.log(values); // [1, 4, 5, 6, 8, 10]
上記コードで分かるように、Array#length は存在する要素インデックスに1を加算した数値になります。
Re: KoO. さん
投稿2019/05/14 13:31
編集2019/05/14 13:48総合スコア18194
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

退会済みユーザー
2019/05/14 21:50

0
BA選出されており、解決済みになっておりますが、
気になったので回答です。
過去バージョンのjQueryには、size()メソッドがありました。
(jQueryバージョン3以降では完全に廃止されている。)
機能としては今日のlengthプロパティと同じ機能です。
miyabi-sunさんのご回答が、的を射ていると思いますが、
ネイティブJavaScriptにおける、HTMLCollection型変数の要素数が格納されているプロパティも、
メソッドではなく、element.lengthとなっているので、
そのネイティブと同じように扱える、仕様を統一する、といったことも考慮されての仕様なのではないでしょうか?
あくまで個人的な感想であり、ジョン・レシグ氏の記事や意図などを参考にしているわけではありません。
そのライブラリ作成者の意図や、思いも参照されると、納得いく答えが見つかるかもしれませんよ。
投稿2019/05/14 08:09
総合スコア9555
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

退会済みユーザー
2019/05/14 21:44

0
ベストアンサー
たしかに「メッソドではなくプロパティだから」は、「なんでプロパティにしたのか」についての回答にはなってないですね。
https://qiita.com/toshi0383/items/656c10388f71c6ab44d2
JavaでもArray.lengthはプロパティらしく、↑の下部のコメントでやり取りがちょっとありますね。
文法を似せようとしていたはずなので、javascriptでも同様にプロパティにしたのかもしれません。
それと、自分も知らなかったのですが、、、
https://builder.japan.zdnet.com/script/sp_javascript-kickstart-2007/20366841/
lengthは書き込み可能ですw
この仕様だとプロパティになっている方が自然な気はします。
投稿2019/05/13 17:23
総合スコア767
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

退会済みユーザー
2019/05/13 17:41

あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。