jQuery scrollTopの引数について

書くのが遅くなってしまいましたが、本記事は[LIFULL Advent Calendar 2018](https://qiita.com/advent-calendar/2018/lifull)の25日目の記事になります。

今年ももう終わりそうですが、全然Qiitaに記事を書けていません。
個人的には、Vue.jsやBigQueryなど初めて触れる技術もあり、なかなか刺激が多い一年だったんですが、こういう場所でアウトプットするほどの内容でもなく。

毎年クリスマスにAdvent Calendarでなにか書く星のもとに生まれたので、先日へーっとなった件を書いておきます。


##三行で
- ``jQuery.scrollTop``には``function``を引数で渡すことができる
- 公式ドキュメントを見てもよくわからん
- ソース頑張って追ったらわかった

##はじめに
デバイス問わず、サイト内でユーザーのアクションに応じて画面をスクロールすることがあると思います。
jQueryを使って下記のような感じです。

```javascript
// hoge要素をクリックしたら、fuga要素のトップまでスクロール
$('#hoge').on('click', function () {
  // fuga要素のwindow内での高さを取得
  var height = $('#fuga').offset().top;

  $（window).scrollTop(height);
});
```

最近ソースレビューをしていて、あれ？となったのですが、``scrollTop``の引数に``function``を渡しても動きます。

```javascript
$('#hoge').on('click', function () {
  $（window).scrollTop(getFugaTop);
});

// fuga要素のwindow内での高さを取得する
var getFugaTop = function () {
  return $('#fuga').offset().top;
};
```

これが、下記のように関数実行していたら全然違和感なかったのですが、上記の通り``function``自体が渡せるのです。

```javascript
// わかる
$（window).scrollTop(getFugaTop());

// え??? でも動く
$（window).scrollTop(getFugaTop);
```

##公式ドキュメントを見てみる
https://api.jquery.com/scrollTop/
> .scrollTop( value )
value
Type: Number
A number indicating the new position to set the scroll bar to.

だよね、数値渡すものだと思ってた。
わからぬ。

##ソースを見てみる
###offset.js
https://github.com/jquery/jquery/blob/master/src/offset.js#L199

長いけど大事なところなので全部引っ張ってきた。

```javascript
// Create scrollLeft and scrollTop methods
jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) {
	var top = "pageYOffset" === prop;

	jQuery.fn[ method ] = function( val ) {
		return access( this, function( elem, method, val ) {

			// Coalesce documents and windows
			var win;
			if ( isWindow( elem ) ) {
				win = elem;
			} else if ( elem.nodeType === 9 ) {
				win = elem.defaultView;
			}

			if ( val === undefined ) {
				return win ? win[ prop ] : elem[ method ];
			}

			if ( win ) {
				win.scrollTo(
					!top ? val : win.pageXOffset,
					top ? val : win.pageYOffset
				);

			} else {
				elem[ method ] = val;
			}
		}, method, val, arguments.length );
	};
} );
```

どうやら``scrollTop``,``scrollLeft``はなかで``access``という関数を使いながら、``window.scrollTo``に委譲しているらしい。

``access``ってなにものだ？

###access.js

https://github.com/jquery/jquery/blob/master/src/core/access.js

ファイル全部になっちゃうので要所だけ。

```javascript
var access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
  var i = 0,
    len = elems.length,
    bulk = key == null;
```

これの``fn``が上の関数ね。``value``が呼び出し元の引数（今回の大元の引数）か。

```javascript
if ( !isFunction( value ) ) {
  raw = true;
}
```
お？``value``がfunctionだと``raw``はいじられず、今回だと``undefined``らしい。

```javascript
if ( fn ) {
  for ( ; i < len; i++ ) {
    fn(
      elems[ i ], key, raw ?
      value :
      value.call( elems[ i ], i, fn( elems[ i ], key ) )
    );
  }
}
```

``scrollTop``側の``fn``に戻って、第三引数の``val``が``value.call``になるんか！
しかもご親切に``element``とかcallbackとして``fn``渡してくれてたりでいろいろやれそう。
なんでこれがドキュメントに書いてないんだ？？

##最後に
これって多分``scrollTop``とかに限った話じゃなさそうですよね。
``access``経由で実装されているjQueryのメソッドなら同じことができそう。
https://github.com/jquery/jquery/search?l=JavaScript&q=access

例えば、、
> https://github.com/jquery/jquery/blob/master/src/dimensions.js
// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods

なんだかAdvent Calendarやクリスマスにふさわしい話でもないですが、最近へーっとなったお話でした。