あなたがまだ知らない JavaScript の 10 の困難を心配しないでください。この記事では、誰でも簡単に解決できるように 1 つずつリストしていますので、興味のある友人は参照してください。
このブログを読むことができる JavaScript 開発者。 、頑張ってください それほど悪くはありません...
1. 関数をすぐに実行します
関数をすぐに実行します。つまり、即時呼び出し関数式 (IIFE) は、その名前が示すように、関数が作成されるとすぐに実行します。これはイベントをバインドせず、非同期操作を待つ必要もありません。
(function() { // 代码 // ... })();
function(){…} は匿名関数であり、それを囲む一組のかっこで式に変換され、その後に一組のかっこが続きます。関数が呼び出されます。関数をただちに実行することは、匿名関数をただちに呼び出すこととしても理解できます。関数をすぐに実行する最も一般的なアプリケーション シナリオは、名前の競合を避けるために、var 変数のスコープを関数に制限することです。
2. クロージャ
クロージャの場合、外側の関数が戻った後も、内側の関数は外側の関数の変数にアクセスできます。
function f1() { var N = 0; // N是f1函数的局部变量 function f2() // f2是f1函数的内部函数,是闭包 { N += 1; // 内部函数f2中使用了外部函数f1中的变量N console.log(N); } return f2; } var result = f1(); result(); // 输出1 result(); // 输出2 result(); // 输出3
このコードでは、外部関数 f1 が 1 回だけ実行され、変数 N が 0 に設定され、内部関数 f2 が変数 result に代入されています。外部関数 f1 は実行されているため、その内部変数 N はメモリ内でクリアされるはずですが、そうではありません。result を呼び出すたびに、変数 N がメモリ内に存在し、蓄積されていることがわかります。なぜ?これがクロージャの魔法です。
3. クロージャを使用してプライベート変数を定義します
通常、JavaScript 開発者はプライベート変数の接頭辞としてアンダースコアを使用します。しかし実際には、これらの変数は引き続きアクセスして変更することができ、真のプライベート変数ではありません。現時点では、クロージャを使用して実際のプライベート変数を定義できます。
function Product() { var name; this.setName = function(value) { name = value; }; this.getName = function() { return name; }; } var p = new Product(); p.setName("Fundebug"); console.log(p.name); // 输出undefined console.log(p.getName()); // 输出Fundebug
コードでは、オブジェクト p の name 属性はプライベート属性であり、p.name を使用して直接アクセスすることはできません。
4. プロトタイプ
各 JavaScript コンストラクターには、すべてのインスタンス オブジェクトが共有する必要があるプロパティとメソッドを設定するために使用されるプロトタイプ属性があります。プロトタイプのプロパティを列挙できません。 JavaScript は、prototype 属性を介したプロパティとメソッドの継承のみをサポートします。
function Rectangle(x, y) { this._length = x; this._breadth = y; } Rectangle.prototype.getDimensions = function() { return { length: this._length, breadth: this._breadth }; }; var x = new Rectangle(3, 4); var y = new Rectangle(4, 3); console.log(x.getDimensions()); // { length: 3, breadth: 4 } console.log(y.getDimensions()); // { length: 4, breadth: 3 }
このコードでは、x と y は両方ともコンストラクター Rectangle によって作成されたオブジェクト インスタンスであり、プロトタイプを通じて getDimensions メソッドを継承します。
5. モジュール性
JavaScript は、少なくとも ES6 がリリースされるまではモジュール型プログラミング言語ではありません。ただし、複雑な Web アプリケーションの場合、モジュール式プログラミングが最も基本的な要件です。現時点では、jQuery や Fundebug などの多くの JS ライブラリがこの方法で実装されているのと同じように、即時実行関数を使用してモジュール性を実現できます。
var module = (function() { var N = 5; function print(x) { console.log("The result is: " + x); } function add(a) { var x = a + N; print(x); } return { description: "This is description", add: add }; })(); console.log(module.description); // 输出"this is description" module.add(5); // 输出“The result is: 10”
いわゆるモジュール化とは、必要に応じてモジュール内の属性やメソッドのアクセス可能性、つまりプライベートかパブリックかを制御することです。コードでは、 module は独立したモジュール、N はそのプライベート プロパティ、print はそのプライベート メソッド、description はそのパブリック プロパティ、および add はそのパブリック メソッドです。
6. 変数のホイスティング
JavaScript は、すべての変数と関数の宣言をスコープの前に移動します。これは、変数のホイスティング (ホイスティング) と呼ばれます。つまり、変数や関数をどこで宣言しても、インタープリタはそれらをスコープの先頭に移動します。したがって、最初に変数と関数を使用してから、それらを宣言することができます。
ただし、変数の宣言のみが昇格され、変数の代入は昇格されません。これを理解していないと、問題が発生することがあります:
console.log(y); // 输出undefined y = 2; // 初始化y
上記のコードは次のコードと同等です:
var y; // 声明y console.log(y); // 输出undefined y = 2; // 初始化y
バグを避けるために、開発者は各スコープの先頭で変数と関数を宣言する必要があります。
7. カリー化
カリー化、つまりカリー化により、関数をより柔軟にすることができます。一度に複数のパラメータを渡して呼び出すこともできますし、パラメータの一部だけを渡して呼び出して、残りのパラメータを処理する関数を返すこともできます。
var add = function(x) { return function(y) { return x + y; }; }; console.log(add(1)(1)); // 输出2 var add1 = add(1); console.log(add1(1)); // 输出2 var add10 = add(10); console.log(add10(1)); // 输出11
コードでは、パラメーターとして 2 つの 1 を一度に add(1)(1) に渡すことも、1 つのパラメーターを渡してから add1 関数と add10 関数を取得することもでき、非常に柔軟に使用できます。
8. apply、call、およびbindメソッド
JavaScript開発者は、apply、call、およびbindメソッドの違いを理解する必要があります。これらに共通しているのは、最初のパラメータが this であり、これは関数が実行時に依存するコンテキストです。
3 つの中で、call メソッドが最も単純です。これは、次の値を指定して関数を呼び出すことと同じです:
var user = { name: "Rahul Mhatre", whatIsYourName: function() { console.log(this.name); } }; user.whatIsYourName(); // 输出"Rahul Mhatre", var user2 = { name: "Neha Sampat" }; user.whatIsYourName.call(user2); // 输出"Neha Sampat"
apply メソッドは call メソッドに似ています。 2 つの唯一の違いは、apply メソッドでは配列を使用してパラメータを指定するのに対し、call メソッドでは各パラメータを個別に指定する必要があることです。
apply(thisArg, [argsArray]) call(thisArg, arg1, arg2, …) var user = { greet: "Hello!", greetUser: function(userName) { console.log(this.greet + " " + userName); } }; var greet1 = { greet: "Hola" }; user.greetUser.call(greet1, "Rahul"); // 输出"Hola Rahul" user.greetUser.apply(greet1, ["Rahul"]); // 输出"Hola Rahul"
bind メソッドを使用すると、この値を関数にバインドして返すことができます。新しい関数として:
var user = { greet: "Hello!", greetUser: function(userName) { console.log(this.greet + " " + userName); } }; var greetHola = user.greetUser.bind({greet: "Hola"}); var greetBonjour = user.greetUser.bind({greet: "Bonjour"}); greetHola("Rahul") // 输出"Hola Rahul" greetBonjour("Rahul") // 输出"Bonjour Rahul"
9. メモ化
メモ化は、計算結果をメモリにキャッシュすることで、時間のかかる計算を最適化するために使用されます。これにより、同じ入力値に対して、結果を読み取るだけで済みます。次回の思い出。
function memoizeFunction(func) { var cache = {}; return function() { var key = arguments[0]; if (cache[key]) { return cache[key]; } else { var val = func.apply(this, arguments); cache[key] = val; return val; } }; } var fibonacci = memoizeFunction(function(n) { return (n === 0 || n === 1) ? n : fibonacci(n - 1) + fibonacci(n - 2); }); console.log(fibonacci(100)); // 输出354224848179262000000 console.log(fibonacci(100)); // 输出354224848179262000000
コードでは、fibonacci(100) の 2 番目の計算は、結果をメモリから直接読み取るだけで済みます。
10. 関数のオーバーロード
所谓函数重载(method overloading),就是函数名称一样,但是输入输出不一样。或者说,允许某个函数有各种不同输入,根据不同的输入,返回不同的结果。凭直觉,函数重载可以通过if…else或者switch实现,这就不去管它了。jQuery之父John Resig提出了一个非常巧(bian)妙(tai)的方法,利用了闭包。
从效果上来说,people对象的find方法允许3种不同的输入: 0个参数时,返回所有人名;1个参数时,根据firstName查找人名并返回;2个参数时,根据完整的名称查找人名并返回。
难点在于,people.find只能绑定一个函数,那它为何可以处理3种不同的输入呢?它不可能同时绑定3个函数find0,find1与find2啊!这里的关键在于old属性。
由addMethod函数的调用顺序可知,people.find最终绑定的是find2函数。然而,在绑定find2时,old为find1;同理,绑定find1时,old为find0。3个函数find0,find1与find2就这样通过闭包链接起来了。
根据addMethod的逻辑,当f.length与arguments.length不匹配时,就会去调用old,直到匹配为止。
function addMethod(object, name, f) { var old = object[name]; object[name] = function() { // f.length为函数定义时的参数个数 // arguments.length为函数调用时的参数个数 if (f.length === arguments.length) { return f.apply(this, arguments); } else if (typeof old === "function") { return old.apply(this, arguments); } }; } // 不传参数时,返回所有name function find0() { return this.names; } // 传一个参数时,返回firstName匹配的name function find1(firstName) { var result = []; for (var i = 0; i < this.names.length; i++) { if (this.names[i].indexOf(firstName) === 0) { result.push(this.names[i]); } } return result; } // 传两个参数时,返回firstName和lastName都匹配的name function find2(firstName, lastName) { var result = []; for (var i = 0; i < this.names.length; i++) { if (this.names[i] === (firstName + " " + lastName)) { result.push(this.names[i]); } } return result; } var people = { names: ["Dean Edwards", "Alex Russell", "Dean Tom"] }; addMethod(people, "find", find0); addMethod(people, "find", find1); addMethod(people, "find", find2); console.log(people.find()); // 输出["Dean Edwards", "Alex Russell", "Dean Tom"] console.log(people.find("Dean")); // 输出["Dean Edwards", "Dean Tom"] console.log(people.find("Dean", "Edwards")); // 输出["Dean Edwards"]
以上がJavaScriptをマスターしなければならない10の難関(必読)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。