JS学习快速笔记(四)

199 阅读3分钟

这是我参与11月更文挑战的第5天,活动详情查看:2021最后一次更文挑战

闭包

高阶函数除了可以接受函数为参数,还可以返回一个函数作为返回的结果

闭包总结来说就是,一个作用域使用了另一个作用域中的变量。那怎么使用呢?用返回的函数。

转换成Java视角,就是一个类定义私有变量private,其他类不能直接访问,只能通过get或者set方法获得或者修改这个私有变量。

function lazy_sum(arr) {
    var sum = function () {
        return arr.reduce(function (x, y) {
            return x + y;
        });
    }
    return sum;
}

var f = lazy_sum([1, 2, 3, 4, 5]); // function sum() 返回一个函数,不立刻求和

f(); // 15 等调用函数时,才真正求和。

//内部函数sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中
function count() {
    var arr = [];
    for (var i=1; i<=3; i++) {  //使用的是var
        arr.push(function () {
            return i * i;
        });
    }
    return arr;
}

var results = count();
var f1 = results[0]; 
var f2 = results[1];
var f3 = results[2];
//最后调用都是16 因为他的循环数使用的是var没有局部作用域,最后的i都是4, 
f1(); // 16
f2(); // 16
f3(); // 16
//我们可以用该函数的参数绑定循环变量当前的值
function count() {
    var arr = [];
    for (var i=1; i<=3; i++) {
        arr.push((function (n) {
            return function () {
                return n * n;
            }
        })(i));
    }
    return arr;
}
//或者
//改用let或者const就会恢复正常,因为他们有局部作用域,每一次循环都是新建一个变量
function count() {
    var arr = [];
    for (let i=1; i<=3; i++) {
        arr.push((function (n) {
            return function () {
                return n * n;
            }
        })(i));
    }
    return arr;
}

返回函数不要引用任何循环变量,或者后续会发生变化的变量

利用闭包,我们可以实现类似Java中的类的不同实例的感觉。

function make_pow(n) {
    return function (x) {
        return Math.pow(x, n);
    }
}

// 创建两个新函数:       类似于不同的实例类 Person 一个名字叫2 一个名字叫3
var pow2 = make_pow(2); 
var pow3 = make_pow(3);

console.log(pow2(5)); // 25
console.log(pow3(7)); // 343

立刻执行的匿名函数

上述代码中有一段这样的代码,他的作用是创建一个匿名函数并立刻执行

(function (n) {
            return function () {
                return n * n;
            }
        })(i) //其中的i为传入,传入之后即为n

//像是这样
(function (x) {
    return x * x;
})(3);
//他和 3 * 3 结果没有区别

没有给函数名称命名的函数,为匿名函数

箭头函数

ES6新增了箭头函数,他用一个等于加上大于号,定义一个函数。

x => x * x

箭头函数简化了函数的定义,他可以把定义的函数体{...}的括号和return省略。当然如果有多条语句,就不能省略

x => {
    if(x = 1){
        return 1;
    }else {
        return x * x;
    }
}

如果参数是多个,就不能省略参数的括号

(x,y) => {
    return x * y;
}

当我们在单表达式中,返回一个对象

x => ({foo: x}) // 主要要用小括号包裹,不然对象的{}和函数体的{}会造成冲突

箭头函数中的this

箭头函数内部的this是词法作用域,由上下文确定

之前有说到this关键字的指向问题。那么箭头函数完全修复了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(); // 25 不再需要之前用var捕获this的写法了

生成器generator

ES6引入了生成器的概念,generator由function*定义,并且,除了return语句,还可以用yield返回多次。

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

直接调用一个generator和调用函数不一样

第一种方式使用generator的next方法

function* fib(max) { //斐波那契数列
    var
        t,
        a = 0,
        b = 1,
        n = 0;
    while (n < max) {
        yield a;
        [a, b] = [b, a + b];
        n ++;
    }
    return;
}

var f = fib(5);
f.next(); // {value: 0, done: false}
f.next(); // {value: 1, done: false}
f.next(); // {value: 1, done: false}
f.next(); // {value: 2, done: false}
f.next(); // {value: 3, done: false}
f.next(); // {value: undefined, done: true}

第二种方式可以使用for of的方式调用

for (var x of fib(10)) {
    console.log(x); // 依次输出0, 1, 1, 2, 3, ...
}