目錄
函數參數-可變參數,rest parameters剩餘參數:... 3
語法:
函數:
function 函數名(參數列表) {
函數體;
return 返回值;
}
例:
function add(x,y) { //沒有邏輯
return x+y;
}
console.log(add(4,5)); //函數調用
函數參數:
函數參數-普通參數:
一個參數佔一個位置,支持默認參數;
js中沒有py中的關鍵字傳參,即缺省值不用像py那樣往後放,建議默認參數寫到後面;
js中只是作位置參數的對應;
js不限制位置參數的位置;
注:C、C++、java都是這樣;
例:
const add = (x,y) => x+y;
const add1 = (x,y=6) => x+y;
const add2 = (x=8,y) => x+y; //js中缺省值不用像py那樣往後放(js中沒有py中的關鍵字傳參),建議像py那樣默認參數寫到後面;js只是作位置參數的對應;js不限制默認參數的位置;
console.log(add(4,5));
console.log(add(y=3,x=2));
console.log(add(aaa=1,bbb=2)); //V,相當於add(1,2),js中沒有關鍵字傳參,但是它的賦值表達式有值,aaa=1就是1
console.log(add()); //NaN,相當於add(undefined,undefined)
console.log(add1(4)); //相當於add(4,undefined)
console.log(add1(y=5,x=6));
console.log(add1()); //NaN,相當於add(undefined,6)
console.log(add2(2,2));
console.log(add(a1=5,(a2=6,a3=5))); //括號中表達式值爲5
輸出:
9
5
3
NaN
10
11
NaN
4
10
函數參數-可變參數,rest parameters剩餘參數:
用...表示可變參數,py中用*收集多個參數;
例:
const add = function (...args) {
result = 0;
for (let i in args) {
result += args[i];
}
return result;
}
const sum = (...args) => args;
console.log(add(3,6,9));
console.log(sum(2,4,6));
let arr = [1,2,3,4,5];
console.log(add(arr)); //X,這樣返回的是字符串
console.log(typeof(add(arr)));
console.log(add(...arr)); //V,將arr解構
輸出:
18
[ 2, 4, 6 ]
01,2,3,4,5
string
15
arguments對象:
函數的所有參數會被保存在arguments的鍵值對字典對象中,py中的dict對應js中的對象;
ES6之前,arguments是唯一可變參數的實現;
ES6開始,不推薦,建議使用可變參數,arguements只是爲兼容而保留;
例:
(function (p1, ...args) { //簡寫,函數定義+調用
console.log(p1);
console.log(args);
console.log(arguments);
for (let i of arguments)
console.log(i);
}) ('abc',1,3,5)
輸出:
abc
[ 1, 3, 5 ]
{ '0': 'abc', '1': 1, '2': 3, '3': 5 }
abc
1
3
5
例:
((x,...args) => { //簡寫,函數定義+調用
console.log(args);
console.log(x);
console.log(arguments);
}) (...[2,4,6,8,10])
輸出:
[ 4, 6, 8, 10 ]
2
{ '0': {},
'1':
{ [Function: require]
resolve: { [Function: resolve] paths: [Function: paths] },
main:
Module {
id: '.',
exports: {},
parent: null,
filename: 'e:\\git_practice\\js\\node_833b816d371f0.tmp',
loaded: false,
children: [],
paths: [Array] },
extensions: { '.js': [Function], '.json': [Function], '.node': [Function] },
cache: { 'e:\git_practice\js\node_833b816d371f0.tmp': [Object] } },
'2':
Module {
id: '.',
exports: {},
parent: null,
filename: 'e:\\git_practice\\js\\node_833b816d371f0.tmp',
loaded: false,
children: [],
paths:
[ 'e:\\git_practice\\js\\node_modules',
'e:\\git_practice\\node_modules',
'e:\\node_modules' ] },
'3': 'e:\\git_practice\\js\\node_833b816d371f0.tmp',
'4': 'e:\\git_practice\\js' }
參數解構:
和py類似,使用...來解構;
另,js的參數解構,不需要解構後的值的個數和參數個數對應;
例:
const add = (x,y) => {console.log(x,y); return x+y};
console.log(add(...[1,2]));
console.log(add(...[1,2,3,4,5]));
console.log(add(...[100]));
輸出:
1 2
3
1 2
3
100 undefined
NaN
函數返回值:
類C的語言,都有一個概念——表達式的值,類C語言都支持逗號表達式的值;
賦值表達式的值,是等號右邊的值;
逗號表達式的值,是最後一個表達式的值;
js的函數返回值即使寫的是多個,實際依然是單值;
另,py中,return 1,2,實質是返回一個tuple;
高級語言,基本都是多個入一個出;
例:
const add = (x,y) => {return x,y};
console.log(add(4,100));
輸出:
100
例:
const add = (x,y) => {return x,y};
res = add(4.0,50);
console.log(res,typeof(res));
b = (x=5,y=6,true);
console.log(b);
a = (123,true,z='test');
console.log(a);
function c() {
return x=5,y=6,true,'ok'
}
console.log(c());
輸出:
50 'number'
true
test
ok
例:
function a(obj) { //實際應用中,用來解決傳參問題
obj.x = 5;
return obj;
}
var o = {
x:100
}
console.log(a(o));
console.log(a(o).x);
輸出:
{ x: 5 }
5
作用域:
函數內定義的變量在函數外不可見;
var b = 200;,可提升聲明,也可突破非函數的塊作用域;
a = 100;,隱式聲明不能提升聲明,在嚴格模式下會報錯,但可把變量隱式聲明爲全局變量,建議少用;
let c = 300;,不能提升聲明,且不能突破任何塊作用域,推薦使用;
例,函數中變量的作用域:
function test() {
a = 100;
var b = 200;
let c = 300;
}
// console.log(a);
// console.log(b);
// console.log(c); //a,b,c均不可見
test();
console.log(a); //函數在調用後纔會把a放到全局中
例,塊作用域中變量的作用域:
"use strict"; //嚴格模式,此模式開啓後,a = 100;不被允許;此句要麼在文件首行,要麼在函數首行
if (1) {
// a = 100; //X
var b = 200;
let c = 300;
}
// console.log(a);
console.log(b);
// console.log(c); //不可見
例:
function show(i,arg) {
console.log(i,arg);
}
x = 500;
function fn() {
let z = 400;
{
var o = 100;
show(1,x);
t = 'free'; //嚴格模式下會報錯
let p = 200;
}
var y = 300;
show(2,z);
show(3,x);
show(4,o);
show(5,t);
// show(6,p); //let不能突破塊作用域
{
show(7,y);
show(8,o);
show(9,t);
{
show(10,o);
show(11,t);
show(12,z);
}
}
}
fn();
// show(13,y);
show(14,t); //global,不要這樣用
// show(15,o) //y,o函數外不可見
show(16,z); //var聲明提升,此時還沒有賦值
var z = 10;
const m = 2;
// m = 3; //常量不可以重新賦值
輸出:
1 500
2 400
3 500
4 100
5 'free'
7 300
8 100
9 'free'
10 100
11 'free'
12 400
14 'free'
16 undefined
函數表達式:
使用表達式來定義函數,表達式中的函數可以省略,如果這個函數名不省略,也只能用在此函數內部;
例,有名字的函數表達式:
const add = function _add(x,y) { //定義函數和常量時常用const,之後不可改;_add只能用在該函數內部
console.log(_add);
console.log(add);
return x+y;
}; //表達式後要有分號
console.log(add(4,5));
// console.log(_add(4,5)); //X
輸出:
[Function: _add]
[Function: _add]
9
例,有名字的函數表達式:
const add = function fn(x,y) { //fn只能用在該函數內部
return x-y;
};
console.log(add(4,5));
// console.log(fn(4,5)); //X
例,匿名函數:
const add = function(x,y) {
console.log(add);
return x+y;
};
console.log(add(4,5));
例,遞歸:
const sum = function _sum(n) {
let result = 0;
if (n==1) return 1;
return result += n + _sum(--n);
};
console.log(sum(5));
輸出:
15
函數、匿名函數、函數表達式的差異:
函數 VS 匿名函數,本質上都一樣,都是函數對象(變量指向一個函數對象),只不過函數有自己的標識符——函數名,匿名函數需要藉助其它的標識符而已;
(函數、匿名函數) VS 函數表達式,區別在於:函數會聲明提升(即調用可在聲明之前),函數表達式不會(即調用必須在聲明之後);
一般用,都是先聲明再調用;
例:
console.log(show); //V,函數會聲明提升
console.log(add); //X,函數表達式不會聲明提升,即,const add不會上移,必須先聲明再調用
const add = function(x,y) {
return x+y;
};
function show() {
console.log(add);
}
高階函數:
函數作爲參數,或返回一個函數;
例:
function a() {
console.log('a func');
return function b() {
console.log('b func');
};
}
let func = a(); //返回值指向b函數的定義
func();
輸出:
a func
b func
例:
function b() {
console.log('b func');
}
function a(fn) {
console.log('a func');
return fn;
}
let func = a(b);
func();
輸出:
a func
b func
例,計數器:
let counter = function() {
let i = 0;
// function inc() { //方式1
// return ++i;
// }
// return inc;
// return function() { //方式2,內部用不需要函數名
// return ++i;
// }
return () => ++i; //方式3,箭頭函數
}
const c = counter();
console.log(c());
console.log(c());
console.log(c());
輸出:
1
2
3
例,map函數:
function map(fn,arr) {
let newarr = [];
for (i in arr) {
newarr[i] = fn(arr[i]);
}
return newarr;
}
// let newarr = map(function (x) {return ++x}, [1,2,3,4,5]); //方式1
// let newarr = map((x) => {return ++x}, [1,2,3,4,5]); //方式2,若只留返回值(正確爲++x),必須把{}和return一起脫掉,若爲{++x}則沒有返回值,返回的是[ undefined, undefined, undefined, undefined, undefined ]
let newarr = map((x) => ++x, [1,2,3,4,5]); //方式3
console.log(newarr);
輸出:
[ 2, 3, 4, 5, 6 ]
例,map函數,生成器實現:
var map = function* (fn,arr) { //生成器函數
for (i in arr)
yield fn(arr[i]);
};
let newarr = map(x => ++x, [1,2,3,4,5]); //生成器對象
for (i of newarr) //迭代
console.log(i);
輸出:
2
3
4
5
6
箭頭函數:
是匿名函數,是一種更加精簡的格式;
let newarr = map(x => ++x, [1,2,3,4,5]); //++x爲return的值
箭頭函數參數:
如果一個函數沒有參數,使用();
如果只有一個參數,參數列表可省略小括號;
多個參數不能省略小括號,且使用逗號間隔;
箭頭函數返回值:
若函數體部分有多行,就要用{},如果有返回值使用return;
若只有一行語句,可同時省略{}和return;
若只一條return語句,不能省略大括號,即有return關鍵字必須要有{},如let newarr = map(x => {return ++x},[1,2,3,4,5]);;
若只一條非return語句,加上{}則無返回值;
因此,記住最簡形式x => x+2即可;
注:
windows.alert() //彈窗,browser對象,是全局對象,而js代碼中的全局對象是在nodejs上;彈窗測試時用,現都用遮罩層
browser新版本支持生成器、箭頭函數;
() => {},最簡單的箭頭函數,返回undefined;