かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

JavaScript (ES2015) 文字列中に変数展開できるテンプレート構文のメモ

JavaScriptで変数を展開した文字列を作成する時、+で文字列連結をしていましたがES2015(ES6)からは``(バッククォート)で囲うテンプレート構文(Template literal)で書くことができるようです。
IEAndroidでは未対応なブラウザもあるようなので、WEBサイト制作とかの場合はまだバベる必要がありそうです。
ブラウザ実装状況: テンプレート文字列 - JavaScript | MDN

テンプレート構文(Template literal)による文字列中での変数展開

var val = "JavaScript"
// 今までのやり方
var str1 = "Hello " + val + "!" // => Hello JavaScript!
// テンプレート構文
var str2 = `Hello ${val}!` // => Hello JavaScript!

文字列中で変数を展開させるプレースホルダーは${...}の形式で記述します。

プレースホルダー内で計算

プレースホルダ${...}内では計算や関数を呼ぶこともできるようです。

var a = 3, b = 5
console.log( `a + b = ${a + b}, a * 2 + b = ${a * 2 + b}` )
// => a + b = 8, a * 2 + b = 11

function sub(a, b) {
  return a - b
}
console.log( `a - b = ${sub(a, b)}` )
// => a - b = -2

同期的なことはできない

function timer() {
  setTimeout(()=> {
    return 'timeout!'
  }, 100)
}
console.log( `set timer ... ${timer()}` )
// => set timer ... undefined

改行

テンプレート構文(Template literal)内では改行がそのまま改行として扱われる。(\nも使える)

var str1 = "Hello\nJavaScript"
// ↓
var str2 = `Hello
JavaScript`
var str3 = `Hello\nJavaScript`
console.log(str2)
// Hello
// JavaScript
console.log(str3)
// Hello
// JavaScript
コード上で改行だけさせたい時

見やすくするとかでコード上でだけ改行させたいような場合

var tag1 = "<div>"
+ "<p>Hello JavaScript</p>"
+ "</div>"
console.log(tag1)
// => <div><p>Hello JavaScript</p></div>

var tag2 = "<div>\
<p>Hello JavaScript</p>\
</div>"
console.log(tag2)
// => <div><p>Hello JavaScript</p></div>

テンプレート構文(Template literal)では改行はそのまま改行になってしまうので、改行前に\を付けて改行すればOK。(以前の方法と同じ)

var tag = `<div>\
<p>Hello JavaScript</p>\
</div>`
console.log(tag)
// => <div><p>Hello JavaScript</p></div>

エスケープ

\\nをそのまま表示したいような場合、テンプレート構文(Template literal)ではString.rawを使う。(\エスケープする今までどおりの方法でもOK)

var str1 = "Hello\\nJava\\Script"
// ↓
var str2 = String.raw`Hello\nJava\Script`
var str3 = `Hello\\nJava\\Script`
console.log(str1 === str2, str1 === str3, str2 === str3)
// => true true true
console.log(str2)
// => Hello\nJava\Script

String.raw内でもプレースホルダ${...}は展開される

var name = "アシリパ"
String.raw`こんにちわ\n${name}さん`
// => こんにちは\nアシリパさん

${...}をそのまま表示させたい場合はString.rawを使わずに\${...}又は$\{...}エスケープする

var name = "アシリパ"
`こんにちは\\n\${name}さん`
// => こんにちは\n${name}さん
`こんにちは\\n$\{name}さん`
// => こんにちは\n${name}さん

String.rawを使うと\${...}$\{...}\が出力されてしまう

var name = "アシリパ"
String.raw`こんにちは\n\${name}さん`
// => こんにちは\n\${name}さん
String.raw`こんにちは\n$\{name}さん`
// => こんにちは\n$\{name}さん

String.raw``のトラップ

テキストの最後に\を出力したい場合String.rawだと最後の`エスケープしていると判断されエラーになるので注意が必要

"Hello\\nJava\\Script\\"
// => Hello\nJava\Script\
`Hello\\nJava\\Script\\`
// => Hello\nJava\Script\
String.raw`Hello\nJava\Script\`
// => Error

String.rawを使って最後に\を出力するには\を文字列結合するか、プレースホルダーで追加すればOK

String.raw`Hello\nJava\Script` + "\\"
// => Hello\nJava\Script\
var suffix = "\\"
String.raw`Hello\nJava\Script${suffix}`
// => Hello\nJava\Script\

エスケープして表示したい時は今までどおりの\エスケープする方法の方が良さそうかも…

String.raw()

静的メソッドであるString.raw()は、文字列リテラルのための PythonrプレフィックスC#@プレフィックスのような template strings のタグ関数です。この関数は、template strings の生の文字列形式を取得するために使用されます。
出典: String.raw() - JavaScript | MDN

String.rawは関数なので、function``で関数が実行される。

テンプレート構文(Template literal)で実行された関数に渡される引数

テンプレート構文(Template literal)で渡される第一引数は文字列をプレースホルダーで分割したArray-likeな変更不可能なオブジェクト、第二引数以降はプレースホルダーに入る値。値は計算された状態で関数に渡されるっぽい。

var a = 5, b = 10
function tag(strings, ...values) {
  console.log(strings) // 変更不可オブジェクト
  console.log(values) // プレースホルダーの値
  return strings
}

var str1 = tag`Hello${a+b}Java${a-b}script`
// strings: [ 'Hello', 'Java', 'script' ]
// vales: [ 15, -5 ]

var str2 = tag`Hello${b-a}Java${a*b}script`
// strings: [ 'Hello', 'Java', 'script' ]
// values: [ 5, 50 ]

console.log(str1 === str2) // true
console.log(typeof(str1)) // 'object'

// 変更しようとするとエラーになる
str1[1] = 'World'
// => TypeError: Cannot assign to read only property
delete str1[2]
// => TypeError: Cannot delete property
str1.push('!')
// => TypeError: Cannot add property

公式を見るとクロージャーな関数を返すとか色々な使い方ができるっぽいけど、実際に使う場面がイマイチピンこない...
 

はてなブログJavaScriptいい感じにシンタックスハイライトされなくて辛い…


[参考]

マークダウンで`バッククォートをcodeで囲って書くTips

初めてのJavaScript 第3版 ―ES2015以降の最新ウェブ開発

初めてのJavaScript 第3版 ―ES2015以降の最新ウェブ開発

Python3のTrueとFalse、それからNone

PythonのboolはTrueFalse
truefalseと小文字にするとエラーになる。

  • 0, 0.0, [], {}, "", Noneはbool化するとFalse
  • Trueは、1扱いにもなる
  • Falseは、0, 0.0扱いにもなる

Bool化

bool(0)    # False
bool(0.0)  # False
bool([])   # False
bool({})   # False
bool("")   # False
bool(None) # False
bool(1)    # True
bool(2)    # True
bool(-1)   # True
  • Noneはbool化するとFalse
  • 数値は0以外はTrueになる

==

==演算子は「等価(同じ値)」かを比較する

True ==
True == True  # True
True == False # False
True == None  # False
True == 1     # True
True == 2     # False

数値は1以外はTrueにはならない

False ==
False == True  # False
False == False # True
False == None  # False
False == 0     # True
False == 0.0   # True
False == []    # False
False == {}    # False
False == ""    # False
False == -1    # False

[], {}, "", Noneはbool化するとFalseだが、Falseとは等しくない。(==Falseになる)

None ==
None == True  # False
None == False # False
None == None  # True
None == 0     # False
None == 0.0   # False
None == []    # False
None == {}    # False
None == ""    # False

Noneと等しくなるのはNoneのときだけ。

is

is演算子は「同一のオブジェクトか」を判定する

追記 is を判定していたプログラムに誤りがあり True is True, False is False, None is NoneFalse になると書いていましたが、正確にはこれはら True になるが正しいので記事を訂正しました。

True is
True is True # True
True is 1    # False

True is TrueTrue だが、 ==ではTrueになっていた1is演算子で比較するとFalse

False is
False is False # True
False is 0     # False
False is 0.0   # False

False is FalseFalseだが、 Falseも同様に==ではになっていた 00.0is演算子ではFalse

None is
None is None # True

NoneNone == None のときと同じで None is None のときは True

int化

int(True)  # 1
int(False) # 0
int(None)
# TypeError: int() argument must be a string, a bytes-like object or a number, not 'NoneType'

True1False0int(None)はエラーになる。

暗黙の型変換?が行われる処理だと、True1False0として扱われる

True > False # true
3 + true     # 4
"7" - False
# => TypeError: unsupported operand type(s) for -: 'str' and 'bool'

数値にTrueを足すのは+1と同じ扱いになる。
javascriptのように- 0で数値化はできない。

PythonTrueFalseRubyfalsenilの時だけfalseで後はtrueに比べて、ちょっと複雑でした。
特にbool(2)Trueだけど、2 == TrueFalseとかはちょっと紛らわしい...


==, is の比較を試してみたコード

def ifEq(v):
    arg = [True, False, None, 0, 0.0, 1, 2, -1, [], {}, ""]
    str_v = str(v)
    for val in arg:
        if v == val:
            print(f"{str_v} == {str(val)} # True")
        else:
            print(f"{str_v} == {str(val)} # False")

def ifIs(v):
    arg = [True, False, None, 0, 0.0, 1, 2, -1, [], {}, ""]
    str_v = str(v)
    for val in arg:
        if v is val:
            print(f"{str_v} is {str(val)} # True")
        else:
            print(f"{str_v} is {str(val)} # False")

print("\n>> True ==")
ifEq(True)

print("\n>> False ==")
ifEq(False)

print("\n>> None ==")
ifEq(None)

print("---------------")

print("\n>> True is")
ifIs(True)

print("\n>> False is")
ifIs(False)

print("\n>> None is")
ifIs(None)

実行してみることができます 👉 Online PHP/Java/C++... editor and compiler | paiza.IO


[参考]

HomebrewでpyenvをアップデートしたらPythonが動かなくなった

HomebrewでpyenvをインストールしてPython環境を作っていました。

brew upgradepyenvがアップデートされた後Pythonを実行しようとしたら次のようなエラーが出るようになってしまいました。

$ python
/Users/user_name/.pyenv/shims/python: line 21: /usr/local/Cellar/pyenv/1.2.4/libexec/pyenv: No such file or directory

.pyenv/shims/pythonの中をエディタで見ると、pyenvのパスがベタで書かれていたので、brew upgradepyenvのバージョンが上がり読み込めなくなっていたのが原因のようでした。

pyenvのパスを再設定 (pyenv rehash)

$ pyenv rehash

このコマンドを実行すると.pyenv/shims/pythonのパスが再設定され無事Pythonが実行できるようになりました。

$ pyenv --help
...
   rehash      Rehash pyenv shims (run this after installing executables)

.pyenv/shims/pythonの中にあるパスを直接書き換えるのは、他にも影響がある可能性があるのであまりよろしくないでしょう。

pyenvをアップデートしたら、忘れなずpyenv rehashを実行する。
自動的に実行して欲しさ若干ありますが


[参考]

Pythonスタートブック [増補改訂版]

Pythonスタートブック [増補改訂版]