1. 機能紹介
関数は、特定の関数を実装するコードをカプセル化します。特定の関数を実装する必要がある場合、その関数を直接呼び出して実装できます。コードの再利用を実現するために毎回大量のコードを記述する必要はありません。
機能:
1. 関数をカプセル化し、コードの再利用を改善します。
2. オブジェクトの構築に使用されるテンプレート (コンストラクター)
関数は実際にはオブジェクトです。各関数は Function 型のインスタンスであり、他の参照型と同様のプロパティとメソッドを持っています。関数はオブジェクトであるため、関数名は実際には関数オブジェクトへのポインタであり、特定の関数には関連付けられません。関数バインディング。
2. 関数宣言
文法:
function 函数名(形参列表){
//函数体
}
関数式として記述することもできます。
var 函数名 = function(形参列表){
//函数体
}
例:
// 函数声明
function sum(a,b){
return a + b
}
//函数声明之后,需要调用才能执行
// 函数调用:函数名(实参)
console.log(sum(1,2)) //3
関数式として記述すると次のようになります。
// 函数声明
var sum = function(a,b){
return a + b
}
// 函数调用:函数名(实参)
console.log(sum(1,2)) //3
関数宣言のプロモーション
関数宣言は var で宣言された変数と似ており、宣言のプロモーションもあります。
sum(1,2) //在此处调用函数不会报错,因为存在函数声明提升
function sum(a,b){
return a + b
}
注: 関数式を使用して関数を宣言する場合、変数の割り当てには昇格がないため、関数宣言の前に関数呼び出しを記述することはできません。
3. 関数の内部プロパティ
関数の内部プロパティには関数内でのみアクセスできます
引数
argument は、関数に渡されるすべてのパラメータを含む配列のようなオブジェクトです。引数の主な目的は、関数のパラメータを保存することです。
function foo(){
console.log(arguments) // [Arguments] { '0': 1, '1': 2, '2': 3, '3': 4 }
console.log(arguments[1]) // 2
}
// 当传递的实参个数超过形参的个数的时候不会报错,所有的实参都会保存在arguments里
foo(1,2,3,4)
注: 引数は仮パラメータではなく引数に格納されます。
function foo(a,b = 2,c = 3){
console.log(arguments) // [Arguments] { '0': 1 }
console.log(b) //2
console.log(c) //3
}
//只传了一个实参,那么arguments中就只有一个值
foo(1)
呼び出し先属性
argument オブジェクトには callee と呼ばれるプロパティがあり、これは argument オブジェクトを所有する関数へのポインターです。
arguments.callee
これは実際には関数名です。
// 递归求n的阶乘
function factorial(n) {
if (n == 1) {
return 1
}
return arguments.callee(n - 1) * n //arguments.callee 相当于函数名factorial
}
console.log(factorial(10));
長さ属性
argument オブジェクトの length プロパティは、実際の引数の数を返します。
function foo(){
console.log(arguments.length) //5
}
foo(1,2,3,4,5)
4. このポインタについて
this: 実行環境コンテキスト オブジェクト。誰を指すかは、いつ呼び出されたか、誰によって呼び出されたかによって異なります。
- メソッドでは、これはメソッドが属するオブジェクトを表します。
- 単独で使用した場合、これはグローバル オブジェクトを表します。
- 関数では、これはグローバル オブジェクトを表します。
- イベントでは、これはイベントを受け取る要素を表します。
- 明示的な関数バインディングでは、このポイントを自分で決定できます。
1.メソッド内のこれ
オブジェクト メソッドでは、this は、そのメソッドが配置されているメソッドを呼び出すオブジェクトを指します。つまり、このメソッドを呼び出す人が誰でも、this を指します。
var person = {
name:'叶子yes',
sayName: function () {
console.log(this.name);
}
}
//person对象调用了该方法,因此this指向person
person.sayName() // '叶子yes'
var name = 'hello'
var person = {
name: '叶子yes',
sayName: function () {
console.log(this.name);
}
}
var a = person.sayName
//这里是全局对象调用了sayName方法,因此this指向全局对象window,结果为'hello'
//注意:在node环境下执行时,结果为undefined
a()
2.これだけで使う
これを単独で使用すると、グローバル オブジェクトを指します。
- ブラウザでは、ウィンドウはグローバル オブジェクトです。
- ノードでは、{} を指します。
ブラウザ環境の場合:
ノード環境の場合:
console.log(this) // {}
3. 関数内で使用する
関数では、関数の所有者はデフォルトでこれにバインドされます。
- ブラウザではウィンドウを指します
- ノードでは、グローバルオブジェクトを指します
ブラウザ環境の場合:
ノード環境の場合:
function foo(){
return this
}
console.log(foo())
出力結果:
4.イベント中のこれ
HTML イベント ハンドラーでは、これはイベントを受け取った HTML 要素を指します。
<button onclick="this.style.display='none'"> 点我后我就消失了 </button> //this指向button
5. 表示機能バインディング
JavaScript では関数もオブジェクトであり、オブジェクトにはメソッドがあり、apply と call は関数オブジェクトのメソッドです。これら 2 つのメソッドは非常に強力で、関数が実行されるコンテキスト、つまりこれにバインドされているオブジェクトを切り替えることができます。(これら 2 つの方法については後で説明します)
次の例では、person2 をパラメータとして使用して person1.sayName メソッドを呼び出すと、それがperson1 のメソッドであるにもかかわらず、person2 を指します。
var person1 = {
name: 'zhangsan',
sayName: function () {
console.log(this.name);
}
}
var person2 = {
name:'叶子yes'
}
//this指向person2
person1.sayName.call(person2) //叶子yes
5. 関数 IIFE をただちに実行する
IIFE: 即時に呼び出される関数式。即時に呼び出される関数式を意味します。つまり、関数が宣言されるとすぐに呼び出されます。
機能
1. 設定機能はページロード後に 1 回だけ実行されます。
2. 設定関数内の変数がグローバル変数に漏れないようにローカルスコープでラップします。
書き方:
(function(形参){
函数体内容
})(实参);
または:
(function(形参){
函数体内容
}(实参));
例:
(function (a) {
console.log(123 + a); //124
})(1)
(function (a) {
console.log(123 + a); //124
}(1))
通常の関数と即時実行関数を記述する場合は、それらをセミコロンで区切るよう注意する必要があります。そうしないと、js コンパイラーがそれらを 1 つのステートメントに解析し、エラーを報告します。
// 普通函数
function foo() {
console.log(123);
}
foo();
// 如果后面跟的是立即执行函数,前面的结束语句必须要加分号
// 立即执行函数
(function (a) {
console.log(123 + a);
})(123)
使用法:
1. 他の関数と同様に、すぐに実行される関数も値を返し、他の変数に割り当てることができます。
var sum = (function (a,b) {
return a + b;
}(1,2))
console.log(sum); //3
2.即時実行関数の前に追加できる!, + などを使用して、戻り値の型変換を実行します。
//1、返回布尔值
!function(形参){
return true
}(实参)
//2、返回数字类型的值
+function(形参){
return 123
}(实参)
//3、对于数字返回的是相反数,非数字返回NaN
-function(形参){
return 123
}(实参)
//4、对于数字返回的是相反数减1,非数字返回-1
~function(形参){
return 123
}(实参)
//5、返回undefined
void function(形参){
函数体内容
}(实参)
3. 面接での質問
//下面代码输出结果是什么
for (var i = 0; i < 6; i++) {
function output() {
console.log(i);
}
}
output() //输出结果是6,因为函数执行时,for循环已经结束,函数体中最终存的i值是6
0,1,2,3,4,5を出力したい場合はどうすればよいでしょうか?現時点では、即時実行関数を使用できます。
for (var i = 0; i < 6; i++) {
(function (j) {
console.log(j); //0,1,2,3,4,5
})(i)
}
/*
因为 JS 中调用函数传递参数都是值传递 ,所以当立即执行函数执行时,
首先会把参数 i 的值复制一份,然后再创建函数作用域来执行函数,
循环6次就会创建6个作用域,所以每个输出访问的都是不同作用域的 i 的值 。
*/
6. 範囲
ES5 の場合: (ES5 にはブロックレベルのスコープはありません)
関数スコープ: JavaScript 関数内で宣言された変数は、関数のローカル変数になります。
関数内で宣言された変数には関数外からアクセスできません。
グローバルスコープ: 関数の外で宣言された変数はグローバル変数になります。
関数の外で宣言された変数には関数内でアクセスできます。
関数を入れ子にすると、このとき内側の関数と外側の関数の変数がクロージャを形成します。
// 全局作用域定义的变量,函数作用域里是可以获取到的
var v1 = 10
v3 = 30
function foo() {
// 函数局部作用域里面定义的变量,外界是获取不到的
var v2 = 20
console.log(v1, v2, v3); //10 20 30
}
foo()
console.log(v2); //ReferenceError: v2 is not defined
スコープの最大の用途は変数を分離することであり、異なるスコープ内にある同じ名前の変数は競合しません。
スコープチェーン
まず、自由変数について理解しましょう。
次のコードでは、console.log(a)
変数 a を取得する必要がありますが、a は現在のスコープで定義されていません (b と比較してください)。現在のスコープには変数が定義されていないため、自由変数になります。自由変数の値を取得する方法 - 関数が作成された親スコープに移動する必要があります。そうでない場合は、常に上位の祖先要素を探します (これはいわゆる「静的スコープ」です。静的スコープとは関数のスコープ。これは関数の定義時に決定されます)
var a = 100
function fn() {
var b = 200
console.log(a) // 这里的a 就是一个自由变量 // 100
console.log(b)
}
fn()
スコープチェーン:
自由変数は親スコープまでのスコープに沿って検索されます。親が見つからない場合は、グローバル スコープが見つかるまでレイヤーごとに検索されますが、それでも見つからない場合は諦められます。このレイヤーごとの関係がスコープ チェーンです。
var a = 100
function F1() {
var b = 200
function F2() {
var c = 300
console.log(a) // 自由变量,顺作用域链向父作用域找 //100
console.log(b) // 自由变量,顺作用域链向父作用域找 //200
console.log(c) // 本作用域的变量 //300
}
F2()
}
F1()
7. 関数呼び出し
関数は宣言された後は直接実行されないため、実行する前に呼び出す必要があります。
関数の呼び出し方法は () 実行に限定されません。
関数の呼び出し方法:
- 関数名(実際のパラメータリスト);
- 関数名.call(実行環境オブジェクト、実パラメータリスト);
- 関数名.apply(実行環境オブジェクト、実パラメータリスト配列);
- 関数名.bind(実行環境オブジェクト)(実パラメータリスト);
以下に、call、apply、bind メソッドを紹介します。
1. call (実行環境オブジェクト、実パラメータリスト);
call メソッドは this のポイントを変更するために使用されます。最初のパラメータは this のポイントを示し、次のパラメータは渡される実際のパラメータを示します (ゼロ、1、または複数)。
var obj = {
name: '叶子yes',
sayName: function (a,b) {
console.log(this.name); // 叶子yes
console.log(a,b); // 1,2
}
}
var b = obj.sayName;
b.call(obj,1,2); // this 指向 obj
2. apply (実行環境オブジェクト、実パラメータリスト配列);
apply メソッドは基本的に call メソッドと同じで、こちらのポインタを変更するためにも使用されますが、apply は実パラメータを渡す際に配列を使用する点が異なります。
var obj = {
name: '叶子yes',
sayName: function (a,b) {
console.log(this.name); // 叶子yes
console.log(a,b); // 1,2
}
}
var b = obj.sayName;
b.apply(obj,[1,2]); // this 指向 obj,实参列表要写成数组的形式
注: call と apply の最初のパラメータが null の場合、これはノード環境のグローバル オブジェクトと HTML のウィンドウ オブジェクトを指します。
var obj = {
name: '叶子yes',
sayName: function (a,b) {
console.log(this); // window 或者 global
}
}
var b = obj.sayName;
b.apply(null);
3.バインド(実行環境オブジェクト)(実際のパラメータリスト);
これの要点を変更するためにもBindを使いますが、bind(执行环境对象)
まだ実行されていない関数が返されるので、関数を実行するには()を使う必要があります。
var obj = {
name: '叶子yes',
sayName: function (a,b) {
console.log(this.name); // 叶子yes
console.log(a,b); // 1,2
}
}
var b = obj.sayName;
var c = b.bind(obj) //返回函数,此时还没有执行,需要再使用()来执行
console.log(c) //[Function: bound sayName]
c(1,2) //执行函数
概要: call と apply はどちらもコンテキスト内でこれを変更し、関数をすぐに実行します。bind メソッドを使用すると、必要なときにいつでも対応する関数を呼び出すことができ、実行中にパラメーターを追加できます。これが両者の違いです。
8. コールバック関数
コールバックとは電話をかけ直すという意味です。main 関数は先に終了しており、渡された関数は後で呼び出されます。
コールバック関数の役割: コールバック関数は通常、時間のかかる操作に使用されます。メイン関数はコールバック関数の実行が完了するまで待つ必要がないため、独自のコードを実行し続けることができます。たとえば、ファイルの処理などの ajax リクエストです。
単純にコールバック関数をシミュレートしてみましょう。
//定义主函数,回调函数作为参数
function A(callback) {
callback();
console.log('我是主函数');
}
//定义回调函数
function B() {
// 模仿延时操作
setTimeout(() => {
console.log('我是回调函数');
}, 3000);
}
//调用主函数,将函数B传进去
A(B);
输出结果为:
我是主函数
我是回调函数
最初に「I am the main function」を出力し、次に 3 秒後に「I am the callback function」を出力します。
9. 締めくくり
1. クロージャとは何ですか?
簡単に言えば、クロージャとは、別の関数のスコープ内の変数にアクセスする権利を持つ関数を指します。
クロージャは特別な種類のオブジェクトです。これは、関数と関数が作成される環境の 2 つの部分で構成されます。環境は、クロージャの作成時にスコープ内にあったローカル変数で構成されます。
2. クロージャーの形成条件
クロージャの生成には次の 3 つの条件が必要です。
1. 関数の入れ子関数
2. 内部関数が外部関数のデータ (プロパティ、関数) を参照する
3. パラメータと変数は再利用されない
これにより、破壊されない関数空間が形成されます。
次の例のクロージャはクロージャです。
function func() {
var a = 1, b = 2;
function closure() {
return a + b;
}
return closure;
}
console.log(func()()); // 3
クロージャのスコープ チェーンには、それ自体のスコープに加えて、それを含む関数のスコープとグローバル スコープが含まれます。
Javascript 言語では、関数内のサブ関数のみがローカル変数を読み取ることができるため、クロージャは単純に「関数内で定義された関数」として理解できます。
したがって、本質的に、クロージャは関数の内部と関数の外部を接続する橋です。
3. クロージャの役割
クロージャーはさまざまな場所で使用できます。その最大の用途は 2 つあり、1 つは関数内の変数を読み取ること、もう 1 つはこれらの変数の値をメモリに保持することです。
function f1() {
var n = 999;
nAdd = function () {
n += 1
}
function f2() {
console.log(n);
}
return f2;
}
var result = f1();
result(); // 999
nAdd();
result(); // 1000
このコードでは、result は実際にはクロージャ f2 関数です。これは 2 回実行され、1 回目の値は 999、2 回目の値は 1000 でした。これは、関数 f1 のローカル変数 n が常にメモリに格納され、f1 が呼び出された後に自動的にクリアされないことを証明します。
なぜそうなるのでしょうか? その理由は、f1 が f2 の親関数であり、f2 がグローバル変数に割り当てられているため、f2 は常にメモリ内に存在し、f2 の存在は f1 に依存するため、f1 は常にメモリ内にあり、削除されないためです。呼び出しが完了すると、ガベージ コレクション メカニズム (ガベージ コレクション) によってリサイクルされます。
このコードのもう 1 つの注目すべき点は、「nAdd=function(){n+=1}」という行です。まず、var キーワードが nAdd の前に使用されていないため、nAdd はローカル変数ではなくグローバル変数です。次に、nAdd の値は匿名関数であり、匿名関数自体もクロージャであるため、nAdd は関数の外部で関数内のローカル変数を操作できるセッターと同等です。
4. クロージャ使用時の注意点
(1) クロージャは関数内の変数をメモリに格納し、大量のメモリを消費するため、クロージャを悪用することはできません。悪用すると、Web ページでパフォーマンスの問題が発生したり、IE でメモリ リークが発生したりする可能性があります。 IEの問題ですバグです。解決策は、関数を終了する前に、未使用のローカル変数をすべて削除することです。
(2) クロージャは、親関数内の変数の値を親関数の外で変更します。したがって、親関数をオブジェクトとして使用し、クロージャをそのパブリック メソッドとして使用し、内部変数をプライベート値として使用する場合は、親関数内の変数の値を自由に変更しないように注意する必要があります。複数の子関数のスコープは同時に親を指し、完全に共有されます。したがって、親の変数オブジェクトが変更されると、すべての子関数が影響を受けます。