JS函数

浏览器内置了很多函数,比如alert(),全称是window.alert()

还有DOM(文档对象模型)的很多方法等等

自定义函数

1
2
3
4
function funName(p1,p2){
//fun body
return 1;
}

注意调用方式,如果将按钮和函数绑在一起,()是否书写效果是不一样的,()是函数调用运算符,匿名函数就相当与不写()的情况

1
2
button.onclick = funName();/*这代表不需要点击就调用*/
button.onclick = funName;/* 需要点击才调用*/

匿名函数

1
2
3
function (){
//funbody
}

匿名函数一般与事件联系在一起

1
2
3
4
5
var myButton = document.querySelector('button');

myButton.onclick = function() {
alert('hello');
}

名字空间

为了避免不同文件的变量和函数名重复,可以将所有变量和函数绑定在一个全局变量里,如

1
2
3
4
5
6
let GLOBAL = {};

GLOBAL.v1 = 1;
GLOBAL.fun = function(){
return 0;
}

高阶函数

高阶函数英文叫Higher-order function。那么什么是高阶函数?

JavaScript的函数其实都指向某个变量。既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。

map

可以快速的将数组的每个元素按照规则得到另一组数组

例如[1,2,3]得到[1,4,9],常规思想是取出元素,进行运算,赋值给其他数组

1
2
3
4
5
6
7
8
function mutiple(x){
return x*x;
}
let li1 = [1, 2, 3];
let li2 = [];
for(let i in li1){
li2.push(mutiple(li1[i]));
}

使用map

1
2
3
4
5
function mutiple(x){
return x*x;
}
let li1 = [1, 2, 3];
let li2 = li1.map(mutiple);

传入的mutiple就是函数本身,因为map将算法抽象了,所以很简洁,而且一眼就看出了新增了一个数组

第二个参数可选,代表索引

reduce

map做的是简单运算,reduce做的是重复运算,传入reduce的函数必须有两个参数,一个作为算子计算,一个接收结果,简单算法如下

1
[x1, x2, x3, x4].reduce(f) == f(f(f(x1, x2), x3), x4)

比如对一个数组求和

1
2
3
4
5
let arr = [1,2,3,4,5,6,7,8,9];
function sum (x,y){
return x+y;
}
arr.reduce(sum);//45

filter

可以过滤掉数组的元素,回调函数的第一个参数是元素(必选),第二个参数是元素索引,第三个参数是数组本身,回调函数应返回一个boolean值来决定是否过滤

如过滤掉一个数组重复的元素

1
2
3
4
5
6
var arr = [1,2,3,4,1,2,3,4];

arr.filter(function (element,index,self){
return self.indexOf(element) === index;
})
//因为indexOf返回第一个值的索引,所以后面的相同的数据就会被过滤

sort

可以对数组进行排序,如果数组里是字符串,会按照首字母的ASCII码进行排序,首字母相同会按照第二个字母的ASCII码排序,以此类推

1
2
let arr = ['a','A','Abc','abc']
arr.sort();//["A", "Abc", "a", "abc"]

如果是数组,sort默认的方法是将数字转化为字符串,然后使用ASCII码的方式进行排序,如

1
2
let arr = [12,22,2,15];
arr.sort();//[12, 15, 2, 22]

但因为sort是高阶函数,它的回调函数接收两个值a、b

  • 若a<b,则返回一个负值表示a排在b之前
  • 若a=b,则返回0
  • a>b,返回一个大于0的数代表a排在后面
1
2
3
4
5
6
7
8
// 升序
arr.sort(function(x,y){
return x-y;
})
// 降序
arr.sort(function(x,y){
return y-x;
})

every

测试每个元素是否满足条件

回调函数需要一个参数,会把所有元素传递给函数,会根据函数的返回结果返回布尔值

forEach

会把所有元素传入回调函数,但不会返回任何值,经常用来遍历

find

查找符合规则(自己写函数定义规则)的元素,返回该元素

findIndex

返回索引

闭包

除了将函数作为参数传入以外,还可以将函数返回,而闭包就是将返回一个变量环境的函数,也就是一个有状态的环境,如

1
2
3
4
5
6
7
8
function wrapper(a,b){
function sum (){
return a+b;
}
return sum;
}
var sum1 = wrapper(3,4);
sum1();// 返回7

注意使用sum1()时返回的函数才被调用,sum使用了外部函数的变量,返回的函数将参数保存了起来

因为返回的函数没有被立即执行,所以需要注意的一点就是返回的函数不能使用循环的变量,或则后续会变化的变量,如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function count() {
var arr = [];
for (var i=1; i<=3; i++) {
arr.push(function () {
return i * i;
});
}
return arr;
}

var results = count();
var f1 = results[0];
var f2 = results[1];
var f3 = results[2];

f1(); // 16
f2(); // 16
f3(); // 16

为什么会返回16呢,因为函数返回时,i已经变成了4

如果非要使用,就必须再创建一个函数将变量值存下来

1
2
3
4
5
6
7
8
9
10
11
function count() {
var arr = [];
for (var i=1; i<=3; i++) {
arr.push((function (n) {
return function(){
return n*n;
}
})(i));
}
return arr;
}

使用了一个‘创建就执行的匿名函数的语法’

1
2
3
(function (x) {
return x * x;
})(3); // 9

不加括号会报语法错误

使用场景

模拟私有变量方法

面向对象编程的语言里都可以在对象内封装一个私有变量,例如Java的private,而JS并没有这种机制,但使用闭包就可以封装一个私有变量或者方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var makeCounter = function () {
var privateCounter = 0;
function changeBy(val) {
privateCounter += val;
}
return {
increment: function() {
changeBy(1);
},
decrement: function() {
changeBy(-1);
},
value: function() {
return privateCounter;
}
}
};
var Counter1 = makeCounter();
var Counter2 = makeCounter();
console.log(Counter1.value()); /* logs 0 */
Counter1.increment();
Counter1.increment();
console.log(Counter1.value()); /* logs 2 */
Counter1.decrement();
console.log(Counter1.value()); /* logs 1 */
console.log(Counter2.value()); /* logs 0 */

箭头函数

是ES6新增的一个函数定义方式,箭头函数表达式的语法比函数表达式更简洁,并且没有自己的thisargumentssupernew.target。箭头函数表达式更适用于那些本来需要匿名函数的地方,并且它不能用作构造函数。

如果函数只是一个表达式,那他的定义非常简单

1
2
3
4
5
x => x * x;
// 相当于
function (x) {
return x * x;
}

如果有语句

1
2
3
4
5
6
7
8
x => {
if (x > 0) {
return x * x;
}
else {
return - x * x;
}
}

如果有多个参数或者无参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 两个参数:
(x, y) => x * x + y * y

// 无参数:
() => 3.14

// 可变参数:
(x, y, ...rest) => {
var i, sum = x + y;
for (i=0; i<rest.length; i++) {
sum += rest[i];
}
return sum;
}

如果要返回一个对象

1
x => ({foo:x})

this

针对前面说的this对象问题在函数的函数里面的指向问题,使用箭头函数就可以解决

1
2
3
4
5
6
7
8
9
var obj = {
birth: 1990,
getAge: function () {
var b = this.birth; // 1990
var fn = () => new Date().getFullYear() - this.birth; // this指向obj对象
return fn();
}
};
obj.getAge(); // 25

另外,箭头函数的call()apply的第一个参数总是被忽略,他总是与外部调用者绑定

1
2
3
4
5
6
7
8
9
var obj = {
birth: 1990,
getAge: function (year) {
var b = this.birth; // 1990
var fn = (y) => y - this.birth; // this.birth仍是1990
return fn.call({birth:2000}, year);
}
};
obj.getAge(2015); // 25

箭头函数不能使用new,没有prototype,不能作为生成器

关于箭头函数的一篇文章

generator

生成器(generator)是ES6引入的数据类型,类似函数,但会返回多个值

函数碰到return就会返回值并结束这个函数,如果没有返回值就隐式的代表return undefined

而generator就是可以使用yield返回多次,比如利用它写一个计数器

1
2
3
4
5
6
7
8
9
10
11
12
function* counter(){
let i= 0;
while(true){
i++;
yield i;
}

}

var g = counter();
g.next();//返回Object { value: 1, done: false }
g.next();//返回Object { value: 2, done: false }

实际上也是一个带状态的值,如果在里面写return,执行到return时done就变成true,这个生成器也就结束了

作者

manu

发布于

2020-04-22

更新于

2023-01-06

许可协议


:D 一言句子获取中...