プロパティを保持し、番号をインクリメントするNumberオブジェクトでこのコードで何が起こっていますか?


246

最近のツイートには、このJavaScriptのスニペットが含まれていました。

誰かがその中で何が起こっているのかを段階的に説明できますか?

> function dis() { return this }
undefined
> five = dis.call(5)
Number {[[PrimitiveValue]]: 5}
> five.wtf = 'potato'
"potato"
> five.wtf
"potato"
> five * 5
25
> five.wtf
"potato"
> five++
5
> five.wtf
undefined
> five.wtf = 'potato?'
"potato?"
> five.wtf
undefined
> five
6

特に、私には明確ではありません。

  • なぜ結果がdis.call(5)あるNumberのいくつかの種類と[[PrimitiveValue]]性質が、結果five++とは、five * 5単なる数字のように見える525(ないNumber秒)
  • five.wtfプロパティがfive++インクリメント後に消える理由
  • 割り当てが明らかに値を設定しているにもかかわらず、インクリメントfive.wtf後にプロパティが設定できなくなった理由。five++five.wtf = 'potato?'

6
あのツイートを見た!それはとても奇妙です、あなたがそれを掛けているので、それはオブジェクトにはまったく影響を与えませんが++、基礎となるタイプに影響を与えるようです
Callum Linington '28

8
わからなかった行はありますか?PrePost増分?乗算後、それは再びプロパティNumberを持たないオブジェクトwtfです...しかし、それでもそれはobjectそれゆえにそれが持つことができますproperties..
Rayon

25
を使用して呼び出すdisdis.call(5)、プリミティブ番号5Numberを含むタイプのオブジェクトにラップされる5ため、このオブジェクトをオブジェクトとして返すことができthisます。は++それを、プロパティを含めることができないプリミティブ番号に変換するため、wtf未定義になり始めます。
GSerg


5
@Rayon ...そしてそれはES6で変更されました。したがって、この全体が起こるのは止まります。
GSerg

回答:


278

OPはこちら。スタックオーバーフローでこれを見て面白い:)

動作を確認する前に、いくつかのことを明確にすることが重要です。

  1. 数値数値オブジェクトa = 3vs a = new Number(3))は大きく異なります。1つはプリミティブで、もう1つはオブジェクトです。属性をプリミティブに割り当てることはできませんが、オブジェクトには割り当てることができます。

  2. 2つの間の強制は暗黙的です。

    例えば:

    (new Number(3) === 3)  // returns false
    (new Number(3) == 3)   // returns true, as the '==' operator coerces
    (+new Number(3) === 3) // returns true, as the '+' operator coerces
  3. すべてのには戻り値があります。ときREPLは、式を読み取って実行し、これは、それが表示するものです。戻り値は、多くの場合、ユーザーが考えていることを意味せず、真実ではないことを意味します。

はい、ここに行きます。

JavaScriptコードの元の画像

誓約。

> function dis() { return this }
undefined
> five = dis.call(5)
[Number: 5]

関数を定義disして呼び出すと、それを5。これにより5、コンテキストとして関数が実行されます(this)。ここでは、数値から数値オブジェクトに強制変換されています。厳密なモードでは これが起こらなかったことに注意してください。

> five.wtf = 'potato'
'potato'
> five.wtf
'potato'

今、私たちは、属性セットfive.wtfにし'potato'て、オブジェクトとして5と、確かにそれは受け入れる単純な代入を

> five * 5
25
> five.wtf
'potato'

ではfiveオブジェクトとして、私はそれはまだ簡単な算術演算を実行できることを確認してください。できる。その属性はまだ残っていますか?はい。

ターン。

> five++
5
> five.wtf
undefined

今チェックしfive++ます。後置インクリメントのトリックは、式全体が元の値に対して評価され、次に値がインクリメントされることです。fiveまだ5であるように見えますが、実際には式は5に評価され、次にに設定さfive6ます。

fiveに設定されただけでなく6、強制的にNumber値に戻され、すべての属性が失われました。プリミティブは属性を保持できないため、five.wtf未定義です。

> five.wtf = 'potato?'
'potato?'
> five.wtf
undefined

私は再び属性を再割り当てしようとwtfしますfive。戻り値はそれが固定されていることを意味しますが、実際にはそうでfiveはありません。式はに評価されます'potato?'が、確認すると割り当てられていないことがわかります。

威信。

> five
6

エバー後置インクリメント以来、fiveされています6


70
よく見ていますか?
ワドル

3
@Nathan Longまあ、JavaScriptが最初からかなり借りていたJavaでは、すべてのプリミティブに同等のクラスがあります。intそしてInteger、例えば。これは、関数を作成し、doSomething(Object)それでもプリミティブを与えることができるようにするためだと思います。この場合、プリミティブは対応するクラスに変換されます。しかし、JSは実際には型を気にしないので、理由はおそらく他の何かです
16:29のSuppen

4
@Eric最初に行うこと++は、値にToNumberを適用することです。文字列で同様のケースの比較:あなたが持っている場合はx="5"、そのx++番号を返します5
アプシラー2016

2
本当に魔法はすべてdis.call(5)、つまりオブジェクトへの強制で発生します。
mcfedr

3
@gmanを除いて、標準ライブラリの大きなチャンクの名前付け、標準オブジェクトタイプの動作、および中括弧とセミコロンの構文を使用するという事実(たとえセミコロンがオプション)は、Javaプログラマーが使い慣れるように設計されているという事実から派生しています。はい、初心者には混乱します。それは影響が存在しないという意味ではありません。
Jules

77

数値を表すには、2つの異なる方法があります。

var a = 5;
var b = new Number(5);

1つ目はプリミティブ、2つ目はオブジェクトです。すべての意図と目的はどちらも同じように動作しますが、コンソールに出力すると外観が異なります。重要な違いの1つは、プリミティブでは受け取られないのとnew Number(5)同じように、オブジェクトとして、通常のと同じように新しいプロパティを受け入れることです。{}5

a.foo = 'bar';  // doesn't stick
b.foo = 'bar';  // sticks

最初のdis.call(5)部分については、「this」キーワードの仕組みを参照してください。への最初の引数がcallの値として使用され、thisこの操作により数値がより複雑なNumberオブジェクト形式に強制されるとしましょう。* ++追加操作+により新しいプリミティブが生成されるため、後でプリミティブ形式に強制的に戻します。

> five = dis.call(5)  // for all intents and purposes same as new Number(5)
Number {[[PrimitiveValue]]: 5}
> five.wtf = 'potato'
"potato"
> five.wtf
"potato"

Numberオブジェクトは、新しいプロパティを受け入れます。

> five++

++結果は新しいプリミティブ6値になります...

> five.wtf
undefined
> five.wtf = 'potato?'
"potato?"
> five.wtf
undefined

...カスタム属性を持たず、受け入れません。

* 厳密モードでは、this引数の扱いが異なり、に変換されないことに注意してくださいNumber。実装の詳細については、http://es5.github.io/#x10.4.3を参照してください。


2
@Pharapは、C / C ++で実行できるより確実な方法です#define true false。またはJavaでは、数値の意味を再定義できます。これらは良い堅固な言語です。実際、すべての言語には同様の「トリック」があり、意図したとおりに機能するが奇妙に見える結果が得られます。
VLAZ

1
@Vld OKそれを言い換えさせてください、これが私がアヒルのタイピングを嫌う理由です。
Pharap

59

JavaScriptの世界に強制が存在する-探偵物語

ネイサン、何を発見したのか分からない。

私はこれを数週間調査してきました。昨年10月の嵐の夜に始まりました。誤ってNumberクラスに偶然出会いました。つまり、なぜJavaScriptにNumberクラスがあったのでしょうか。

次に何を調べるつもりだったのか。

JavaScriptは、ユーザーに通知せずに、数値をオブジェクトに変更し、オブジェクトを鼻の下の数値に変更していることがわかりました。

JavaScriptは誰も追いつかないことを望んでいましたが、人々は奇妙な予期しない振る舞いを報告してきました。

これは、これまでに判明したことです。私がこれをあなたに言うべきかどうかさえ知りません-あなたはあなたのJavaScriptをオフにしたいかもしれません。

> function dis() { return this }
undefined

その関数を作成したとき、おそらく次に何が起こるか分からなかったでしょう。すべてが正常に見え、すべてが正常でした-今のところ。

エラーメッセージは表示されず、コンソール出力に "undefined"と表示されるだけで、期待どおりの結果になります。結局のところ、これは関数宣言でした-何も返さないことになっています。

しかし、これはほんの始まりにすぎませんでした。次に何が起こったか、誰も予測できなかったでしょう。

> five = dis.call(5)
Number {[[PrimitiveValue]]: 5}

そうですね、あなたはを期待していました5が、それはあなたが得たものではありませんでした。

同じことが私にも起こりました。

どうしたらいいかわからなかった。それは私にナッツを運転しました。眠れず、食べられず、飲み干そうとしましたが、Mountain Dewの量を忘れることはありませんでした。まったく意味がありませんでした!

そのとき私は本当に何が起こっているのかを知りました-それは強制でした、そしてそれは私の目の前でそこで起こっていました、しかし私はそれを見るにはあまりにも盲目でした。

Mozillaは、誰も見ないだろうと思っていた場所にドキュメントを置いて埋めようとしました。

何時間も再帰的に読んだり、再読したり、再読したりした後、私はこれを発見しました:

「...およびプリミティブ値はオブジェクトに変換されます。」

Open Sansフォントで表記できるように、それはそこにありました。それはcall()機能でした-どのように私はそんなに愚かになることができましたか?!

私の番号はもはや番号ではありませんでした。私がそれを渡した瞬間call()、それは別のものになりました。それは...オブジェクトになりました。

最初は信じられませんでした。どうしてこれが本当だろうか?しかし、身の回りで高まっている証拠を無視することはできませんでした。見ればすぐそこです。

> five.wtf = 'potato'
"potato"

> five.wtf
"potato"

wtf正しかった。数値にカスタムプロパティを含めることはできません-私たちは皆それを知っています!それは彼らがアカデミーであなたに教える最初のことです。

コンソールの出力を見た瞬間はわかっていたはずです。これは、私たちが思っていた数ではありませんでした。これは詐欺師でした-私たちの甘い無実の数としてそれ自体を偽装するオブジェクト。

これはでした... new Number(5)

もちろん!それは完璧な意味がありました。call()やるべき仕事があり、関数を呼び出す必要がありthis、そのためにはデータを入力する必要があり、数字ではそれを実行できないことを知っていました。それが私たちの数を強制することを意味する場合。call()5を見たとき、彼は機会を見ました。

それは完璧な計画でした。だれもが見ないようになるまで待って、私たちの番号をそれと同じように見えるオブジェクトに交換してください。数値を取得すると、関数が呼び出され、誰も賢くなりません。

それは本当に完璧な計画でしたが、すべての計画と同様に、完璧な計画であっても、そこには穴があり、私たちはまさにそれに陥りそうでした。

ほら、call()理解できなかったのは、彼が町で数字を強制できる唯一の人ではなかったということでした。結局のところ、これはJavaScriptでした。強制はどこにでもありました。

call() 私の番号を受け取り、私が彼の小さな詐欺師からマスクを外してスタックオーバーフローコミュニティ全体に彼を公開するまで、私は止めるつもりはありませんでした。

しかし、どうやって?私は計画が必要でした。確かにそれは数字のように見えますが、私はそうではないことを知っています。それを証明する方法があるはずです。それでおしまい!数字のように見えますが、数字のように振る舞うことができますか?

私はfive彼に5倍大きくする必要があると言った-彼は理由を尋ねなかったし、私は説明しなかった。次に、優れたプログラマーが行うことを実行しました。確かに彼はこれから自分の道を偽ることができる方法はありませんでした。

> five * 5
25
> five.wtf
'potato'

畜生!うまくfive乗算しただけでなく、wtfまだそこにありました。この男と彼のジャガイモをくそー。

一体何が起こっていたのですか?私はこのすべてについて間違っていましたか?あるfive数は本当に?いいえ、私は何かを逃しているに違いない、それを知っている、忘れてはならないものがある、私が完全に見落としているほど単純で基本的なものがある。

これは見栄えが良くなかった、私はこの答えを何時間も書いていたが、私はまだ私の主張をすることに近づいていなかった。私はこれを続けることができず、結局人々は読むのをやめてしまい、私は何かを考えなければならず、それを速く考えなければなりませんでした。

それで待ってください!five25ではなかった、25は結果、25は完全に異なる数だった。もちろん、どうすれば忘れられますか?数値は不変です。5 * 5何を掛けても、何にも割り当てられず、新しい番号を作成するだけ25です。

ここで何が起こっているのでしょう。どういうわけか私は乗算する場合five * 5five数に強制取得する必要があり、その数は乗算に用いられるものでなければなりません。それfive自体の値ではなく、コンソールに出力されるのはその乗算の結果です。five何も割り当てられません-もちろん変更されません。

それではfive、操作の結果を自分に割り当てるにはどうすればよいですか。わかった。five考える前に「++」と叫んだ。

> five++
5

ああ!彼がいた!誰もが知っていること5 + 16、これは私が明らかにするために必要な証拠であり、それfiveは数ではなかった!詐欺師でした!数える方法を知らなかった悪い詐欺師。そして、それを証明することができました。実数の作用は次のとおりです。

> num = 5
5
> num++
5

待つ?ここで何が起こっていましたか?私はつぶしに夢中になりすぎて、fiveポストオペレーターのしくみを忘れてしまいました ++最後にを使用するfiveと、現在の値を返し、次に増分しますfive。コンソールに出力されるのは、操作が発生するの値です。num実際に6それを証明することができました:

>num
6

five実際に何であったかを確認する時間:

>five
6

...それはまさにあるべき姿でした。five良かった-私はより良かった。もしはfiveまだそれがまだ性質を持つことになり意味し、オブジェクトだったwtfと私はそれがなかったベットすべてに喜んでいました。

> five.wtf
undefined

ああ!私が正しかった。彼がいた!five今は数字でした-それはもうオブジェクトではありませんでした。今回は乗算のトリックでは節約できないことを知っていました。参照はfive++本当にありますfive = five + 1。乗算とは異なり、++演算子はに値を割り当てますfive。より具体的には、それの結果割り当てfive + 1だけ乗算戻る新しい不変の場合のように

私は彼がいることを知っていました、そして彼がそれから彼の方法を身をよじることができないことを確認するためだけに。もう一回試してみました。私が正しく、five今は本当に数であるなら、これはうまくいきません:

> five.wtf = 'potato?'
'potato?'

今回、彼は私をだますつもりはありませんでした。potato?割り当ての出力なので、コンソールに出力されることはわかっていました。本当の問題は、wtfまだそこにあるのでしょうか?

> five.wtf
undefined

私が思ったように-何もない-数にプロパティを割り当てることができないからです。私たちはアカデミーの最初の年であることを学びました;)

ネイサン、ありがとう。この質問をするあなたの勇気のおかげで、私はこれをすべて私の後ろに置き、新しいケースに進むことができます。

このような機能についてtoValue()。ああ親愛なる神。やった!


9
それを忘れてジェイク。それはJavascriptです。
Seth

JSでコンストラクタ関数を「クラス」と呼ぶのはなぜですか?
evolutionxbox

27
01 > function dis() { return this }
02 undefined
03 > five = dis.call(5)
04 Number {[[PrimitiveValue]]: 5}
05 > five.wtf = 'potato'
06 "potato"
07 > five.wtf
08 "potato"
09 > five * 5
10 25
11 > five.wtf
12 "potato"
13 > five++
14 5
15 > five.wtf
16 undefined
17 > five.wtf = 'potato?'
18 "potato?"
19 > five.wtf
20 undefined
21 > five
22 6

01disコンテキストオブジェクトを返す関数を宣言します。thisストリクトモードを使用しているかどうかに応じて、変化を表すものは何ですか。関数が次のように宣言されている場合、例全体の結果は異なります。

> function dis() { "use strict"; return this }

これは、ES5仕様のセクション10.4.3で詳しく説明されています

  1. 関数コードが厳密なコードである場合、ThisBindingをthisArgに設定します。
  2. そうでない場合、thisArgがnullまたは未定義の場合は、ThisBindingをグローバルオブジェクトに設定します。
  3. そうでない場合、Type(thisArg)がObjectでない場合は、ThisBindingをToObject(thisArg)に設定します。

02関数宣言の戻り値です。undefinedここで自明でなければなりません。

03変数fivedis、プリミティブ値のコンテキストで呼び出されたときの戻り値で初期化されます5disは厳密モードではないため、この行はを呼び出すのと同じですfive = Object(5)

04奇数のNumber {[[PrimitiveValue]]: 5}戻り値は、プリミティブ値をラップするオブジェクトの表現です5

05fiveオブジェクトのwtfプロパティは、文字列の値が割り当てられます'potato'

06 割り当ての戻り値であり、自明である必要があります。

07fiveオブジェクトのwtfプロパティが検討されています

08five.wtf以前に設定した'potato'ことを返す'potato'ここ

09fiveオブジェクトは、プリミティブ値で乗算されています5。これは、乗算される他のオブジェクトと同じで、ES5仕様のセクション11.5で説明されています。特に注目すべきは、オブジェクトが数値にキャストされる方法です。これは、いくつかのセクションで説明されています。

9.3 ToNumber

  1. primValueをToPrimitive(入力引数、ヒント番号)にします。
  2. ToNumber(primValue)を返します。

9.1 ToPrimitive

オブジェクトのデフォルト値を返します。オブジェクトのデフォルト値を取得するには、オブジェクトの[[DefaultValue]]内部メソッドを呼び出し、オプションのヒントPreferredTypeを渡します。[[DefaultValue]]内部メソッドの動作は、8.12.8のすべてのネイティブECMAScriptオブジェクトのこの仕様で定義されています。

8.12.8 [[DefaultValue]]

valueOfを、引数「valueOf」でオブジェクトOの[[Get]]内部メソッドを呼び出した結果とします。

  1. IsCallable(valueOf)がtrueの場合、

    1. valをvalueOfの[[Call]]内部メソッドを呼び出した結果とし、Oをこの値として、空の引数リストを指定します。
    2. valがプリミティブ値の場合、valを返します。

これは、オブジェクトのvalueOf関数が呼び出され、その関数からの戻り値が方程式で使用されるという言い回しです。valueOf関数を変更する場合は、操作の結果を変更できます。

> five.valueOf = function () { return 10 }
undefined
> five * 5
50

10機能が変化しなかった、それは、ラップされたプリミティブ値を返すように評価するのに結果fivevalueOf5five * 55 * 525

11fiveオブジェクトのwtfプロパティが評価され、再びそれが上の割り当てられていたときから変わらずあったことにもかかわらず05

12 'potato'

13Postfixのインクリメント演算子は上と呼ばれるfive数値を取得した、( 5、私たちはどのように以前のカバー)、値を格納それを返すことができるように、追加されます1(値6)、に値を代入するfiveと、保存された値を(返します5

14 前と同じように、戻り値はインクリメントされる前の値です

15変数に格納されてwtfいるプリミティブ値(6)のプロパティにfiveアクセスします。ES5仕様のセクション15.7.5がこの動作を定義しています。数値はからプロパティを取得しますNumber.prototype

16 Number.prototypewtfプロパティがないためundefined、返されます

17 five.wtfの値が割り当てられます'potato?'割り当ては、ES5仕様の11.13.1で定義されています。基本的に、割り当てられた値は返されますが、保存されません。

18 'potato?' 代入演算子によって返されました

19再びfive6アクセスされる値を持ち、再びプロパティをNumber.prototype持たないwtf

20 undefined 上記で説明したように

21 five アクセスされる

22 6 で説明されているように返されます 13


17

とても簡単です。

function dis () { return this; }

これはthisコンテキストを返します。したがって、その場合はcall(5)、数値をオブジェクトとして渡します。

call関数は、引数を指定しない、あなたが与える最初の引数は、のコンテキストですthis。あなたはそれが文脈上だ上でそれをしたい場合は通常、あなたはそれを与える{}ので、dis.call({})その手段、this空である関数内this。ただし、渡す5とオブジェクトに変換されるようです。.callを参照してください

だからリターンは object

あなたが行うとfive * 5、JavaScriptはオブジェクトを見ているfiveプリミティブ型として、これに相当します5 * 5。興味深いことに、そうです'5' * 5、それでもと等しい25ので、JavaScriptは内部で明らかにキャストされています。この行では、基になるfiveタイプへの変更は行われません。

ただし、これを行う++と、オブジェクトがプリミティブnumber型に変換され、.wtfプロパティが削除されます。基礎となる型に影響を与えているため


戻り値はNumberです。
GSerg

++変換して割り当て直します。++はに等しいvariable = variable + 1。ゆるいwtf
Rajesh 2016

*++すべてで私を混同していません。引数を期待せずにを返すthis関数を持つこと、その関数がとにかく引数を受け入れて、正確ではないものの引数を返すこと-それは私には意味がありません。
Nathan Long

@NathanLongを更新して、何をするかを示しましたdis.call()
Callum Linington

5 ++はC言語と同じように動作するため、驚くべきことは何もありません。thisオブジェクトへの単なるポインタなので、プリミティブ型は暗黙的に変換されます。引数のない関数が少なくともコンテキストを持たないのはなぜですか?callまたはの最初の引数はbind、コンテキストを設定するために使用されます。また、機能は、彼らがより単なるへのアクセス権を持っている手段で閉鎖されているarguments
Rivenfall

10

プリミティブ値はプロパティを持つことができません。しかし、プリミティブ値のプロパティにアクセスしようとすると、一時的なNumberオブジェクトに透過的にトランスタイプします。

そう:

> function dis() { return this }
undefined
// Like five.dis(), so dis return the temporaty Number object and 
// reference it in five
> five = dis.call(5)
Number {[[PrimitiveValue]]: 5}

// Write the wtf attribut on the Number object referenced by five
> five.wtf = 'potato'
"potato"
// Read the wtf attribut on the Number object referenced by five
> five.wtf
"potato"

// Return 5*5 but dont change the reference of five
> five * 5
25
// Read the same wtf attribut on the Number object referenced by five
> five.wtf
"potato"

// Change the five reference to a new primitive value (5+1). Five
// reference a primitive now.
> five++
5

// Read the wtf attribut on a new temporary Number object construct from
// the primitive referenced by five. So wtf does not exist.
> five.wtf
undefined

// Write the wtf attribut on a new temporary Number object construct from
// the primitive referenced by five. But this object not referenced by
// five. It will be lost.
> five.wtf = 'potato?'
"potato?"

// Read the wtf attribut on a new temporary Number object construct from
// the primitive referenced by five. So wtf does not exist.
> five.wtf
undefined
> five
6

8

関数を宣言しますdis。関数はそのコンテキストを返します

function dis() { return this }
undefined

disコンテキストを指定して呼び出します5。厳密モード(MDN)でコンテキストとして渡されると、プリミティブ値はボックス化されます。これfiveがオブジェクトです(ボックス化された番号)。

five = dis.call(5)
Number {[[PrimitiveValue]]: 5}

変数のwtfプロパティを宣言するfive

five.wtf = 'potato'
"potato"

の価値 five.wtf

five.wtf
"potato"

fiveはボックス化されている5ため、同時に番号とオブジェクトです(5 * 5 = 25)。変わりませんfive

five * 5
25

の価値 five.wtf

five.wtf
"potato"

fiveここで開梱します。five今は原始的numberです。印刷5してからに追加1fiveます。

five++
5

five6現在、プリミティブな数値です。その中にプロパティはありません。

five.wtf
undefined

プリミティブはプロパティを持つことができません、これを設定することはできません

five.wtf = 'potato?'
"potato?"

設定されていなかったため、これを読むことができません

five.wtf
undefined

fiveされる6ため、ポストの上でインクリメント

five
6

7

まず、これはnodejsコンソールから実行されているようです。

1。

    function dis() { return this }

は関数dis()を作成しますが、それがとして設定されてvarいないため、返される値がなかったためundefineddis()定義されていても出力がありました。余談ですthisが、関数が実行されなかったため、は返されませんでした。

2。

    five = dis.call(5)

Number関数dis()this値をプリミティブ5に設定するだけなので、これはJavaScriptのオブジェクトを返します。

3。

   five.wtf = 'potato'

"potato"プロパティwtffiveに設定しただけなので、最初のコードが返されます'potato'。JavaScriptは設定した変数の値を返すため、複数の変数を簡単に連結して、次のように同じ値に設定することができますa = b = c = 2

4。

    five * 5

これは25、プリミティブ数5をに掛けただけなので戻りますfive。の値はfiveNumberオブジェクトの値によって決定されました。

5。

    five.wtf

ここで繰り返すので、前にこの行をスキップしました。wtf上で設定したプロパティの値を返すだけです。

6。

    five++

@Callumが言ったように、オブジェクトから同じ値に++型を変換numberしますNumber {[[PrimitiveValue]]: 5}}

今のでfiveされnumberますが、このような何かをするまで、あなたはもうそれにプロパティを設定することはできません。

    five = dis.call(five)
    five.wtf = "potato?"

または

    five = { value: 6, wtf: "potato?" }

また、2番目の方法は最初Numberに作成したオブジェクトの代わりに汎用オブジェクトを定義しているため、最初の方法を使用する場合とは動作が異なります。

これがお役に立てば幸いです。JavaScriptは何かを前提にしているので、Numberオブジェクトからプリミティブに変更するときに混乱する可能性がありますnumbertypeofキーワードを使用して何かのタイプをチェックできます。初期化後にtypeof 5を書き込んでから、戻り'object'後にをfive++返します'number'

@decezeは、Numberオブジェクトとプリミティブ番号の違いを非常によく説明しています。


6

JavaScriptスコープは、実行コンテキストで構成されています。各実行コンテキストには、字句環境(外部/グローバルスコープ値)、変数環境(ローカルスコープ値)、およびthisバインディングがあります。ます。

このバインディング実行コンテキストの非常に重要な部分です。を使用callすることは、thisバインディングを変更する1つの方法であり、これを行うと、バインディングを生成するオブジェクトが自動的に作成されます。

Function.prototype.call()(MDNから)

構文
fun.call(thisArg[, arg1[, arg2[, ...]]])

thisArg
funの呼び出しに提供されるthisの値。これは、メソッドによって表示される実際の値ではない可能性があることに注意してください。メソッドが非厳密モードコードの関数である場合、nullおよびundefinedはグローバルオブジェクトに置き換えられ、プリミティブ値はobjectsに変換されます。(強調鉱山)

5がに変換されていることが明らかになればnew Number(5)、残りはかなり明白になります。それらがプリミティブ値である限り、他の例も機能することに注意してください。

function primitiveToObject(prim){
  return dis.call(prim);
}
function dis(){ return this; }

//existing example
console.log(primitiveToObject(5));

//Infinity
console.log(primitiveToObject(1/0));

//bool
console.log(primitiveToObject(1>0));

//string
console.log(primitiveToObject("hello world"));
<img src="http://i.stack.imgur.com/MUyRV.png" />

ここに画像の説明を入力してください


4

何が起こるかを説明するいくつかの概念

5 数値、プリミティブ値です

Number {[[PrimitiveValue]]: 5} Numberのインスタンスです(オブジェクトラッパーと呼びましょう)。

プリミティブ値のプロパティ/メソッドにアクセスするたびに、JSエンジンは適切なタイプ(Numberfor 5Stringfor 'str'およびBooleanfor true)のオブジェクトラッパーを作成し、そのオブジェクトラッパーのプロパティアクセス/メソッド呼び出しを解決します。これはtrue.toString()、たとえば、あなたが行うときに起こることです。

オブジェクトに対して操作を実行すると、それらの操作を解決するために、(toStringまたはを使用してvalueOf)プリミティブ値に変換されます。

var obj = { a : 1 };
var string = 'mystr' + obj;
var number = 3 + obj;

string文字列の連結を保持するmystrobj.toString()してnumberの追加を開催します3obj.valueOf()

すべてをまとめる

five = dis.call(5)

dis.call(5)実際にメソッドがある(5).dis()かのように動作します。メソッド呼び出しを解決するために、オブジェクトラッパーが作成され、メソッド呼び出しが解決されます。この時点で、5はプリミティブ値5のオブジェクトラッパーを指しています。5dis

five.wtf = 'potato'

オブジェクトにプロパティを設定します。ここでは特別なことはしません。

five * 5

これは、実際にfive.valueOf() * 5はオブジェクトラッパーからプリミティブ値を取得しています。fiveまだ初期オブジェクトを指しています。

five++

これは実際five = five.valueOf() + 1です。この行の前fiveは値5のオブジェクトラッパーを保持していますが、この後fiveはプリミティブ値を保持しています6

five.wtf
five.wtf = 'potato?'
five.wtf

fiveオブジェクトではなくなりました。これらの各行は、.wtfプロパティアクセスを解決するために、Numberの新しいインスタンスを作成します。インスタンスは独立しているため、あるプロパティを設定しても、別のプロパティには表示されません。コードはこれと完全に同等です:

(new Number(6)).wtf;
(new Number(6)).wtf = 'potato?';
(new Number(6)).wtf;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.