[javascript] 이 코드에서 Number 객체가 속성을 유지하고 숫자를 증가 시키면 어떻게됩니까?

최근 트윗 에는이 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)A는 Number(A)의 일부 종류의 [[PrimitiveValue]]속성 만의 결과 five++와는 five * 5그냥 일반 번호로 표시 5하고 25(안 Number들)
  • 증가 five.wtf후에 속성이 사라지는 이유five++
  • 할당이 분명히 값을 설정 했음에도 불구하고 증가 five.wtf후에 속성을 더 이상 설정할 수없는 이유five++five.wtf = 'potato?'


답변

여기 OP 스택 오버플로에서 이것을 보는 것이 재미 있습니다 🙂

행동을 밟기 전에 몇 가지 사항을 분명히하는 것이 중요합니다.

  1. 숫자 값숫자 개체 ( a = 3vs a = new Number(3))는 매우 다릅니다. 하나는 프리미티브이고 다른 하나는 객체입니다. 프리미티브에는 속성을 할당 할 수 없지만 객체에는 속성을 할당 할 수 있습니다.

  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. 모든 Expression 에는 반환 값이 있습니다. 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를 객체로 설정 하면 Simple Assignment를 받아 들일 수 있습니다 .

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

으로 five대상으로, 나는 여전히 간단한 산술 연산을 수행 할 수 있도록. 할 수 있습니다. 그 속성은 여전히 ​​고착되어 있습니까? 예.

차례.

> five++
5
> five.wtf
undefined

이제 확인 five++합니다. 접미사가 증가 하는 트릭 은 전체 표현식이 원래 값에 대해 평가 한 다음 증가시키는 것입니다. 것 같습니다 five, 다음 설정 여전히 다섯이지만, 정말 표현이 다섯 평가 five6.

five설정 되었을뿐만 아니라 6다시 숫자 값으로 강제 변환되어 모든 속성이 손실됩니다. 프리미티브는 속성을 보유 할 수 없으므로 five.wtf정의되지 않습니다.

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

다시 속성 wtf을 에 할당하려고 합니다 five. 반환 값은 그것이 붙어 있음을 의미하지만 실제로는 fiveNumber 객체가 아니라 Number 값 이기 때문에 그렇지 않습니다 . 식은로 평가 'potato?'되지만 검사 할 때 할당되지 않은 것을 볼 수 있습니다.

명성.

> five
6

접미사가 증가한 이후 five6.


답변

숫자를 나타내는 두 가지 방법이 있습니다.

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

첫 번째는 기본 요소이고 두 번째는 개체입니다. 모든 의도와 목적에 대해 콘솔로 인쇄 될 때 다르게 보이는 것을 제외하고 모두 동일하게 작동합니다. 한 가지 중요한 차이점은 객체로서 new Number(5)plain과 같은 새로운 속성을 받아들이지 만 {}프리미티브 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 을 참조 하십시오.


답변

JavaScript 세계에는 강요가 있습니다-형사 이야기

나단, 당신은 당신이 무엇을 발견했는지 전혀 모른다.

나는 지금 몇 주 동안 이것을 조사해 왔습니다. 지난 10 월 폭풍이 치는 밤에 시작되었습니다. 실수로 Number수업을 우연히 발견했습니다. 왜 전 세계에 JavaScript가 Number수업을 받았습니까?

다음에 찾을 내용에 대해서는 준비하지 않았습니다.

말하지 않고 JavaScript는 숫자를 객체로, 객체를 코 바로 아래의 숫자로 변경 한 것으로 나타났습니다.

JavaScript는 아무도 붙잡기를 바라지 않았지만 사람들은 예상치 못한 이상한 행동을보고했으며 이제는 당신과 당신의 질문 덕분 에이 일을 크게 열어야한다는 증거가 있습니다.

이것이 우리가 지금까지 찾은 것입니다. 내가 당신에게 이것을 말해야하는지 모르겠습니다. JavaScript를 끄고 싶을 수도 있습니다.

> function dis() { return this }
undefined

이 기능을 만들었을 때 다음에 어떤 일이 일어날 지 전혀 몰랐을 것입니다. 모든 것이 잘 보였고 모든 것이 잘되었습니다.

오류 메시지가없고 콘솔 출력에서 ​​”정의되지 않음”이라는 단어 만 예상됩니다. 결국, 이것은 함수 선언이었습니다-아무것도 반환하지 않아야합니다.

그러나 이것은 시작에 불과했습니다. 다음에 일어난 일은 아무도 예측할 수 없었습니다.

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

그래, 당신은 알고 5있었지만, 그것은 당신이 얻은 것이 아닙니다. 다른 것이 있습니다.

나에게도 같은 일이 일어났다.

무엇을 만들어야할지 몰랐습니다. 그것은 나를 미치게했다. 나는 잠을 잘 수 없었고, 먹을 수 없었고, 그것을 마시려고했지만 산 이슬은 나를 잊지 않을 것입니다. 이해가되지 않았습니다!

그때 내가 실제로 무슨 일이 일어나고 있는지 알았을 때-그것은 강제력이었고, 그것은 내 눈앞에서 일어나고 있었지만 그것을 볼 수는 너무 멀었습니다.

모질라는 아무도 문서를 보지 않을 것이라는 것을 알고있는 곳에 두어 매장을 시도했다 .

몇 시간을 반복해서 읽고 다시 읽고 다시 읽은 후에 나는 이것을 발견했다.

“… 및 프리미티브 값이 객체로 변환됩니다.”

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 * 5, five숫자를 강요 받고해야하며, 그 수는 곱셈에 사용되는 하나 여야합니다. 그것은 five그 자체 의 가치가 아니라 콘솔에 인쇄되는 곱셈의 결과입니다 . five아무것도 할당되지 않습니다-물론 변경되지 않습니다.

그렇다면 어떻게 작업 five결과를 스스로 할당 할 수 있습니까? 알았어 five생각하기도 전에 “++”를 외쳤다.

> five++
5

아하! 나는 그를했다! 모두가 알고 있다는 5 + 1것은 6이것이 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(). 오 세상에 누!


답변

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가 널이거나 정의되지 않은 경우 ThisBinding을 글로벌 오브젝트로 설정하십시오.
  3. 그렇지 않으면 Type (thisArg)이 Object가 아닌 경우 ThisBinding을 ToObject (thisArg)로 설정하십시오.

02함수 선언의 반환 값입니다. undefined여기에 설명이 필요합니다.

03변수 fivedis프리미티브 값의 컨텍스트에서 호출 될 때 의 반환 값으로 초기화됩니다 5. 때문에 dis엄격 모드가 아닌,이 라인은 전화와 동일합니다 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 기본 :

Object의 기본값을 반환합니다. 객체의 기본값은 객체의 [[DefaultValue]] 내부 메소드를 호출하여 선택적 힌트 PreferredType을 전달하여 검색됩니다. [[DefaultValue]] 내부 메소드의 동작은 8.12.8의 모든 기본 ECMAScript 객체에 대해이 사양으로 정의됩니다 .

8.12.8 [[기본값]] :

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

10fivevalueOf기능은 불변, 상기 래핑 프리미티브 값을 반환 5되도록 five * 5평가합니다 행 5 * 5에있는 결과25

11five객체의 wtf속성은 평가 를 다시 가에 할당 된 때부터 불변에도 불구 05.

12 'potato'

13후위 증가 연산자 에 대한라고 five숫자 값을 얻을 수있는, ( 5, 추가, 값 때문에 반환 할 수 있다는 점, 우리는 어떻게 이전 포함) 1값 (으로 6의 가치 할당) five(반환 저장된 값을 5)

14 이전과 같이 반환 된 값은 증분되기 전의 값입니다.

15변수에 저장된 wtf프리미티브 값 ( 6) 의 속성에 five액세스합니다. ES5 사양의 섹션 15.7.5 는이 동작을 정의합니다. 숫자는에서 속성을 가져옵니다 Number.prototype.

16 Number.prototype이없는 wtf속성을, 그래서 undefined반환

17 five.wtf의 값이 할당됩니다 'potato?'. 할당은 ES5 사양의 11.13.1에 정의되어 있습니다. 기본적으로 할당 된 값이 반환되지만 저장되지는 ​​않습니다.

18 'potato?' 할당 연산자에 의해 반환되었습니다

19다시 five6이 액세스 Number.prototype되며 wtf속성 이 없습니다.

20 undefined 위에서 설명한대로

21 five 액세스

22 6 에 설명 된대로 반환 13


답변

꽤 간단합니다.

function dis () { return this; }

this컨텍스트를 반환합니다 . 따라서 그렇게 call(5)하면 숫자를 객체로 전달합니다.

call함수는 인수를 제공하지 않으며, 첫 번째 인수는의 컨텍스트입니다 this. 일반적으로 컨텍스트에서 원하는 경우 {}그렇게 합니다. 함수에서 비어 dis.call({})있음을 의미 this합니다 this. 그러나 전달 5하면 객체로 변환되는 것처럼 보입니다. 보다.call

그래서 반환은 object

그렇게 five * 5하면 JavaScript는 객체 five를 기본 유형으로 인식하므로와 같습니다 5 * 5. 흥미롭게도 do는 '5' * 5여전히 같으 25므로 JavaScript가 명확하게 캐스팅됩니다. 이 줄 에서 기본 five유형은 변경되지 않습니다.

그러나 그렇게 ++하면 객체가 기본 number유형으로 변환 되어 .wtf속성 이 제거 됩니다. 기본 유형에 영향을 미치기 때문에


답변

기본 값은 속성을 가질 수 없습니다. 그러나 기본 값의 속성에 액세스하려고하면 임시 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


답변

기능 선언 dis. 함수는 컨텍스트를 반환

function dis() { return this }
undefined

discontext로 전화하십시오 5. 엄격 모드 ( MDN ) 에서 컨텍스트로 전달되면 기본 값이 상자로 표시됩니다 . 자 five이제 객체 (박스 번호)입니다.

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

변수 wtf에 대한 속성 선언five

five.wtf = 'potato'
"potato"

가치 five.wtf

five.wtf
"potato"

fiveboxed 5이므로 숫자와 객체가 동시에 (5 * 5 = 25)입니다. 변경되지 않습니다 five.

five * 5
25

가치 five.wtf

five.wtf
"potato"

박스를 개봉 five합니다. five이제는 단지 원시적 number입니다. 인쇄 5한 다음에 추가 1합니다 five.

five++
5

five6이제 기본 숫자 이며 속성이 없습니다.

five.wtf
undefined

프리미티브는 속성을 가질 수 없습니다, 당신은 이것을 설정할 수 없습니다

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

설정되지 않았기 때문에 읽을 수 없습니다

five.wtf
undefined

five입니다 6때문에 게시물 위의 증가의

five
6