Underscore: ๋ฐฐ์—ด์ด ์•„๋‹Œ ๊ฐ’์— ๋Œ€ํ•œ ๋ฐฐ์—ด ์ฒด์ธ ๋ฉ”์„œ๋“œ ์˜ค๋ฅ˜

์— ๋งŒ๋“  2016๋…„ 03์›” 20์ผ  ยท  6์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: jashkenas/underscore

๋‹ค๋ฅธ ๋ฐฐ์—ด ๋ฒ”์ฃผ ๋ฐฉ๋ฒ•๊ณผ ๋‹ฌ๋ฆฌ ๋ฐฐ์—ด์ด ์•„๋‹Œ ๊ฐ’์— ๋Œ€ํ•œ ๋ฐฐ์—ด ์ฒด์ธ ๋ฐฉ๋ฒ• ์˜ค๋ฅ˜.

_().pop(); // error
_('').pop(); // error
_().first() // undefined

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

์ด๊ฒƒ์€ ๋˜ ๋‹ค๋ฅธ ๋ฌธ์ œ๋ฅผ ์•ผ๊ธฐํ•ฉ๋‹ˆ๋‹ค. ๋ฐฐ์—ด๊ณผ ์œ ์‚ฌํ•œ ๊ฒƒ์„ ๋ฐฐ์—ด๋กœ ์บ์ŠคํŒ…ํ•ด์•ผ ํ•ฉ๋‹ˆ๊นŒ?

_('hello').first() // 'h'
_('hello').pop() // ?

๋ชจ๋“  6 ๋Œ“๊ธ€

์ด๊ฒƒ์€ ๋˜ ๋‹ค๋ฅธ ๋ฌธ์ œ๋ฅผ ์•ผ๊ธฐํ•ฉ๋‹ˆ๋‹ค. ๋ฐฐ์—ด๊ณผ ์œ ์‚ฌํ•œ ๊ฒƒ์„ ๋ฐฐ์—ด๋กœ ์บ์ŠคํŒ…ํ•ด์•ผ ํ•ฉ๋‹ˆ๊นŒ?

_('hello').first() // 'h'
_('hello').pop() // ?

์ด๊ฒƒ์€ ์—ฌ์ „ํžˆ โ€‹โ€‹๋ฌธ์ œ์ธ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค - 1.8.3 t0 1.9.0์—์„œ ์šฐ๋ฆฌ ๋ฒ„์ „์„ ๋ฒ”ํ•œ ํ›„์— ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.

ํŽธ์ง‘: 1.9.1์—์„œ ์ˆ˜์ •๋œ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด ์ด ๋ฌธ์ œ๋ฅผ ๊ณ„์† ์—ด์–ด ๋‘์–ด์•ผ ํ•ฉ๋‹ˆ๊นŒ?

์ด ๋™์ž‘์€ ์—ฌ์ „ํžˆ โ€‹โ€‹1.10.2์— ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

๋‘˜ ๋‹ค ์ œ์ž๋ฆฌ์—์„œ ๊ฐ’ ์ˆ˜์ •์„ ํฌํ•จํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋‚ด ์˜ต์…˜์—์„œ ์•ฝ๊ฐ„ ๋” ์„ค๋“๋ ฅ ์žˆ๋Š” ๋Œ€์กฐ:

Object.assign(undefined, {a: 1})  // error
_().extend({a: 1})  // undefined

Array.prototype.push.call(undefined, 1)  // error
_().push(1)  // error

๋‚˜๋Š” ์ด๊ฒƒ์ด ์ผ๊ด€์„ฑ์ด ์—†๋‹ค๋Š” ๋ฐ ์–ด๋Š ์ •๋„ ๋™์˜ํ•ฉ๋‹ˆ๋‹ค.

์–ด๋–ค ์ข…๋ฅ˜์˜ ์‹ค์ œ ์ƒํ™ฉ์—์„œ ์ด๊ฒƒ์ด ๊ฐ€์žฅ ๋งŽ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ๊ทธ๋Ÿฌํ•œ ๊ฒฝ์šฐ์— ๋ž˜ํ•‘๋œ ๊ฐ์ฒด์— ๋Œ€ํ•ด ๋ฌด์—‡์„ ๊ธฐ๋Œ€ํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ๊ณ ๋ คํ•ด ๋ณผ ๊ฐ€์น˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฒด์ธ์˜ ์ค‘๊ฐ„ ์—์„œ ๋ž˜ํ•‘๋œ ๊ฐœ์ฒด๋Š” ์˜ˆ์ธก ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์•„๋ž˜ ์ฒด์ธ์—์„œ push ์— ์ „๋‹ฌ๋˜๋Š” ๋ž˜ํ•‘๋œ ๊ฐœ์ฒด๋Š” Array ๋˜๋Š” undefined ์ž…๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ด์œ ๋กœ Array ๋ž˜ํผ ๋ฉ”์„œ๋“œ๋Š” null ๊ฒ€์‚ฌ๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค๊ณ  ์ฃผ์žฅํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์‰ฌ์›Œ์š”.

_.chain(something).map(f).push(x)  // hoping to push x to an array

์˜ˆ์ธก๋œ ๋ž˜ํ•‘๋œ ๊ฐœ์ฒด๊ฐ€ ๋ฐฐ์—ด์ด ์•„๋‹Œ ๋‹ค๋ฅธ ๊ฒƒ์ผ ๊ฐ€๋Šฅ์„ฑ์ด ๋” ๋†’์œผ๋ฉด ํ‹€๋ฆผ์—†์ด ํ”„๋กœ๊ทธ๋ž˜๋จธ ์˜ค๋ฅ˜์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ทธ ๊ฒฝ์šฐ์— ์˜ˆ์™ธ๋ฅผ ๋˜์ง€๋Š” ๊ฒƒ์— ์‹ ๊ฒฝ ์“ฐ์ง€ ์•Š๋Š”๋‹ค.

_.chain(something).map(f).join('').push(x)  // hoping to push x to a string??

์ฒด์ธ์˜ ์‹œ์ž‘ ๋ถ€๋ถ„์—์„œ ์ด์•ผ๊ธฐ๋Š” ์–ธ๋œป๋ณด๊ธฐ์—๋Š” ์•ฝ๊ฐ„ ๋‹ค๋ฅด๊ฒŒ ๋ณด์ผ ์ˆ˜ ์žˆ์ง€๋งŒ ๋™์ผํ•˜๋ฉฐ null ์ฒดํฌ๋กœ ๋‚จ๊ฒจ ๋‘์–ด์•ผํ•œ๋‹ค๊ณ  ์ฃผ์žฅํ•ฉ๋‹ˆ๋‹ค. ์ ์–ด๋„ ํ”„๋กœ๊ทธ๋ž˜๋จธ๋Š” something ๊ฐ€ ๊ฐ€๋ณ€ ๋ฐฐ์—ด๊ณผ ๋น„์Šทํ•  ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์–ด๋–ค ์ด์œ ๊ฐ€ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด push ๋ฅผ ํ˜ธ์ถœํ•  ์ด์œ ๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

function giveMeAMutableArraylike(something) {
    _.chain(something).push(x)
}

something ๊ฐ€ ๊ฐ€๋ณ€ ๋ฐฐ์—ด๊ณผ ์œ ์‚ฌํ•˜๋‹ค๋Š” ๊ธฐ๋Œ€๋Š” ๋‹ค์–‘ํ•œ ์ด์œ ๋กœ ์ถฉ์กฑ๋˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  1. giveMeAMutableArraylike ๋Š” ์–ด๋–ค ์ด์œ ๋กœ๋“  null ๋˜๋Š” undefined ๋กœ ํ˜ธ์ถœ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์ฒซ ๋ฒˆ์งธ chain-in-the-middle ์˜ˆ์ œ์™€ ๋น„์Šทํ•˜๋ฉฐ ๋™์ผํ•œ null ๊ฒ€์‚ฌ๋กœ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  2. ํ˜ธ์ถœ์ž๋Š” giveMeAMutableArraylike ๊ฐ€ ๋ถ„๋ช…ํžˆ ํ•  ์ˆ˜ ์—†๋Š” ์ผ, ์ฆ‰ ๊ณ„์•ฝ ์œ„๋ฐ˜์„ ํ•˜๋„๋ก ํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋‘ ๋ฒˆ์งธ chain-in-the-middle ์˜ˆ์ œ์™€ ๋น„์Šทํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ์—๋„ ์˜ค๋ฅ˜๋ฅผ ๋˜์ง€๋Š” ๊ฒƒ์€ ๊ดœ์ฐฎ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.
  3. something ๋Š” ๋‹จ์ผ ์š”์†Œ๊ฐ€ ์žˆ๋Š” ๋ฐฐ์—ด ๋Œ€์‹  ์ˆœ์ˆ˜ํ•œ ๊ฐ’์ž…๋‹ˆ๋‹ค. ์ฆ‰, v [v] ์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋งŽ์€ JavaScript API์—์„œ ์ผ๋ฐ˜์ ์ธ ์ƒํ™ฉ์ž…๋‹ˆ๋‹ค. giveMeAMutableArraylike ์˜ ๊ณ„์•ฝ์ด ์ด๋ฅผ ํ—ˆ์šฉํ•œ๋‹ค๋ฉด v ๊ฐ€ ๋ฌด์—‡์ด๋“  ์ƒ๊ด€์—†์ด ์˜ค๋ฅ˜๋ฅผ ๋˜์ง€๋Š” ๊ฒƒ์€ ๋ถ„๋ช…ํžˆ ์ž˜๋ชป๋œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

ํ›„์ž์˜ ๊ฒฝ์šฐ Underscore๋Š” giveMeAMutableArraylike ๊ฐ€ ๋‹จ์ผ ๋ฒ ์–ด ์š”์†Œ๋ฅผ ํ—ˆ์šฉํ•˜๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ์•Œ ์ˆ˜ ์—†์œผ๋ฏ€๋กœ giveMeAMutableArraylike ์˜ ํ”„๋กœ๊ทธ๋ž˜๋จธ๊ฐ€ ํŠน์ • ๊ณ„์•ฝ์„ ๊ตฌํ˜„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. Underscore์™€ ๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•œ ๋ชจ๋“  ๊ณ„์•ฝ์— ๋Œ€ํ•ด ์˜ฌ๋ฐ”๋ฅธ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์€ ์—†์Šต๋‹ˆ๋‹ค.

| ํ˜„์žฌ ํ–‰๋™ | ๋‹จ์ผ ์š”์†Œ ๋ฐฐ์—ด๋กœ ๋ž˜ํ•‘
---|---|---
๊ณ„์•ฝ์€ ๋ฒ ์–ด ๋‹จ์ผ ์š”์†Œ๋ฅผ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค | _ํ”„๋กœ๊ทธ๋ž˜๋จธ๊ฐ€ ๊ฐœ์ž…ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค_ | ๋ฐ‘์ค„์€ ์ž๋™์œผ๋กœ ์˜ฌ๋ฐ”๋ฅธ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
๊ณ„์•ฝ์€ ๋ฒ ์–ด ๋‹จ์ผ ์š”์†Œ๋ฅผ ํ—ˆ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค | ๋ฐ‘์ค„์€ ์ž๋™์œผ๋กœ ์˜ฌ๋ฐ”๋ฅธ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค | _ํ”„๋กœ๊ทธ๋ž˜๋จธ๊ฐ€ ๊ฐœ์ž…ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค_

๋˜ํ•œ ๊ฐ’์„ ๋ž˜ํ•‘ํ•ด์•ผ ํ•˜๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ์–ด๋–ป๊ฒŒ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๊นŒ? ์ผ๋ถ€ ๊ณ„์•ฝ์€ Array ๊ฐ€ ์•„๋‹Œ ๋ชจ๋“  ๊ฒƒ์„ ๋ž˜ํ•‘ํ•˜๋ ค๊ณ  ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ๋‹ค๋ฅธ ๊ณ„์•ฝ์€ arguments ๋ฐ ์ผ๋ฐ˜ ๊ฐœ์ฒด๋ฅผ ์žˆ๋Š” ๊ทธ๋Œ€๋กœ ์ „๋‹ฌํ•˜๊ธฐ๋ฅผ ์›ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์‹œ ๋งํ•˜์ง€๋งŒ ์ด๊ฒƒ์€ Underscore์™€ ๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์‚ฌ์šฉ์ž๋ฅผ ๋Œ€์‹ ํ•˜์—ฌ ์ถ”์ธกํ•  ์ˆ˜ ์—†๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋” ์ด๊ตญ์ ์ธ ์ƒํ™ฉ๋„ ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๋ฌธ์ž์—ด๊ณผ ๊ฐ™์€ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๋Š” ๋ฐฐ์—ด๊ณผ ๊ฐ™์€ ๊ฐ์ฒด๋ฅผ ํ—ˆ์šฉํ•˜๊ณ  ๋จผ์ € Array ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ณ„์•ฝ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ€๋Šฅํ•œ ๋ชจ๋“  ๋ณ€ํ˜•์„ ํฌ๊ด„ํ•˜๋Š” ๊ฒƒ์€ ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ์ตœ์†Œ ๋ณ€๊ฒฝ ์›์น™์— ๋”ฐ๋ผ ์ด๋ฏธ ์ง€์›๋œ ๊ณ„์•ฝ์„ ํฌ์ƒ์‹œํ‚ค๋ฉด์„œ ์ผ๋ถ€ ๊ณ„์•ฝ์„ ์ง€์›ํ•˜๋Š” ๊ฒƒ์— ๋Œ€ํ•œ ์„ค๋“๋ ฅ ์žˆ๋Š” ์ฃผ์žฅ์ด ์—†์Šต๋‹ˆ๋‹ค. ๊ตฌํ˜„์„ ๊ฐ€๋Šฅํ•œ ํ•œ ์ตœ์†Œํ™”ํ•ด์•ผ ํ•˜๋Š” ๊ฐ•๋ ฅํ•œ ๊ฒฝ์šฐ๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ์˜ฌ๋ฐ”๋ฅธ ์†”๋ฃจ์…˜์€ null ๊ฒ€์‚ฌ๋งŒ ์‚ฝ์ž…ํ•˜๊ณ  ๊ทธ๋Œ€๋กœ ๋‘๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ์ƒํ™ฉ์—์„œ ๋ฐฉ์–ดํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด๋Š” Underscore์˜ ๋™์ž‘์— ๋Œ€ํ•œ ์œ ์ผํ•œ ๋ณ€๊ฒฝ์ด๋ฉฐ _.extend ์˜ ๋™์ž‘๊ณผ๋„ ์ผ์น˜ํ•ฉ๋‹ˆ๋‹ค. ๋„ ๊ฒ€์‚ฌ๋Š” ์—ฌ์ „ํžˆ ๋ฒ„๊ทธ ์ˆ˜์ •์œผ๋กœ ๊ฐ„์ฃผ๋  ์ˆ˜ ์žˆ์ง€๋งŒ ๊ทธ ์ด์ƒ์„ ์ˆ˜ํ–‰ํ•˜๋ฉด ์‹ ์†ํ•˜๊ฒŒ ์ž„์˜์˜ ์ฃผ์š” ๋ณ€๊ฒฝ ์‚ฌํ•ญ์œผ๋กœ ์ „ํ™˜๋ฉ๋‹ˆ๋‹ค.

@jashkenas ์ด๊ฒƒ์— ๋Œ€ํ•ด ๋น›์„ ๋ฐœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ? ๋™์˜ํ•˜์‹œ๋ฉด null ์ฒดํฌ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” pull request๋ฅผ ์ค€๋น„ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

@jgonggrijp โ€” null ๊ฒ€์‚ฌ๋กœ ๊ตฌํ˜„ํ•˜๋ ค๋Š” ๋™์ž‘์„ ํ•œ๋‘ ๋ฌธ์žฅ์œผ๋กœ ์ž์„ธํžˆ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

null ๊ฐ’์— ๋Œ€ํ•ด ํ˜ธ์ถœ๋  ๋•Œ ๋ฐฐ์—ด ๋ฉ”์„œ๋“œ๊ฐ€ ๋ช…์‹œ์ ์œผ๋กœ ์˜ˆ์™ธ๋ฅผ throwํ•ฉ๋‹ˆ๊นŒ? ์•„๋‹ˆ๋ฉด nullish ๊ฐ’์ด ํ˜ธ์ถœ๋  ๋•Œ ํ—›์†Œ๋ฆฌ๋ฅผ ํ•˜์ง€ ์•Š์„๊นŒ์š”?

@jashkenas ๋„ค. ๋‚ด๊ฐ€ ๊ตฌํ˜„ํ•  ๋™์ž‘์€ Underscore ๋ฐฐ์—ด ํ•จ์ˆ˜์˜ ์Šคํƒ€์ผ์„ ๋”ฐ๋ฅด๋Š” no-op์ž…๋‹ˆ๋‹ค.

https://github.com/jashkenas/underscore/blob/4cf715f593805ba8d7c5685cd06c82b3cd9b55ae/modules/index.js#L495

length ์ˆ˜ํ‘œ๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š๋‹ค๋Š” ์ ์„ ์ œ์™ธํ•˜๊ณ ๋Š”

if (obj == null) return chainresult(this, obj);

์ด ๋‘ ์ค„ ์‚ฌ์ด:

https://github.com/jashkenas/underscore/blob/4cf715f593805ba8d7c5685cd06c82b3cd9b55ae/modules/index.js#L1654 -L1655

ํ”Œ๋Ÿฌ์Šค ํ…Œ์ŠคํŠธ๋Š” ๋ฌผ๋ก ์ด๊ณ  ์ด๋Ÿฌํ•œ ํ…Œ์ŠคํŠธ์—์„œ ํ•„์š”์„ฑ์ด ๋“œ๋Ÿฌ๋‚  ๊ฒฝ์šฐ ์ด ์ค„ ์ „์— ๋ฉ”์„œ๋“œ๋ฅผ no-op์œผ๋กœ ๋ฐ”๊พธ๋Š” null ๊ฒ€์‚ฌ๋„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

https://github.com/jashkenas/underscore/blob/4cf715f593805ba8d7c5685cd06c82b3cd9b55ae/modules/index.js#L1665

๋‚˜์—๊ฒŒ ์ž˜ ๋“ค๋ฆฐ๋‹ค!

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰