资讯专栏INFORMATION COLUMN

JavaScript学习笔记第二天_函数

ShevaKuilin / 2649人阅读

摘要:廖雪峰的教程学习笔记变量作用域不能声明块级的变量,的函数内变量声明会被提升至函数体开头则用来解决这个块级变量声明,于引入。普通函数一般将赋值为。高阶函数输出结果是。箭头函数新引入的相当于如下的匿名函数其中为参数。

廖雪峰的JavaScript教程学习笔记 1. 变量作用域

var 不能声明块级的变量,js的函数内变量声明会被提升至函数体开头
let 则用来解决这个块级变量声明,于ES6引入。
const用于声明常量,也是ES6引入。

2. 命名空间

全局变量会被默认绑定到window,不同JS文件如果定义了相同的名称的全局变量或者顶级函数,那么就会导致冲突。因此,解决方法就是把自己的全局变量绑定到一个全局变量中,类似于Java中的class,个人感觉。

3.this 关键字
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了

因为xiaoming是个对象,所以方法内部的this就是指当前xiaoming这个对象,和java对象内部使用的this是一个意思。但是如果在方法内不又定义了方法,并在这个内部方法里引用this,那么这个this将报错,undefined或者window。如果从java角度看的话,有点像是内部类里使用this因而出错。 但也可以在内部方法的外层,捕获this对象,比如赋值给that。

4. apply 和 call

每个函数都自带applycall方法,其中apply接受两个参数(object, arguments[]), 即传入对象本身和参数数组,这样就方法体内的this就会自动指向object,从而避免this乱指。

call方法与apply具有同样的功能,第一个也是object自己,之后是参数列表,非Array
普通函数一般将obejct赋值为null
比如

Math.max.call(null, 3,4,4,5,6,6,67,8,9);
Math.max.apply(null, [3,4,4,5,6,6,67,8,9]);
5. 装饰器

javascript所有的对象都是动态,即使是内置的函数,也可以重新指向新的函数。
如window上有个全局函数parseInt()函数,需要统计parseInt被调用次数。

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

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

// 测试:
parseInt("10");
parseInt("20");
parseInt("30");
count; // 3

可以看出来JavaScript如此轻易就实现了装饰器,java哭晕在厕所。

6. 高阶函数

定义:高阶函数英文叫Higher-order function。
JavaScript的函数其实都指向某个变量。既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。这个厉害了,WOW。
里面也涉及到一个javascript的函数的参数是可以传入多个或不传的原理,所以方法体内才可以直接对传入变量输入参数,并且个数不限制。直接show me the fucking source code。

function add(x, y, f){
    return f(x) + f(y);
}

var f = Math.abs;
add(6, -3, f); // 输出9
7. 高阶函数map/reduce

666啊,这个map/reduce不是大数据用的么?居然是js的高阶函数啊,看起来好吊啊,真是吊打java了。
网上找了一段解释:

array.map(callback[, thisArg]);map 方法会给原数组中的每个元素都按顺序调用一次 callback 函数。callback 每次执行后的返回值组合起来形成一个新数组。 callback 函数只会在有值的索引上被调用;那些从来没被赋过值或者使用 delete 删除的索引则不会被调用。

callback 函数会被自动传入三个参数:数组元素,元素索引,原数组本身。
如果 thisArg 参数有值,则每次 callback 函数被调用的时候,this 都会指向 thisArg 参数上的这个对象。如果省略了 thisArg 参数,或者赋值为 null 或 undefined,则 this 指向全局对象 。

如对每个元素进行平方运算。

var pow = function(x){
    return x*x;
}
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
arr.map(pow);

输出结果是[1, 4, 9, 16, 25, 36, 49, 64, 81]
所以结合上面的解释就是map对数组arr的每个元素调用一次callback即pow()函数。由于map会给callback传入三个参数,而pow只取了第一个参数,即数组元素,所以结果是对每个元素平方,然后组成成新的数组。

我们可以试着修改pow,去把传入到callback的第二个参数也使用上,那么我们来试试给给每个元素平方后加上他的索引值。

var pow = function(x){
    return x*x+arguments[1];
}
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
arr.map(pow);

输出结果为[1, 5, 11, 19, 29, 41, 55, 71, 89]
更为具体的map定义可以看MSDN的解释。

接下来看看reduce函数。同样作为array的方法,reduce是对数组的每一个元素与其下一个元素调用callback一次方法,并将callback的结果作为下一次的参数与下一个元素再次调用callback方法,进行累积运算。有点绕,看数学公式可能更好理解

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

所以我们来进行一个求和,计算1到100和运算。

var arr = [];
for(var i = 1; i <= 100; i++){
    arr[i-1] = i;
}
arr.reduce(function (x, y){
    return x+y;
});

输出结果为5050
再来看看具体reduce定义

arr.reduce(callback[,initialValue])
initialValue作为reduce方法的可选参数,callback接收4个参数, accumulator 是累积值,如果reduce方法设定了initialValue,那么accumulator的初始值就是initialValue。

accumulator

currentValue

currentIndex

array

所以我们可以继续尝试设定初始的方式

var arr = [];
for(var i = 1; i <= 100; i++){
    arr[i-1] = i;
}
arr.reduce(function (x, y){
    return x+y;
}, 100);

我们将初始值设为100,那么输出结果是5150。

测试题

不要使用JavaScript内置的parseInt()函数,利用map和reduce操作实现一个string2int()函数:

function string2ints(s){

return s.split("").map(function(x){
    return x*1}).reduce(function(x, y){
        return x*10+y*1;
});

}

请把用户输入的不规范的英文名字,变为首字母大写,其他小写的规范名字。输入:["adam", "LISA", "barT"],输出:["Adam", "Lisa", "Bart"]

// comment on this
var arr = ["LINda", "adam", "barT"];
arr.map(function(x){
    return x.toLowerCase();}).map(function(x){
        return x[0].toUpperCase()+x.slice(1);
});

小明的疑惑在与没搞懂parseInt的接受的参数,因为map的callback默认是会传入三个参数,而parseInt的参数有两个parseInt(string, radix);那么此时parseInt自动忽略传入的第三个参数array,而传入了(element, index)。索引改变了进制,因而得到错误的结果。
这个感觉是个坑,还不清楚IDE是否会提示函数或者方法的参数为啥。

8.高阶函数filter

语法:var new_array = arr.filter(callback[, thisArg])
filter也是一个常用的操作,它用于把Array的某些元素过滤掉,然后返回剩下的元素。与map相同的是,它也接受一个函数callback,然后对每个元素调用函数进行处理,不同的地方在于,函数需要返回true或false,用户决定是保留该元素还是删除该元素,最后生成一个新的数组,如其名字所决定的一样。thisAgrs不用解释。

其中callback同样接收3个参数element,index,array。

简单的做个去重操作和过滤掉奇数。

var arr = [1,2,3,4,4,4,5,5,5,6,6,7,7,8,8,9,9,9];
arr.filter(function(x){return x % 2 === 0;})
   .filter(function(element, index, self){
        return self.indexOf(element) === index;}
   ); 

self.index(element)只会返回找到的第一个元素的index,因此后续的index就不再相等了。

在做一个找出1-100以内所有的质数或者说素数。
质数或素数定义

质数大于等于2 不能被它本身和1以外的数整除

做法:在一般领域,对正整数n,如果用2到sqrt(n)之间的所有整数去除,均无法整除,则n为质数。
代码:

function getPrimes(arr){
return arr.filter(function(x){
        if(x <= 3) return x>1; // 大于等于2才可以为质数,所以1除外
        // 2到sqrt(n)之间均无法整除,即只要有一个可以整除即非质数。
        var end = Math.round(Math.sqrt(x));
        for(let i=2; i <= end; i++){
            if(x % i === 0) return false;
        }
        return true;
       });
}
9.高阶函数sort

[1, 2,3,10,24].sort();
输出结果是[1, 10, 2, 24, 3]。javascript或默认将数组里数字转换成string去比较,那么根据ASCII嘛,自然是1要比2小,所以10就在2的前面。

But, 高阶函数嘛,那么自然sort也是可以接受函数来进行修改比较算法的,类似于要实现Java里的comparable接口,规则也是类似的,若x < y, 则返回-1,表示小于;若x === y, 返回0;若x > y,则返回1。理论上,并不要定义为1或者-1,只要用正数表示大于,负数表示小于就好了。所以你可以直接使用x-y;

那就数字排个序好了。

arr = [12343,5435,6,7,2,3,77,88,322];
arr.sort(function(x, y){
    return x-y;
});

Caution:这个sort方法会将排序结果直接作用在数组本身上,即会修改arr。

10.闭包

高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。
比如返回求和的函数。

function lazy_sum(arr){
    var sum = function(){
        return arr.reduce(function(x, y){
        return x+y;
        });
    return sum;
}
// 使用时
var f = lazy_sum([1,3,2,5]);
f();
var f1 = lazy_sum([1,3,2,5]);
f === f1; // result is false.

由于返回的是个函数,即使传入的参数相等,也相当于是返回了不同的函数对象。

返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
这个例子返回的函数引用了局部变量, 如果局部变量后续会变化,那么最好是再创建一个函数,绑定局部变量作为参数。

创建匿名函数并立刻执行.

(function (x) { return x * x }) (3);

其中function (x) { return x * x }是匿名函数,需要使用括号括起来才可以立刻执行,不然会报语法错误。

使用闭关封装一个私有变量private。

function create_counter(initial){
    var x = initial || 0;
    return {
        inc:function(){
            x += 1;
            return x;
        }
    }
}

create_counter()返回了一个匿名对象,这个对象有个属性inc,而这个inc的值又是一个函数。那么就可以获取这个匿名对象,然后调用函数inc()即可实现对private的x加1.

缩减参数列表的用法

function make_pow(n){
    return function(x){
        return Math.pow(x,n);
    }
}
var pow2 = make_pow(2);// 返回一个Math.pow(x, 2)的函数。
var pow3 = make_pow(3);// 返回一个Math.pow(x, 3)的函数。

pow2 和 pow3就只需要接收一个参数,即可实现power的操作。

底下的Lambda表达式加法没能理解呀。

11.箭头函数

ES6 新引入的

x => x * x

相当于如下的匿名函数,其中x为参数。

function (x){
    return x*x;
}

如果有多个参数需要使用括号括起来,如(x,y) => x+y;
用法呢和匿名函数也差不多,比如:

var pow2 = x=> x*x;
pow2(2); // 4
// 创建匿名函数并执行
(x => x*x)(2); // 4

还有一种包含多条语句的,需要使用{}将其包起来。如:

(x, y) => {
    if(x>y){
        return x-y;}
    else{
        return y-x;}
}

还有类似于()=>3.14的写法,虽然看起来没啥卵用。

可变参数写法,就是参数变了一下

(x, y, ...rest) =>{
    var sum= x =y;
    for(var x of arguments){
        sum += x;
    }
    return sum;
}

廖老师说x => { foo: x }这样写会语义错误,然而并没有。但是确实没能够被正确解析,所以当返回匿名对象的时候,使用()将其包起来,如x => ({ foo: x })。

需要注意的是箭头函数内部的this是词法作用域,由上下文确定。箭头函数完全修复了this的指向,this总是指向词法作用域,也就是外层调用者obj。那么在第4节里说的每个函数都自带apply和call方法,他们的第一个参数object就都不在有用,因为箭头函数会自动绑定外层调用者obj。

12. generator

ES6新引入的数据类型,看上去像是一个函数,但是可以返回多次。
与函数定义不同的是,使用function*来定义一个generator,除了使用return退出函数之后,还可以使用yield交出控制权,这时并没有退出整个函数,使用next时,就会从当前yield处往下执行。

以斐波那契数列为例:[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377]
普通的使用数组来保存结果的斐波那契数列生成方法

function fib(max){
    var 
        t,
        a = 0,
        b = 1,
        arr = [0, 1];
    while (arr.length < max){
        t = a + b;
        a = b;
        b = t;
        arr.push(t);
    }
    return arr;
}

以generator生成器生成斐波那契数列的方法。

function* fib2(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 = fib2(5);
undefined
> f.next(); // 输出0返回。
Object {value: 0, done: false}
> f.next();
Object {value: 1, done: false}
> f.next();
Object {value: 1, done: false}
> f.next();
Object {value: 2, done: false}
> f.next();
Object {value: 3, done: true}
> f.next();
Object {value: undefined, done: true}

可以看到每次调用next,都会促使整段代码执行到下一个yield,然后输出一个匿名对象{value:xx, done:true/false} 其中xx是yield的返回值,done是表明此时是否生成结束,即是否执行到return。

还有一种使用方式,就是for...of,无需判断是否执行完毕。

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

But这个generator有啥卵用呢?我暂时想起来一个状态转换的,即每次调用都会切换到下一个状态,直到切换完成,像Android中的StateMachine一样。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/91292.html

相关文章

  • 26天学通前端开发(配资料)

    摘要:网上有很多前端的学习路径文章,大多是知识点罗列为主或是资料的汇总,数据量让新人望而却步。天了解一个前端框架。也可以关注微信公众号晓舟报告,发送获取资料,就能收到下载密码,网盘地址在最下方,获取教程和案例的资料。 前言 好的学习方法可以事半功倍,好的学习路径可以指明前进方向。这篇文章不仅要写学习路径,还要写学习方法,还要发资料,干货满满,准备接招。 网上有很多前端的学习路径文章,大多是知...

    blair 评论0 收藏0
  • 二天

    摘要:学习用时小时学习目标第二天任务目标是否已达成麻麻嘚。摘录一个笔记第二天笔记以下是原文爱上前端,用技术带给用户最优美的体验。为文本框命名,以备后台程序使用。 codepen好像崩了?-_-|| 2019.3.6 怎么也要至少过一遍鸭。学习用时:3小时学习目标:第二天任务目标是否已达成:麻麻嘚。 今天学了什么? 勉强再认了一次各种标签(虽然感觉还是认不住),ol,ul,li和dl,dt,d...

    warkiz 评论0 收藏0
  • 全国计算机等级考试二级Python(2021年9月)备考笔记 二天

    摘要:本题目的考察点在于函数的格式输出规则。方法改变随机数生成器的种子,可以在调用其他随机模块函数之前调用此函数。参数改变随机数生成器的种子。返回一个至区间包含和的整数。 ...

    Codeing_ls 评论0 收藏0
  • Day02 - JavaScript + CSS Clock

    摘要:作者简介是推出的一个天挑战。完整指南在从零到壹全栈部落。通过时分秒对一圈度,进行映射,确定每一个指针所需旋转的角度。此前的代码中,每秒都会重新一个对象,用来计算角度值,但如果让这个角度值一直保持增长,也就不会出现逆时针回旋的问题了。 Day02 - JavaScript + CSS Clock 作者:©liyuechun 简介:JavaScript30 是 Wes Bos 推出的一个...

    zzbo 评论0 收藏0

发表评论

0条评论

ShevaKuilin

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<