一つの難しいJavaScript問題

5147 ワード

原文のリンク:https://segmentfault.com/a/1190000007979730
前回一つの問題を共有しました.反応がいいです.自分で書いたものを時間をかけて見に行きたいという人もいます.自分に大きな励ましを与えました.実は問題を作るのは本物のプログラミングとは比べものになりませんが、あなたの前に気づかなかった言語レベルの問題を発見することができます.ですから、今回はちょっと難しいJavaScriptのテーマを共有します.
function Foo() {
    getName = function () { 
        console.log('1');
    };
    return this;
}
Foo.getName = function () {
    console.log('2');
};
Foo.prototype.getName = function () { 
    console.log('3');
};
var getName = function () { 
    console.log('4');
};
function getName() { 
    console.log(5);
}

Foo.getName();  
getName();    
Foo().getName(); 
getName();  
new Foo.getName(); 
new Foo().getName();   
new new Foo().getName();        
上記のコードはブラウザの環境下で、出力結果はいくらですか?最終的な答えを発表します.
2 4 1 1 2 3 3
前の4つの難しさはそんなに大きくないです.主に後の3つの道です.基本的には全軍壊滅しました.感嘆は本当に回りくどいです.後はゆっくり分析して、一つずつ話してください.まず一つの問題に注意しなければならない.
function Foo() {
    getName = function () { 
        console.log('1');
    };
    return this;
}
関数内部宣言のgetName変数は、前にvarletconstを持っていないので、実はLHS(この紹介で行くことができる私のブログでLHSとRHSに関するまとめを見てください)によると、声明のgetNameは大域的な範囲内にある(windowでもあります).次に下記のコードがブラウザで実行された結果を知る必要がありますか?
var getName = function () { 
    console.log('4');
};
function getName() { 
    console.log(5);
}
getName();
上記コードの実行結果は、4です.理由は、varによって宣言された変数および関数宣言functionはいずれも向上されるが、関数宣言のレベルはvarよりも高いので、上のコードの実際の実行結果は以下の通りである.
function getName() { 
    console.log(5);
}
var getName = function () { 
    console.log('4');
};
getName();
後の関数式getNameは、前の関数ステートメントgetNameをカバーしています.実際には関数式が実行されています.まず、下記のコードに必要なコメントを追加します.
//    
function Foo() {
    //    
    getName = function () { 
        console.log('1');
    };
    return this;
}
//       getName,    Function,          ,Function    Object
Foo.getName = function () {
    console.log('2');
};
// Foo       getName
Foo.prototype.getName = function () { 
    console.log('3');
};
var getName = function () { 
    console.log('4');
};
function getName() { 
    console.log(5);
}
最初のステートメントを実行します.
Foo.getName();  
関数4自体は実行されていません.関数の属性Fooが実行されます.もちろん出力はgetNameです.次に実行されます.
getName();    
これは、大域的に2を実行したもので、2つの対応するgetName()の声明があり、前述のように、レベルアップされたレベルから実際に実行されるのは、関数式である.
var getName = function () { 
    console.log('4');
};
したがって、出力はgetNameです.次に実行します.
Foo().getName(); 
まず、JavaScriptのオペレータ優先度を見てください.高い順に並べ替えられていることから、4()と同じ優先度であることが分かります.したがって、.は左から右にかけて実行されます.まずFoo().getName()を実行し、大域のFoo()は出力getNameに覆われ、console.log('1')はこのときに表されるthisである.次に実行されるwindowに相当すると、出力の実際はwindow.getName()である.下に着きました
getName();  
これはもちろん、実行します.1です.次は三つの一番難しい部分に着きました.
new Foo.getName();
このステートメントの実行には、2つの可能性があります.
(new Foo).getName()
または
new (Foo.getName)()
しかし、実際には1よりも優先度が高いので、実際に実行されるのは第二の種類であることが分かります.
Foo.getName = function () {
    console.log('2');
};
関数は.動作を実行し、もちろん出力はnewです.以下は実行します
new Foo().getName();   
この文の可能性は2つあります.
(new Foo()).getName();
または
new (Foo().getName)();
そんなはずですか?もともとは第二種類の実行方式だと思っていましたが、後はブラウザで試運転して、実際に実行する方法は第一です.テーマを見た作者はこう説明しています.
まず、演算子優先かっこがnewより高いことを見ます.実際には(new Foo().getName()として実行されます.まずFoo関数を実行します.
上記の説明は問題があると思います.上記の2つの実施形態と比較して、1つ目はnewを実行してから2を実行してから実行します.第二に、newが先に実行され、.が実行され、最後に()オペレータが実行される.本当に引用によって優先度で判別するなら、第一ではなく第二の方法で実行すべきです.その後、ようやく原因を見つけました.以前に発生した多くのJavaScript優先度の表は完全ではなく、万能MDNが最も権威のあるJavaScript優先度表演算子優先度を与えています.一番重要な部分を挙げました.実行順は確かに第一です.()に従って実行すれば、状況は簡単であり、.は新たに生成されたオブジェクトを返し、newの方法がないので、new(new Foo()).getName();の方法が見つかった.したがって、出力は(new Foo())です.勝利は目の前にあります.最後の質問を見てみます.
new new Foo().getName();        
前のステップと同じ方法で、優先表に従ってこの文がどのように実行されているかを分析します.まず、パラメータ付きgetName()は、オペレータの優先度が最も高く、第1のステップは、次のように分割される.
new (new Foo().getName)();
二番目のステップは、次のステップに分けられます.
new ((new Foo()).getName)();
したがって、prototypeを実行するという関数は、対応するFoo.prototype.get Nameであるので、getName()を実行すると、必ず出力されるのは3である.ハハハ、せっかくの問題がやっと解決しました.楽しいです.まとめてください.まずJavaScript知識はMDNに行って調べたほうがいいです.もし他のところが間違っていたら、本当に大変です.第二に、コードを書く時には操作者優先度というものをあまり使わないと、明確でないところではすぐにnewを使うことができます.コードの読み取り可能性は本当に重要です.大事です大事ですコードはやはり人に見せます.正しくないところがあれば、指摘してください.経歴が浅いので、よろしくお願いします.私のブログを見に行きませんか?http://mrerhu.github.io