◆ Object.defineProperty で undefined みたいな上書き不可能なプロパティを作れる
◆ configurable を true で最初に定義しないと書き換えを可能にしたり不可能にしたりを自由に切り替えられなくなる
◆ 普通に代入してプロパティを作ると configurable は true 
◆ configurable が false でも書き換え可能→書き換え不可能への変更はできる
◆ configurable も writable も false なプロパティを定義してしまうと変更は不可能

グローバルの undefined って上書きできないですよね
Object.defineProperty を使えば自分でも同じような書き換え不可能なプロパティを作れます


まず Object.defineProperty なのでなにかオブジェクトのプロパティであることが前提です
グローバル変数は window のプロパティなのでグローバル変数にプロパティ定義はできますが ローカル変数だと無理です
Object.defineProperty(window, "xyz", {configurable: false, writable: false, value: 10})

こんな感じで使います

第一引数がオブジェクトで第二引数がプロパティ名です
第一引数のオブジェクトに対して 第二引数で指定された名前のプロパティを定義します
第三引数で定義するプロパティのオプションを指定します


value は名前の通りそのプロパティの値です
省略すると定義前の値そのままです
未定義なら undefined になります

writable はそのプロパティを書き換えできるかどうかです
false にすると代入してもエラーはでませんが値は変化しません
Object.defineProperty(window, "test1", {writable:true,value:100})
console.log(test1) // 100
console.log(test1 = 99) // 99
console.log(test1) //99

Object.defineProperty(window, "test2", {writable:false,value:100})
console.log(test2) // 100
console.log(test2 = 99) // 99
console.log(test2) // 100

代入自体は成功扱いで代入式自体の返り値は代入しようとした値です
ただし 代入はされないので次にプロパティにアクセスすると代入前の値が返って来ます
(ES6 の const はこれを false にしてるだけじゃないかな と思ってる)


configurable は Object.defineProperty で定義したときのオプション(writableとか)を変更できるかどうかです
省略すると false になり 変更不可能ですが 普通にプロパティを代入でつくると true となっています
window.test11 = 11
Object.defineProperty(window, "test11", {writable:true})
Object.defineProperty(window, "test11", {writable:false})
Object.defineProperty(window, "test11", {writable:true})

Object.defineProperty(window, "test12", {writable:true})
Object.defineProperty(window, "test12", {writable:false})
Object.defineProperty(window, "test12", {writable:true})
Uncaught TypeError: Cannot redefine property: test12

test11 では先にプロパティを作っておいて writable を true → false → true にしてもエラーは起きないです
test12 ではプロパティが無い状態で writable を true → false → true に切り替えると 最後の true への変更でエラーです

writable: false で変更禁止にすると configurable で許可されてないかぎり変更可能にはできなくなります
configurable は最初に指定すればよくて
Object.defineProperty(window, "test13", {writable:true, configurable:true})
Object.defineProperty(window, "test13", {writable:false})
Object.defineProperty(window, "test13", {writable:true})

test13 = 100
console.log(test13) // 100
これはエラーがでません

最初に false するとあとから true にはできません
Object.defineProperty(window, "test14", {writable:true, configurable:false})
Object.defineProperty(window, "test14", {configurable:true})
Uncaught TypeError: Cannot redefine property: teset14

configurable が false でも書き換え可能から書き換え不可能へは切り替えできるらしく これはエラーが出ません
Object.defineProperty(window, "test15", {writable:true, configurable:false})
Object.defineProperty(window, "test15", {writable:false})

test15 = 555
console.log(test15) // undefined


ところで undefined も writable を true にすることができません
Object.defineProperty(window, "undefined", {writable:true, configurable:true})
Uncaught TypeError: Cannot redefine property: undefined

ということは undefined は
Object.defineProperty(window, "undefined", {writable:false, configurable:false})
で作られてる
のだと思います


他にも第三引数に使えるオプションは for in などでプロパティがあらわれるかを決める enumerable や getter, setter を表す get と set もあります
第三引数のオブジェクトのキー名は間違っていてもエラーは出ないで動かないだけです
特に enumerable や configurable はタイプミスしやすいと思いますので注意が必要です
(私はよく enumable とか configable とか書いて「あれ?動かない」とか言ってます)