sort() | 闭包 | 箭头函数 | Generator函数

sort() | 闭包 | 箭头函数 | Generator函数_第1张图片
概述.png

廖雪峰老师----JavaScript教程

一.sort()

  • 排序的核心?比较两个元素的大小

  • 问题:

[1,10,5,45].sort();
//(4) [1, 10, 45, 5]

因为:Array的sort()方法默认把所有元素先转换为String再排序,同时4的ASCll码比5小。

  • 解决
//常规方式
var arr = [1,10,45,5];
arr.sort(function(x,y){
    if(x > y){
        return 1;
    }
    if(x < y){
        return -1;
    }
    return 0;
});
//(4) [1, 5, 10, 45]
//优雅的方式
var arr = [1,10,45,5];
arr.sort(function(a,b){
    return a-b;
});
//(4) [1, 5, 10, 45]
sort() | 闭包 | 箭头函数 | Generator函数_第2张图片
sort()
sort() | 闭包 | 箭头函数 | Generator函数_第3张图片
sort()数值排序
  • sort()方法会直接修改原数组Array,同时没有"另起炉灶"---copy,返回的结果仍然是当前Array。
var a1 = ['b','a','c'];
var a2 = a1.sort();
console.log(a1);//["a", "b", "c"]
console.log(a2);//["a", "b", "c"]


二. 闭包

1. 函数作为返回值

function parent(arr){
    var child = function(){
        return arr.reduce(function(a,b){
            return a+b;
        });
    }
    return child;
}
var f = parent([1,2,3,4,5]);
f();//15
  • More:该子函数可以引用父函数中的参数和局部变量;当从父函数中返回子函数时,相关的参数和变量仍保存在返回的子函数内。

  • 注意:当我们调用父函数时,每次调用都会返回一个新的函数,即使传入相同的参数:

var f1 = parent([1,2,3,4,5]);
var f2 = parent([1,2,3,4,5]);
f1 === f2;//false

2. 返回的函数并没有立刻执行

  • 什么意思?
function count(){
    var arr = [];
    for(var i = 1;i <=3;i++){
        arr.push(function(){
            return i*i;
        });
    }
    return arr;
}
var result = count();
var f1 = result[0];
var f2 = result[1];
var f3 = result[2];
console.log(f1());//16
console.log(f2());//16
console.log(f3());//16
  • 变量result是一个指向函数count执行结果arr的引用,该数组中包含了三个函数:
ƒ (){
   return i*i;
}
  • 这三个函数都引用了父函数count中的局部变量 i,但这三个返回的子函数并没有立即执行。当这三个函数都返回时,它们所引用的变量i已经变成了4,所以三次结果均为16。

  • 返回闭包时要牢记一点:返回的函数不要引用任何循环变量,或者后续会发生变化的变量。

  • 这里你想要的结果是:1 4 9,可以用以下方法实现:

var arr = [1,2,3];
function square(arr){
    return arr.map(function(a){
        return a*a;
    });
}
square(arr);//[1, 4, 9]
  • 如果你非要用嵌套函数的方式
//创建一个匿名函数并立即执行
function count(){
    var arr = [];
    for(var i = 1;i <=3;i++){
        arr.push((function(a){
            return function(){
                return a*a;
            }
        })(i));
    }
    return arr;
}
var result = count();
var f1 = result[0];
var f2 = result[1];
var f3 = result[2];
console.log(f1());//1
console.log(f2());//4
console.log(f3());//9
//优雅点的---let
function count(){
    var arr = [];
    for(let i = 1;i <=3;i++){
        arr.push(function(){
            return i*i;
        });
    }
    return arr;
}
var result = count();
var f1 = result[0];
var f2 = result[1];
var f3 = result[2];
console.log(f1());//1
console.log(f2());//4
console.log(f3());//9

3. 闭包的真正威力

  • 模拟私有变量:

  • 设计一个计数器,执行一下,加1,而不是人工加1:

function counter(i){
    return i++;
}
counter(0);
//0
counter(1);
//1
counter(2);
//2
counter(3);
//3
//自动化
function counter(initial){
    var x = initial || 0;//防止initial = undefined
    return {
        inc : function(){
            return x++;
        }
    };
}
var c1 = counter();
c1.inc();//0
c1.inc();//1
c1.inc();//2
c1.inc();//3

闭包就是携带状态的函数,并且它的状态可以对外完全隐藏。

4. 使用闭包将多参数的函数变成单参数的函数。

  • 比如:x^y 可以使用Math.pow(x,y)函数,但对于x^2 或x^3有点大材小用了,使用闭包弄个简洁版的。
function make_pow(y){
    return function(x){
        return Math.pow(x,y);
    };
}
var pow2 = make_pow(2);
var pow3 = make_pow(3);
pow2(5);//25
pow3(3);//27


三. 箭头函数(Arrow Function)

1.箭头函数 & 匿名函数 区别?

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

//匿名函数
var obj = {
    birth : 1996,
    getAge : function(){
        var b = this.birth;
        var fn = function(){
            return (new Date().getFullYear() - this.birth);
        }
        return fn();
    }
}
obj.getAge();//NaN
2017 - undefined;//NaN
//箭头函数
var obj = {
    birth : 1996,
    getAge : function(){
        var fn = () => new Date().getFullYear() - this.birth;
        return fn();
    }
}
obj.getAge();//21
//由于箭头函数中的this已绑定了词法作用域,
//因此call()或者apply()调用箭头函数时,传入的第一个参数无效。
var obj = {
    birth : 1996,
    getAge : function(year){
        var f = (y) => y - this.birth;//this.birth仍为1996,而非2000
        return f.call({birth: 2000},year);    
    }
}
obj.getAge(2017);//21


四.generator(生成器)

1.调用函数:传入参数,返回结果

//常规方法
function fib(count){
    var a = 0,
        b = 1,
        temp,
        arr = [0,1];
    while(arr.length < count){
        temp = a + b;
        a = b;
        b = temp;
        arr.push(temp);
    }
    return arr;
}
fib(10);
>>>
(10) [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

阮一峰老师---Generator 函数的语法

  • 调用Generator函数,返回一个遍历器对象,代表Generator函数的内部指针。以后,每次调用遍历器对象的next方法,都会返回一个带有value和done两个属性的对象。value属性表示当前的内部状态的值,是yield或者return后面那个表达式的值;done属性是一个布尔值,表示是否遍历结束。

  • return & yield
    1.相似处:都返回紧跟在语句后面的那个表达式的值。
    2.区别:在一个函数中,只能执行一个return语句,执行后,就结束该函数了。而每个yield表达式都能通过next()方法在该函数中执行。在一个函数中,每次遇到yield语句,函数暂停执行,下一次再从该位置继续往后执行,因此,它具有位置记忆的能力。
    3.next()方法会执行Generator函数中的yield表达式和return语句;但当使用for...of时,仅会执行yield表达式。
    4.开发者角度:通过next方法,Generator函数依次遍历yield表达式。
    5.Generator函数角度:依次生成了一系列的值,也就是Generator(生成器)名字的来源。

next()  & for...of
///next()
function* fib(max) {
    var
        t,
        a = 0,
        b = 1,
        n = 1;
    while (n < max) {
        yield a;
        t = a + b;
        a = b;
        b = t;
        n ++;
    }
    return a;
}
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: true}

//for...of
function* fib(max) {
    var
        t,
        a = 0,
        b = 1,
        n = 1;
    while (n < max) {
        yield a;
        t = a + b;
        a = b;
        b = t;
        n ++;
    }
    return a;
}
for(var i of fib(5)){
    console.log(i);
}
>>>
0
1
1
2
  • 如果在普通函数中使用yield表达式,会产生语句错误。
var arr = [1, [[2, 3], 4], [5, 6]];
var flat = function* (a) {
  //普通函数
  a.forEach(function (item) {
    if (typeof item !== 'number') {
      yield* flat(item);
    } else {
      yield item;
    }
  });
};
for (var f of flat(arr)){
  console.log(f);
}
//Uncaught SyntaxError: Unexpected identifier
//解决:使用for循环替代内部的普通函数
var arr = [1,[[2,3],4],[5,6]];
var flat = function* (a){
    var length = a.length;
    for(var i = 0;i < length;i++){
        var item = a[i];
        //将数组中的嵌套数组中的每个元素依次输出
        if(typeof item !== 'number'){
            yield* flat(item);
        }else{
            yield item;
        }
    }
};
for(var f of flat(arr)){
    console.log(f);
}

>>>
1
2
3
4
5
6
  • yield表达式如果用在另一个表达式中,必须用圆括号括起来:
function* demo() {
  console.log('Hello' + yield); // SyntaxError
  console.log('Hello' + yield 123); // SyntaxError
}
//Uncaught SyntaxError: Unexpected identifier

function* demo() {
  console.log('Hello ' + (yield)); // OK
  console.log('Hello ' + (yield 123)); // OK
}
var f = demo();
f.next();
//{value: undefined, done: false}

f.next();
>>>
Hello undefined
{value: 123, done: false}

f.next();
>>>
Hello undefined
{value: undefined, done: true}
  • yield表达式用作函数参数或放在赋值表达式的右边,可以不加括号
function* demo(){
    foo(yield 'a',yield 'b');
    let input = yield;
}

你可能感兴趣的:(sort() | 闭包 | 箭头函数 | Generator函数)