コードに規則的な繰り返しが発生した場合、関数を利用し、変数を定義し、メソッドを呼び出すことで、コードを繰り返し変更する必要がなくなり、関数の修正だけで済みます。基本的にすべての高級言語は関数をサポートしており、javascript も例外ではありません。変数のように使用でき、便利で強力です。したがって、この記事では js 関数を体系的に学び、学習過程で詳細なノートとサンプルを作成しました。
一、関数の定義と呼び出し#
1. 関数の定義#
function abs(x) {
if(x = 0) {
return x;
} else {
return -x;
}
}
function()
はこれは関数の定義であることを示します。abs
は関数の名前です。(x)
の中身は関数の引数です。{...}
の中身は関数本体で、いくつかの文を含むことができ、文がない場合もあります。
関数本体は、必ず
return
で終わる必要があり、結果を返すことができます。return
で終わらない場合は、undefined が返されます。
オブジェクトを直接定義することもでき、このオブジェクトは関数の形式で書くこともできます。
var abs = function (x) {
if (x>=0) {
return x
}else {
return -x
}
};
function(x)
は匿名関数であり、この関数は変数abs
に代入されているため、abs
を通じて直接呼び出すことができます。
この 2 つの関数定義の方法は完全に一致しますが、変数で定義する場合は、
;
で終わる必要があり、関数文の終了を示します。
2. 関数の呼び出し#
関数を呼び出すときは、引数を直接渡すだけです。
abs (10)、関数の定義に従って 10 を代入し、返される結果は x、すなわち 10 です。
3. 引数のチェック#
引数が自分が望む型であるかどうかを確認できます。
引数abs(x)
に非数値を渡すと、コンソールはthis is not number
と返します。数値を渡すと、条件判断が行われます。
function abs(x) {
// 引数xが数値かどうかをチェック
if (typeof x !== 'number') {
console.log('this is not number')
}else{
if (x >= 0) {
return x
}else {
return -x
}
}
}
4.arguments#
arguments
を利用することで、呼び出し元が渡したすべての引数を取得できます。
arguments
は渡された引数を表し、arguments.length
は渡された引数の長さを表します。
console.log(arguments.length)
// この行のコードは関数内に書かれており、コンソールに出力されます。
引数を出力するためのループを最初に書き、関数を書き終えた後に引数を渡すと、コンソールに渡された引数が印刷されます。
function str() {
var s
for(var i = 0; i<arguments.length; i++) {
// 渡された引数を返す
console.log(arguments[i]);
s += arguments[i] + ",";
}
return s;
};
// 引数を渡す
str("name", "age");
//コンソール出力:name, age
5.return#
true を返すと、リンクをクリックすると直接ジャンプし、false を返すと、a リンクのアドレスを無視し、window.location.href の後のアドレスにジャンプします。
<a href="https:www.baidu.com" onclick="return myfun()">baidu</a>
<input type="text" id="test" value="click">
<script>
function myfun() {
window.location.href = 'https://www.bilibili.com';
var test = document.getElementById('test').value;
console.log(test);
return false;
}
</script>
return に注意すべき点:関数は自動的に行末に
;
を追加するため、return を書くときは必ず注意し、単純に 2 行に分けないようにしてください。エラーが発生しやすいです。
return
{ name: 'foo' }
// 上記の書き方は問題があります。jsのメカニズムは自動的に次のようにレンダリングします。
return; //return undefined
{ name: 'foo' };
// 正しい書き方は次のようになります。
return {
name: 'foo'
};
6.rest#
渡された引数の余分な部分を配列の形式で保存し、追加の引数を取得するために i = 2 から始めて、既存の a,b を除外します。
function arr(a, b) {
var i, rest = [];
if (arguments.length > 2) {
for (i = 2; i<arguments.length; i++) {
rest.push(arguments[i]);
}
}
console.log('a = ' + a);
console.log('b = ' + b);
console.log(rest);
};
arr(1,2,3,4);
コンソール出力:
余分な部分がArray
に印刷されていることがわかります。
この書き方は少し面倒ですが、以下はより簡単な書き方です。
関数内で引数rest
を定義し、前に...
を付けることで、余分な引数を直接配列の形式で変数rest
に渡すことができ、arguments
を使わずにすべての引数を取得できます。
引数の数が定義された引数の数を超えていない場合、関数は空の配列を返します。
function foo(a, b, ...rest) {
console.log('a = ' + a);
console.log('b = ' + b);
console.log(rest)
}
foo(1,2,3,4)
// a = 1
// b = 2
// Array [3,4]
foo(1)
// a = 1
// b = undefined
// Array [ ]
7. 計算#
渡された引数の合計を求める
// forEachは配列内のすべての要素を返すことができます。
function sum(...rest) {
var sum = 0;
rest.forEach(function(x) {
sum += x;
});
return sum;
};
//sum(1,2)
//コンソール出力 3。合計成功
円の面積を計算する
// rは円の半径を表します。
// piは引数がない場合、デフォルトで3.14になります。
function area_of_circle(r, pi){
var area;
if(arguments.length == 1) {
// 渡された引数が1つだけの場合、3.14*rの平方を計算します。
area = 3.14*r*r;
}else{
area = pi*r*r;
}
return area;
}
二、変数とスコープ#
1. 変数の宣言#
js では通常var
を使用して変数を宣言しますが、宣言された変数には実際にスコープがあります。
- 関数体内で宣言された変数は、関数体内でのみ有効であり、関数体外では認識されません。
function fun() {
var a = 1;
};
a = a + 1; // err この行のコードは直接エラーになります。なぜなら、グローバルにはaという変数が存在しないからです。
-
もし 2 つの関数体でそれぞれ
変数a
を宣言した場合、互いに干渉せず、自分の関数体内では正常に作用し、関数体を出ると作用しなくなります。 -
js 関数はネストでき、内部関数は外部関数にアクセスでき、外部関数は内部関数にアクセスできません。
function par() {
var x = 1;
function son() {
var y = x + 1;
};
var z = x + y; // Error:
}
したがって、var z = x + y
はエラーになります。なぜなら、変数y
はson()
内にあり、関数外部から内部関数にアクセスできない
ため、y
にアクセスできず、var z = x + y
はエラーになります。
- 2 つのネストされた関数体には、それぞれ同名の変数があります。js 関数は変数を検索する際、まず自身から始め、もし自身にその変数があれば取得し、なければ内向外に、下から上へと検索します。
function par() {
var num = 1;
function son() {
var num = 2;
console.log("son() = " + num);
};
console.log("par() = " + num);
son();
};
par();
関数は呼び出された後にのみ有効です。
son()
とpar()
2. 変数の昇格#
JavaScript の関数定義には特徴があり、関数体の文をすべてスキャンし、すべての宣言された変数を関数の先頭に「昇格」させますが、代入は一緒に昇格しません。これにより、コードのエラーが発生しやすくなります。
したがって、この問題に対処するために、変数を宣言する際は、すべてを関数の開始位置にまとめて配置し、関数内部で最初にすべての変数を宣言する
という原則を厳守する必要があります。
3. グローバルスコープ#
どの関数内部にも定義されていない変数はグローバル変数と呼ばれ、window
の下にあります。これはグローバルスコープ
とも呼ばれ、グローバルスコープ内の変数は実際にはwindow
にバインドされています。
var course = 'learn js';
console.log(course); // learn js
console.log(window.course) // learn js
グローバル変数に直接アクセスするか、前にwindow
を付けると、結果は同じです。
全体の js ファイルには 1 つのグローバルスコープしかなく、それはwindow
です。もし特定の関数スコープ内で変数を検索し、見つからなければ、内から外へと一層ずつ検索し、最終的にグローバルスコープ内でも見つからなければ、ReferenceError
エラーが発生します。
4. ローカルスコープ#
関数内部はローカルスコープであり、このコードの名前は関数内部でのみ機能します。
for
ループなどの文の中では、ローカルスコープの変数を定義することはできません。
5. グローバル変数とローカル変数の違い#
-
グローバル変数:どこでも使用でき、グローバル変数はブラウザを閉じるまで消えず、メモリリソースを多く消費します。
-
ローカル変数:関数内部でのみ使用でき、そのコードブロックが実行されると初期化され、コードブロックが実行され終わると消えます。したがって、メモリスペースを節約します。
-
関数スコープ内で変数を操作する場合、まず自身のスコープ内で検索し、あれば直接使用し、なければ上位スコープを探します。もしグローバルスコープ内にもなければ、エラーが発生します。
6. 定数#
var
とlet
は変数を宣言しますが、ES6 以前は大文字の変数名を使用して定数を定義します。
// ES5
var NAME = 'xiaoming'
ES6 では新しいキーワードconst
を使用して定数を定義します。
// ES6
const name = 'xiaoming'
三、分割代入#
1. 配列の要素をそれぞれ異なる変数に代入できます。
var array = ['hello', 'javascript', 'ES6'];
var x = array[0];
var y = array[1];
var z = array[2];
// x = 'hello'
// y = 'javascript'
// z = 'ES6'
2. 配列自体にネストがある場合も、ネストの階層を注意して分割代入できます。
let [x, [y, z]] = ['hello', ['JavaScript', 'ES6']];
x; // 'hello'
y; // 'JavaScript'
z; // 'ES6'
3. 分割代入時に要素を無視できます。
let [, , z] = ['hello', 'JavaScript', 'ES6'];
z; // ES6
4. オブジェクトに対しても分割代入できます。
var person = {
name: 'xiaoming',
age: 22,
gender: 'male',
email: '[email protected]',
school: 'zyg'
}
// 3つの変数を定義し、それぞれ3つの属性に対応させます。
var {name, age, email} = person;
console.log(name, age, email);
コンソールに私たちが望む内容が印刷されます。
オブジェクトに対して分割代入を行う際にも、ネストが可能です。
5. 属性名を使って新しい変数名を再定義できます。
var person = {
name: '小明',
age: 20,
gender: 'male',
passport: 'G-12345678',
school: 'No.4 middle school'
};
// passport属性を変数idに代入します。
let {name, passport:id} = person;
console.log(name);
console.log(age);
console.log(id);
console.log(email);
コンソール出力結果:
name, age, id がすべて印刷され、email はエラーになります。なぜなら、email の内容は新しい変数id
に代入され、email
には何の内容もないため、エラーが発生します。
6. 存在しない属性が undefined を返さないように、デフォルト値 true を使用できます。
var person = {
name: '小明',
age: 20,
gender: 'male',
passport: 'G-12345678'
};
// personオブジェクトにsingle属性がない場合、デフォルトでtrueを代入します。
var {name, single=true} = person;
name; // '小明'
single; // true
注意が必要なのは、代入時に
{
で始めないことです。そうしないと、js がレンダリングに失敗します。
var person = {
name: '小明',
age: 20,
gender: 'male',
passport: 'G-12345678'
};
// 変数を宣言
var x;
var y;
// 分割代入
{x, y} = { name: '小明', x: 100, y: 200} // Error:
ここで{x, y} = person
はエラーになります。=
が不正です。したがって、正しい書き方は、代入文の外側に()
を包むことです。
({x, y} = { name: '小明', x: 100, y: 200});
7. 分割代入の使用シーン
2 つの変数の値を交換します。
var a = 1;
var b = 2;
[a, b] = [b, a]
四、オブジェクトのメソッド#
オブジェクトにバインドされた関数はメソッドと呼ばれます。
オブジェクト内に関数をバインドすることを、そのオブジェクトのメソッドと呼びます。
1.this#
以下のコードは(今年の年 - 生年)を返します。
var xm = {
name: 'xiaoming',
birth: 1998,
age: function() {
var year = new Date().getFullYear();
return year - this.birth
}
};
// オブジェクトxm内でメソッドage()を呼び出します。
xm.age(); // 22
ここで新しいキーワードthis
が導入されます。
メソッド内部で、this
は特別な変数であり、常に現在のオブジェクトを指します。つまり、xm
という変数です。
したがって、this.birth
は変数xmのbirth属性
を指します。
this
はメソッド内に存在し、メソッド内でオブジェクトの属性を呼び出すには、必ずthis
を使用する必要があります。
もしメソッドがオブジェクトの外部に書かれている場合、this
の指向の問題をよく分析する必要があります。例えば:
function getage() {
var year = new Date().getFullYear();
return year - this.birth;
}
var xiaoming = {
name: '小明',
birth: 1990,
age: getage
};
getage()
を単独で呼び出すと、getage()
はメソッドを指し、全体のスコープ内にあるため、この時のthis
はグローバルオブジェクトwindow
を指し、したがってNaN
が返されます。
xiaoming.age()
を呼び出すと、オブジェクトxiaoming
の下にあるメソッドgetage()
が呼び出されます。
したがって、
this
の指向を正しく保つためには、必ずobj.xxx()
の形式で呼び出す必要があります。
この方法を使用しないと、すべてエラーが発生します。'use strict'
モードの場合、this
は undefined を指します。
2.that#
もしオブジェクト内のメソッドにさらにイベントがネストされている場合、this
の指向が再び問題になります。最初のメソッドを指し、メソッドに対応するオブジェクトを指しません。
したがって、メソッドを書くときは、最初にvar that = this
を宣言し、このthat
がオブジェクト内の属性を指すようにし、次にメソッド内で属性を呼び出すときは、that.
を前に付けるだけで、オブジェクトの下の属性を直接指すことができます。
var xm = {
name: 'xiaoming',
birth: 1998,
age: function() {
var that = this;
function getbirthage() {
var y = new Date().getFullYear();
return y - that.birth;
}
return getbirthage();
}
};
// xm.age();
var that = this
を定義することで、メソッド内でthat.
を使用して属性を指すことができ、何層ネストされてもエラーが発生せず、オブジェクトの下の属性を直接指すことができます。
var that = this
を使用することで、メソッド内で他の関数を定義しても、オブジェクト属性を取得できない心配はありません。
ただし、注意すべき点は、各メソッドの終了後に結果を返す必要があることです。
return getbirthage()
3.apply#
var that = this
の他に、apply
属性を使用してthis
の指向を制御することもできます。
apply
は関数自体のメソッドであり、2 つの引数を持っています。
function getage() {
var y = new Date().getFullYear();
return y - this.birth;
}
var xm = {
name: 'xiaoming',
birth: 1998,
age: getage
}
// xm.age();
// getage.apply(xm, []);
書き方はgetage.apply(xm, [])
で、apply
の最初の引数はthisの指向
、すなわちオブジェクトを表し、2 番目の引数は関数自体の引数を表します。
4.apply () と call ()#
call()
はapply
と似たメソッドですが、違いは:
apply()
は引数をArray
にパッケージ化します。call()
は引数を順番に渡します。
math.max(1,2,3)
を呼び出すとき、2 つの方法を使用します。
math.max.apply(null, [1,2,3]); // 3
math.max.call(null, [1,2,3]); // 3
2 つの結果は同じで、通常の関数を呼び出すときは、this
を null にバインドします。