javascript函数用法速记

参考:https://www.liaoxuefeng.com/wiki/1022910821149312/1023021087191360

在js里,函数是一等公民。

  • 函数可以分配给变量
  • 函数可以作为参数传递给其他函数
  • 函数可以从其他函数返回

1 定义函数

方式1

function abs(x) {
    if (x >= 0) {
        return x;
    } else {
        return -x;
    }
}

方式2

var abs = function (x) {
    if (x >= 0) {
        return x;
    } else {
        return -x;
    }
};

其中方式2定义了一个匿名函数,然后赋值给abs

上述两种定义完全等价

2 调用函数

JavaScript允许传入任意个参数而不影响调用,因此传入的参数比定义的参数多也没有问题,虽然函数内部并不需要这些参数

abs(10, 'blablabla'); // 返回10
abs(-9, 'haha', 'hehe', null); // 返回9

传入的参数比定义的少也没有问题

 abs(); // 返回NaN 

此时abs(x)函数的参数x将收到undefined,计算结果为NaN。

3 arguments

JavaScript还有一个关键字arguments,它只在函数内部起作用,并且永远指向当前函数的调用者传入的所有参数。arguments类似Array但它不是一个Array

function foo(x) {
    console.log('x = ' + x); // 10
    for (var i=0; i<arguments.length; i++) {
        console.log('arg ' + i + ' = ' + arguments[i]); // 10, 20, 30
    }
}
foo(10, 20, 30);
//运行结果为
x = 10
arg 0 = 10
arg 1 = 20
arg 2 = 30

4 ...参数

ES6引入了一个新的函数参数写法...

function foo(a,b,...rest){
    console.log(a)
    console.log(b)
    console.log(rest)
}
foo(1,2,3,4,5)
//运行结果
1
2
[3, 4, 5]

5 解构赋值

ES6引入解构赋值

var [x, y, z] = ['hello', 'JavaScript', 'ES6'];
let [, , z] = ['hello', 'JavaScript', 'ES6']; // 忽略前两个元素,只对z赋值第三个元素
//从一个对象中取出若干属性
var person = {
    name: '小明',
    age: 20,
    gender: 'male',
    passport: 'G-12345678',
    school: 'No.4 middle school'
};
var {name, age, passport} = person;
//对一个对象进行解构赋值时,同样可以直接对嵌套的对象属性进行赋值,只要保证对应的层次是一致的
var person = {
    name: '小明',
    age: 20,
    gender: 'male',
    passport: 'G-12345678',
    school: 'No.4 middle school',
    address: {
        city: 'Beijing',
        street: 'No.1 Road',
        zipcode: '100001'
    }
};
var {name, address: {city, zip}} = person;
//输出结果
name; // '小明'
city; // 'Beijing'
zip; // undefined, 因为属性名是zipcode而不是zip
// 注意: address不是变量,而是为了让city和zip获得嵌套的address对象的属性:
address; // Uncaught ReferenceError: address is not defined

如果要使用的变量名和属性名不一致,可以用下面的语法获取

var person = {
    name: '小明',
    age: 20,
    gender: 'male',
    passport: 'G-12345678',
    school: 'No.4 middle school'
};

// 把passport属性赋值给变量id:
let {name, passport:id} = person;
name; // '小明'
id; // 'G-12345678'
// 注意: passport不是变量,而是为了让变量id获得passport属性:
passport; // Uncaught ReferenceError: passport is not defined

6 方法

绑定到对象的函数称为方法。

var xiaoming = {
    name: '小明',
    birth: 1990,
    age: function () {
        var y = new Date().getFullYear();
        return y - this.birth;
    }
};

xiaoming.age; // function xiaoming.age()
xiaoming.age(); // 今年调用是25,明年调用就变成26了

7 this关键字

上例中方法中的this指的是当前对象。

但是,全局的this指的是window对象

8 函数的apply()方法

我们看一下下面的代码

function getAge() {
    var y = new Date().getFullYear();
    return y - this.birth;
}

var xiaoming = {
    name: '小明',
    birth: 1990,
    age: getAge
};

xiaoming.age(); // 25, 正常结果
getAge(); // NaN

如果以对象的方法形式调用,比如xiaoming.age(),该函数的this指向被调用的对象,也就是xiaoming,这是符合我们预期的。

如果单独调用函数,比如getAge(),此时,该函数的this指向全局对象,也就是window

我们可以使用函数的apply方法对上例做一下改造,使得单独调用getAge()时指向函数内部this的指向

function getAge() {
    var y = new Date().getFullYear();
    return y - this.birth;
}

var xiaoming = {
    name: '小明',
    birth: 1990,
    age: getAge
};

xiaoming.age(); // 25
getAge.apply(xiaoming, []); // 25, this指向xiaoming, 参数为空

9 函数的call()方法

call()方法和与apply()类似,唯一的区别是

  • apply()把参数打包成Array再传入;

  • call()把参数按顺序传入。

Math.max.apply(null, [3, 5, 4]); // 5
Math.max.call(null, 3, 5, 4); // 5

10 apply()实现装饰器

这个例子我们修改window.parseInt()方法,加上我们定制化的代码。

'use strict';

var count = 0;
var oldParseInt = parseInt; // 保存原函数

window.parseInt = function () {
    count += 1;
    return oldParseInt.apply(null, arguments); // 调用原函数
};

后续执行parseInt('3')将会首先计算count值,然后再调用真实的parseInt()。起到装饰器的作用

11 高阶函数

如果一个函数,其参数也是一个函数,则我们称之为高阶函数

11.1 map

map()方法定义在Array中

用法举例

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
var results = arr.map(x=>x*x); // [1, 4, 9, 16, 25, 36, 49, 64, 81]

上面,我们可以一眼就可以看出代码的用意,即把f(x)=x2作用在Array的每一个元素并把结果生成一个新的Array

再举例,我么把一个整型数组转换为对应的字符串数组

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
arr.map(String); // ['1', '2', '3', '4', '5', '6', '7', '8', '9']

11.2 reduce

reduce()方法也是Array中定义

在一个数组上应用reduce函数,效果如下

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

举例

var arr = [1, 3, 5, 7, 9];
arr.reduce((x, y)=>x+y); // 25

11.3 filter

filter()方法也是定义在Array上

举例

var arr = [1, 2, 4, 5, 6, 9, 10, 15];
arr.filter(x=> x % 2 !== 0);

11.4 Array上其他的高阶函数

除了上面提到的高阶函数,还有一些常用的高阶函数:sort、every、find、findIndex、forEach

12 闭包

定义枯燥无味且难于理解,我们举例说明

我们定义一个函数lazy_sum(),用于计算一个数组的和,我们不返回求和结果,而是返回一个函数

function lazy_sum(arr) {
    var sum = function () {
        return arr.reduce((x, y) => x + y);
    }
    return sum;
}
var f=lazy_sum([1,2,3,4,5])
f() //执行结果是15

在这个例子中,我们在函数lazy_sum中又定义了函数sum,并且,内部函数sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,故称之为闭包。这种称为“闭包(Closure)”的程序结构拥有极大的威力。

13 箭头函数

箭头函数可以解决this指向问题

举例,

如果我们不用箭头函数

var obj = {
    birth: 1990,
    getAge: function () {
        var b = this.birth; // 1990
        var fn = function () {
            return new Date().getFullYear() - this.birth; // this指向window或undefined
        };
        return fn();
    }
};

调用结果

obj.getAge()
NaN

我们使用箭头函数,则完全修复了this的指向,this总是指向词法作用域,也就是外层调用者obj

var obj = {
    birth: 1990,
    getAge: function () {
        var b = this.birth; // 1990
        var fn = () => new Date().getFullYear() - this.birth; // this指向obj对象
        return fn();
    }
};

调用结果

obj.getAge()
32

14 generator

ES6引入了一个新的数据类型generator

generator定义

function* foo(x) {
    yield x + 1;
    yield x + 2;
    return x + 3;
}

generator和函数不同的是,generator由function*定义(注意多出的*号),并且,除了return语句,还可以用yield返回多次

调用

var f=foo(1)
f.next(); // {value: 2, done: false}
f.next(); // {value: 3, done: false}
f.next(); // {value: 4, done: true}

generator的一个巨大的好处,就是把异步回调代码变成“同步”代码。

有了generator,发送ajax请求时就可以很优雅的写

try {
    r1 = yield ajax('http://url-1', data1);
    r2 = yield ajax('http://url-2', data2);
    r3 = yield ajax('http://url-3', data3);
    success(r3);
}
catch (err) {
    handle(err);
}

 

posted @ 2022-12-01 07:03  zhenjingcool  阅读(27)  评论(0编辑  收藏  举报