ホームページ  >  記事  >  ウェブフロントエンド  >  多くのフロントエンド プログラマーが見落としがちな JavaScript の面接の質問の概要

多くのフロントエンド プログラマーが見落としがちな JavaScript の面接の質問の概要

黄舟
黄舟オリジナル
2017-03-09 14:38:092088ブラウズ

多くのフロントエンド プログラマーが無視しがちな JavaScript の面接の質問の要約:

前書き

私が数年前に仕事を辞めたばかりのこの質問は、一連の質問です。フロントエンド面接で尋ねた質問 質問の最後の質問 は、面接官の 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 番目の質問

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 番目の質問

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 のポインティングの問題です。

4 番目の質問

getName 関数を直接呼び出します。これは window.getName() に相当します。この変数は実行時に Foo 関数によって変更されているため、結果は 3 番目の質問と同じになります。 1

5番目の質問

5つの質問 new Foo.getName(); ここではjsの演算子の優先順位の問題を調べます。

js演算子の優先度:

多くのフロントエンド プログラマーが見落としがちな JavaScript の面接の質問の概要

参考リンク:http://www.php.cn/

上記の表を参照すると、ドット(.)の優先度が新しい演算よりも高いことがわかります。これは、:

new (Foo.getName)();

と同等です。つまり、getName 関数が実際にはコンストラクターとして実行され、2 がポップアップします。

第六问

第六问 new Foo().getName() ,首先看运算符优先级括号高于new,实际执行为

(new Foo()).getName()

遂先执行Foo函数,而Foo此时作为构造函数却有返回值,所以这里需要说明下js中的构造函数返回值问题。

构造函数的返回值

在传统语言中,构造函数不应该有返回值,实际执行的返回值就是此构造函数的实例化对象。

而在js中构造函数可以有返回值也可以没有。

1、没有返回值则按照其他语言一样返回实例化对象。

多くのフロントエンド プログラマーが見落としがちな JavaScript の面接の質問の概要1

2、若有返回值则检查其返回值是否为引用类型。如果是非引用类型,如基本类型(string,number,boolean,null,undefined)则与无返回值相同,实际返回其实例化对象。

多くのフロントエンド プログラマーが見落としがちな JavaScript の面接の質問の概要2

3、若返回值是引用类型,则实际返回值为这个引用类型。

多くのフロントエンド プログラマーが見落としがちな JavaScript の面接の質問の概要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 サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。