私が数年前に仕事を辞めたばかりのこの質問は、一連の質問です。フロントエンド面接で尋ねた質問 質問の最後の質問 は、面接官の JavaScript に関する総合的な能力を評価するために使用されます。残念ながら、これまでのところ、過去 2 年間で完全に正解できた人はほとんどいません。それは難しいということではなく、ほとんどの面接官が彼を過小評価しているからです。
質問は次のとおりです:
function Foo() { getName = function () { alert (1); }; return this; } Foo.getName = function () { alert (2);}; Foo.prototype.getName = function () { alert (3);}; var getName = function () { alert (4);}; function getName() { alert (5);} //请写出以下输出结果: Foo.getName(); getName(); Foo().getName(); getName(); new Foo.getName(); new Foo().getName(); new new Foo().getName();
答えは次のとおりです:
function Foo() { getName = function () { alert (1); }; return this; } Foo.getName = function () { alert (2);}; Foo.prototype.getName = function () { alert (3);}; var getName = function () { alert (4);}; function getName() { alert (5);} //答案: Foo.getName();//2 getName();//4 Foo().getName();//1 getName();//1 new Foo.getName();//2 new Foo().getName();//3 new new Foo().getName();//3
この質問は、私のこれまでの開発経験と、私が遭遇したさまざまな JS の落とし穴をまとめたものです。この質問には、変数定義のプロモーション、このポインターの指定、演算子の優先順位、プロトタイプ、継承、グローバル変数の汚染、オブジェクト属性とプロトタイプ属性の優先順位など、多くの知識ポイントが含まれます。
この質問には 7 つの質問が含まれています。以下で説明してください。
最初に Foo という関数を定義し、次に Foo の getName という静的プロパティを作成し、匿名関数を保存しました。 Foo。オブジェクトは getName という名前の新しい匿名関数を作成します。次に、関数変数式を通じて getName 関数が作成され、最後に getName 関数が宣言されます。
Foo.getName に関する最初の質問は、当然のことながら、Foo 関数に格納されている静的プロパティにアクセスすることです。これは当然 2 です。言うことはありません。
2 番目の質問は、getName 関数を直接呼び出します。直接呼び出しているため、上記の現在のスコープにある getName という関数にアクセスしているため、1 2 3 とは関係ありません。この質問に対して多くの面接官は5と答えました。ここには 2 つの落とし穴があります。1 つは変数宣言の昇格、もう 1 つは関数式です。
つまり、宣言されたすべての変数または宣言された関数は、現在の関数の先頭に昇格されます。
たとえば、次のコード:
console.log('x' in window);//true var x;
x = 0;
コードが実行されると、JS エンジンは宣言ステートメントをコードの先頭に上げて次のようになります:
var x; console.log('x' in window);//true x = 0;
var getName と function getName はどちらも宣言ステートメントです。違いは、var getName が関数式であるのに対し、function getName は関数宣言であることです。 JS でさまざまな関数を作成する方法の詳細については、記事「ほとんどの人が間違える古典的な JS クロージャ面接の質問」を参照してください。
関数式の最大の問題は、js がこのコードを 2 行のコードに分割し、別々に実行することです。
たとえば、次のコード:
console.log(x);//输出:function x(){} var x=1; function x(){}
実際に実行されるコードは、まず var x=1 を var x; と x = 1; の 2 行に分割し、次に var x と function x; を分離します。 (){ } 2 行を一番上に上げて、次のようになります。
var x; function x(){} console.log(x); x=1;
したがって、final 関数によって宣言された x は、変数によって宣言された x をカバーし、ログ出力は x 関数になります。
同様に、元の質問のコードの最終実行は次のとおりです:
function Foo() { getName = function () { alert (1); }; return this; } var getName;//只提升变量声明 function getName() { alert (5);}//提升函数声明,覆盖var的声明 Foo.getName = function () { alert (2);}; Foo.prototype.getName = function () { alert (3);}; getName = function () { alert (4);};//最终的赋值再次覆盖function getName声明 getName();//最终输出4
3 番目の質問 Foo().getName(); は、最初に Foo 関数を実行し、次に Foo を呼び出します。 function 値オブジェクトの getName プロパティ関数を返します。
Foo 関数 getName = function () {alert (1) }; の最初の文は関数代入ステートメントであるため、最初に現在の Foo 関数で getName 変数を探します。範囲はありません。次に、現在の関数スコープの上位層、つまり外側のスコープを調べて、getName 変数が含まれているかどうかを確認します。これは、2 番目の質問のalert(4) 関数です。 function(){alert(1) } への変数。
外側のスコープの getName 関数は、ここで実際に変更されます。
注: ここでまだ見つからない場合は、ウィンドウ オブジェクトまで検索します。ウィンドウ オブジェクトに getName 属性がない場合は、ウィンドウ オブジェクトに getName 変数を作成します。
その後、Foo関数の戻り値はこれになります。この問題についてはJSブログですでに多くの記事が書かれているので、ここでは詳しく説明しません。
簡単に言うと、thisのポインタは関数の呼び出し方法によって決まります。ここでの直接呼び出しメソッドでは、this は window オブジェクトを指します。
その後、Foo 関数は window.getName() を実行するのと同等の window オブジェクトを返し、ウィンドウ内の getName はalert(1) に変更されているため、最終的には 1 を出力します
2 つの知識ポイントが調べられますここで、1 つは変数のスコープの問題、もう 1 つは this のポインティングの問題です。
getName 関数を直接呼び出します。これは window.getName() に相当します。この変数は実行時に Foo 関数によって変更されているため、結果は 3 番目の質問と同じになります。 1
5つの質問 new Foo.getName(); ここではjsの演算子の優先順位の問題を調べます。
js演算子の優先度:
参考リンク:http://www.php.cn/
上記の表を参照すると、ドット(.)の優先度が新しい演算よりも高いことがわかります。これは、:
new (Foo.getName)();
と同等です。つまり、getName 関数が実際にはコンストラクターとして実行され、2 がポップアップします。
第六问 new Foo().getName() ,首先看运算符优先级括号高于new,实际执行为
(new Foo()).getName()
遂先执行Foo函数,而Foo此时作为构造函数却有返回值,所以这里需要说明下js中的构造函数返回值问题。
在传统语言中,构造函数不应该有返回值,实际执行的返回值就是此构造函数的实例化对象。
而在js中构造函数可以有返回值也可以没有。
1、没有返回值则按照其他语言一样返回实例化对象。
2、若有返回值则检查其返回值是否为引用类型。如果是非引用类型,如基本类型(string,number,boolean,null,undefined)则与无返回值相同,实际返回其实例化对象。
3、若返回值是引用类型,则实际返回值为这个引用类型。
原题中,返回的是this,而this在构造函数中本来就代表当前实例化对象,遂最终Foo函数返回实例化对象。
之后调用实例化对象的getName函数,因为在Foo构造函数中没有为实例化对象添加任何属性,遂到当前对象的原型对象(prototype)中寻找getName,找到了。
遂最终输出3。
第七问, new new Foo().getName(); 同样是运算符优先级问题。
最终实际执行为:
new ((new Foo()).getName)();
先初始化Foo的实例化对象,然后将其原型上的getName函数作为构造函数再次new。
遂最终结果为3
就答题情况而言,第一问100%都可以回答正确,第二问大概只有50%正确率,第三问能回答正确的就不多了,第四问再正确就非常非常少了。其实此题并没有太多刁钻匪夷所思的用法,都是一些可能会遇到的场景,而大多数人但凡有1年到2年的工作经验都应该完全正确才对。
只能说有一些人太急躁太轻视了,希望大家通过此文了解js一些特性。
并祝愿大家在新的一年找工作面试中胆大心细,发挥出最好的水平,找到一份理想的工作。
以上が多くのフロントエンド プログラマーが見落としがちな JavaScript の面接の質問の概要の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。