【フロントエンド面接】スコープとクローズド
8872 ワード
1.テーマ
変数アップの理解を教えてください.
thisのいくつかの異なる使用シーンを説明します.
aラベルを10個作成し、クリックした時に該当する番号を弾きます.
スコープの理解方法
実際の開発におけるクローズドアプリケーション
call apply bindを手動で実現
2.知識点
2.1実行コンテキスト
範囲:セグメントスクリプトまたは関数
大域:変数定義、関数宣言script
関数:変数定義、関数宣言、this、argments(実行前)
関数宣言と関数式の違い:
thisは実行時に確認できますが、定義時には確認できません.
コンストラクタ(構造のオブジェクトを指す)
一般関数(windowを指す)
call apply bind
一つの関数を変えるためのthis指向です.使い方はちょっと違っています.
コール:後ろのパラメータは呼び出し関数のパラメータリストです.
ここで疑問がありますが、なぜここでは5が出力されていませんか?私はmethodの中でthisを使って、objを指すべきではありませんか?少なくとも10も出力します.これはどのような騒ぎですか?
実は、この1がargments.lengthであり、本関数のパラメータの個数です.なぜここのthisはargmentsを指していますか?Javascriptでは、配列は数字を属性名にするだけの方法です.つまり、argments[0]()の意味は、argments.0()の意味と同じです.
出力するなら、5はどう書きますか?直接method:fnでいいです.
2.3スコープ
ブロックレベルのスコープがありません.
関数レベルのみのスコープ
2.4作用ドメインチェーン
自由変数の現在のスコープに定義されていない変数が自由変数です.
自由変数はその親レベルのスコープを探しに行きます.は、実行ではなく定義時の親レベルのスコープです.
一つの関数の中に他の関数を入れ子して、この関数をリセットして、このreturnから出てきた関数を変数に保存すると、閉じたパケットが作成されます.
クローズドの二つの使用シーン
1.関数を戻り値として使う
閉包の二つの役割:
他の関数の内部変数の関数を読み込むことができます.
関数内部の変数を常にメモリに保存することができます.
実際の応用シーン1:
クローズド・パケットは、グローバルに露出したくない変数のいくつかを「プライベート変数」としてカプセル化することができる.
積を計算する関数がある場合、mult関数はnumberタイプのパラメータをいくつか受信し、積結果を返します.関数の性能を向上させるために、キャッシュメカニズムを追加して、以前計算した結果をキャッシュして、次回同じパラメータに出会うと、直接に結果を返します.演算に参加する必要はありません.ここでは、キャッシュ結果を格納する変数は外部に露出する必要はなく、関数の運転が終了した後も保存する必要があるので、クローズドを採用することができます.
上のコード:
局所変数の継続寿命
対象者はしばしばデータを報告するために用いられ、以下の通りである.
データの損失の原因は、レポート関数における局所変数であり、レポート関数の呼び出しが終了すると、すぐに一部変数が破壊され、HTTP要求がまだ発行されていないかもしれないので、今回の要求は失われます.
今は変数をクローズドで閉じておけば、失われた問題を解決できます.
3.問題の解答
3.1変数アップの理解を話してください.
変数の定義と関数宣言
関数宣言と関数表現の違いに注意してください.
変数の定義はデフォルトで彼の変数宣言をアップグレードします.構造関数(指向構造のオブジェクト) オブジェクト属性における 一般関数(windowを指す) call apply bind 3.3 aラベルを10個作成し、クリックした時に該当する番号を弾きます.
実現方法1:letでiを宣言する.
他の関数の内部変数の関数を読み込むことができます.
関数内部の変数を常にメモリに保存することができます.
パッケージ変数
アプリケーション1
アプリケーション2
3.5手動でcall apply bindを実現する contextはオプションのパラメータです.伝えないなら、デフォルトのコンテキストはwindowです. contextはSymbol属性を作成し、呼び出し後に削除します.context に影響しません.
2.パラメータはターゲット関数の開始位置に挿入します.
変数アップの理解を教えてください.
thisのいくつかの異なる使用シーンを説明します.
aラベルを10個作成し、クリックした時に該当する番号を弾きます.
スコープの理解方法
実際の開発におけるクローズドアプリケーション
call apply bindを手動で実現
2.知識点
2.1実行コンテキスト
範囲:セグメントスクリプトまたは関数
大域:変数定義、関数宣言script
関数:変数定義、関数宣言、this、argments(実行前)
関数宣言と関数式の違い:
a(); // 。
var a = function(){}
b(); //
function b(){}
変数の定義はデフォルトで彼の変数宣言をアップグレードします.console.log(a);
var a = 0;
実際にはvar a;
console.log(a);
a = 0;
2.2 thisthisは実行時に確認できますが、定義時には確認できません.
var a = {
name:'a',
fn:function(){
console.log(this.name);
}
}
a.fn(); // a
a.fn.apply({name:'b'}); // b a.fn.call({name:'b'});
var fn1 = a.fn();
fn1(); // undefined
thisの使用シーンコンストラクタ(構造のオブジェクトを指す)
function Fun(name){
this.name = name;
}
var f = new Fun('a');
console.log(f.name);
オブジェクトの属性(オブジェクトを指す)一般関数(windowを指す)
call apply bind
一つの関数を変えるためのthis指向です.使い方はちょっと違っています.
コール:後ろのパラメータは呼び出し関数のパラメータリストです.
function greet(name) {
console.log(this.animal,name);
}
var obj = {
animal: 'cats'
};
greet.call(obj,' ');
apply:2番目のパラメータは呼び出し関数のパラメータ配列です.function greet(name) {
console.log(this.animal,name);
}
var obj = {
animal: 'cats'
};
greet.apply(obj,[' ']);
bind:バインディング関数が呼び出されると、bindから入ってきたパラメータはターゲット関数のパラメータリストの開始位置に挿入され、バインディング関数に渡すパラメータはそれらの後に付いてきます.var fun = function (name1,name2){
console.log(this);
console.log(name);
}.bind({a:1},"name1");
fun("name2");
argmentsの中のthis:var length = 10;
function fn(){
alert(this.length)
}
var obj = {
length: 5,
method: function(fn) {
arguments[0]()
}
}
Obj.method(fn)/出力1ここでは5も出力していません.10も出力していません.逆に1も出力しています.面白いです.ここでargmentsはjavascriptの内蔵オブジェクト(mdn:argments-JavaScriptを参照することができます)で、一つのクラスの配列です.つまり、ここでargments[0]はあなたのmethod関数の最初のパラメータです.fnです.だから、argments[0]()の意味はfn()です.ここで疑問がありますが、なぜここでは5が出力されていませんか?私はmethodの中でthisを使って、objを指すべきではありませんか?少なくとも10も出力します.これはどのような騒ぎですか?
実は、この1がargments.lengthであり、本関数のパラメータの個数です.なぜここのthisはargmentsを指していますか?Javascriptでは、配列は数字を属性名にするだけの方法です.つまり、argments[0]()の意味は、argments.0()の意味と同じです.
arguments = {
0: fn, // functon() {alert(this.length)}
1: , //
2: , //
...,
length: 1 //
}
だからここでalertが出てきた結果は1です.出力するなら、5はどう書きますか?直接method:fnでいいです.
2.3スコープ
ブロックレベルのスコープがありません.
if(true){
var name = "test"
}
console.log(name);
ブロック内で変数を宣言しないようにします.関数レベルのみのスコープ
2.4作用ドメインチェーン
自由変数の現在のスコープに定義されていない変数が自由変数です.
自由変数はその親レベルのスコープを探しに行きます.は、実行ではなく定義時の親レベルのスコープです.
var a = 100;
function f1(){
var b = 200;
function f2(){
var c = 300;
console.log(a); //
console.log(b); //
console.log(c);
}
f2();
};
f1();
2.5クローズド一つの関数の中に他の関数を入れ子して、この関数をリセットして、このreturnから出てきた関数を変数に保存すると、閉じたパケットが作成されます.
クローズドの二つの使用シーン
1.関数を戻り値として使う
function fun(){
var a = 0;
return function(){
console.log(a); // ,
}
}
var f1 = fun();
a = 1000;
f1();
2.関数をパラメータとして function fun(){
var a = 0;
return function(){
console.log(a); // ,
}
}
function fun2(f2){
a = 10000
f2();
}
var f1 = fun();
fun2(f1);
具体的な説明は高級-閉包中の説明を見ます.閉包の二つの役割:
他の関数の内部変数の関数を読み込むことができます.
関数内部の変数を常にメモリに保存することができます.
実際の応用シーン1:
クローズド・パケットは、グローバルに露出したくない変数のいくつかを「プライベート変数」としてカプセル化することができる.
積を計算する関数がある場合、mult関数はnumberタイプのパラメータをいくつか受信し、積結果を返します.関数の性能を向上させるために、キャッシュメカニズムを追加して、以前計算した結果をキャッシュして、次回同じパラメータに出会うと、直接に結果を返します.演算に参加する必要はありません.ここでは、キャッシュ結果を格納する変数は外部に露出する必要はなく、関数の運転が終了した後も保存する必要があるので、クローズドを採用することができます.
上のコード:
function calculate(param){
var cache = {};
return function(){
if(!cache.parame){
return cache.param;
}else{
// ....
//cache.param = result
//
}
}
}
実際の応用シーン2局所変数の継続寿命
対象者はしばしばデータを報告するために用いられ、以下の通りである.
var report = function( src ){
var img = new Image();
img.src = src;
};
report( 'http://xxx.com/getUserInfo' );
しかし、いくつかの低バージョンのブラウザの実装によってバグが存在し、これらのブラウザでレポート関数を使ってデータをアップロードすると30%ぐらいのデータが失われるということは、レポート関数は毎回HTTP要求を成功させることではないということです.データの損失の原因は、レポート関数における局所変数であり、レポート関数の呼び出しが終了すると、すぐに一部変数が破壊され、HTTP要求がまだ発行されていないかもしれないので、今回の要求は失われます.
今は変数をクローズドで閉じておけば、失われた問題を解決できます.
var report = (function(){
var imgs = [];
return function( src ){
var img = new Image();
imgs.push( img );
img.src = src;
}
})();
短所を包含する:資源を浪費する!3.問題の解答
3.1変数アップの理解を話してください.
変数の定義と関数宣言
関数宣言と関数表現の違いに注意してください.
変数の定義はデフォルトで彼の変数宣言をアップグレードします.
console.log(a);
var a = 0;
実際にはvar a;
console.log(a);
a = 0;
3.2 thisのいくつかの異なる使用シーンを説明する実現方法1:letでiを宣言する.
var body = document.body;
console.log(body);
for (let i = 0; i < 10; i++) {
let obj = document.createElement('i');
obj.innerHTML = i + '
';
body.appendChild(obj);
obj.addEventListener('click',function(){
alert(i);
})
}
実現方法2包装作用エリア var body = document.body;
console.log(body);
for (var i = 0; i < 10; i++) {
(function (i) {
var obj = document.createElement('i');
obj.innerHTML = i + '
';
body.appendChild(obj);
obj.addEventListener('click', function () {
alert(i);
})
})(i)
}
3.4実際の開発におけるクローズドの応用他の関数の内部変数の関数を読み込むことができます.
関数内部の変数を常にメモリに保存することができます.
パッケージ変数
アプリケーション1
var report = (function(){
var imgs = [];
return function( src ){
var img = new Image();
imgs.push( img );
img.src = src;
}
})();
変数の破壊を防ぐために使用します.アプリケーション2
function isFirstLoad() {
var arr = [];
return function (str) {
if (arr.indexOf(str) >= 0) {
console.log(false);
} else {
arr.push(str);
console.log(true);
}
}
}
var fun = isFirstLoad();
fun(10);
fun(10);
arrを関数の内部にカプセル化し、勝手に変更することを禁止し、変数の破壊を防止します.3.5手動でcall apply bindを実現する
Function.prototype.myCall = function (context) {
if (typeof this !== 'function') {
return undefined; // Function.prototype.myCall()
}
context = context || window;
const fn = Symbol();
context[fn] = this;
const args = [...arguments].slice(1);
const result = context[fn](...args);
delete context[fn];
return result;
}
appryは類似のcallを実現して、パラメーターは配列です. Function.prototype.myApply = function (context) {
if (typeof this !== 'function') {
return undefined; // Function.prototype.myCall()
}
context = context || window;
const fn = Symbol();
context[fn] = this;
let result;
if (arguments[1] instanceof Array) {
result = context[fn](...arguments[1]);
} else {
result = context[fn]();
}
delete context[fn];
return result;
}
1.コンストラクタコールかどうかを判断する2.パラメータはターゲット関数の開始位置に挿入します.
Function.prototype.myBind = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
const _this = this
const args = [...arguments].slice(1)
return function F() {
//
if (this instanceof F) {
return new _this(...args, ...arguments)
}
return _this.apply(context, args.concat(...arguments))
}
}