Qingquan-Li / blog

My Blog
https://Qingquan-Li.github.io/blog/
132 stars 16 forks source link

JavaScript函数 #94

Open Qingquan-Li opened 6 years ago

Qingquan-Li commented 6 years ago

JavaScript 函数

JavaScript 中的函数:用于执行特定任务的代码块。

另外,将脚本script编写为函数以避免页面载入时执行该脚本。


JavaScript 函数语法:

  1. A JavaScript function is defined with the function keyword, followed by a name, followed by parentheses ()
  2. 函数名称可以包含字母,数字,下划线和美元符号(与变量相同的规则),建议使用驼峰命名法
  3. The parentheses may include parameter names separated by commas: (parameter1, parameter2, ...)
  4. The code to be executed, by the function, is placed inside curly brackets: {}
function name(parameter1, parameter2, parameter3) {
    code to be executed
}


形参和实参的区别:


注意:JavaScript 函数传入的参数是可选的。

ECMAScript 函数的参数与大多数其他语言中函数的参数有所不同。ECMAScript 函数不介意传递进来多少个参数,也不在乎传进来参数是什么数据类型。也就是说,即便你定义的函数只接收两个参数,在调用这个函数时也未必一定要传递两个参数。可以传递一个、三个甚至不传递参数,而解析器永远不会有什么怨言。之所以会这样,原因是 ECMAScript 中的参数在内部是用一个数组来表示的。函数接收到的始终都是这个数组,而不关心数组中包含哪些参数(如果有参数的话)。如果这个数组中不包含任何元素,无所谓;如果包含多个元素,也没有问题。实际上,在函数体内可以通过 arguments 对象来访问这个参数数组,从而获取传递给函数的每一个参数。 其实, arguments 对象只是与数组类似(它并不是 Array 的实例),因为可以使用方括号语法访问它的每一个元素(即第一个元素是 arguments[0] ,第二个元素是 argumetns[1] ,以此类推),使用 length 属性来确定传递进来多少个参数。

——《JavaScript高级程序设计(第3版)》





JavaScript 函数分类

  1. 系统函数
  2. 自定义函数
  3. 匿名函数(属于自定义函数)



分类一:系统函数

常用的系统函数举例 描述
parseInt() 解析一个字符串并返回一个整数
parseFloat() 解析一个字符串并返回一个浮点数
isNaN() 检查某个值是否是数字,返回 true / false
eval() 计算 JavaScript 字符串,并把它作为脚本代码来执行


/*系统函数实例*/
parseInt("143dd") //将字符串转化为整数,结果为143
parseFloat("143.33dd") //解析一个字符串,并返回一个浮点数,结果为143.33



分类二:自定义函数:

自定义函数方式一:

function 函数名 (参数1,参数2, ... ){
    //函数代码块
}

自定义函数方式二:

var 函数名 = function (参数1,参数2, ... ){
    //函数代码块
}


函数的调用

函数可以通过其名字加上括号中的参数进行调用。

JavaScript 自定义函数时,参数只需要声明变量名称,不需要使用 var

函数的调用有以下两种方式:

  1. 事件名=函数名(传递的实参值) ,例如:

    onclick = "myFunction()"
  2. 直接使用 函数名(传递的实参值) ,例如:

    var result = add(2, 3);


函数的返回值

通过 return 关键字返回函数的值

实例:

function sayHello(name){
    return ( name + "说了句hello。" );
}
var str = sayHello("Fatli");
alert( str );   // Fatli说了句hello。

与 Java 一样,函数在执行过 return 语句后立即停止代码。因此, return 语句后的代码都不会被执行。



分类三:匿名函数(属于自定义函数)

还有一种自定义函数,叫匿名函数 Anonymous Function ,即没有函数名。

存储在变量中的函数不需要函数名称。它们总是使用变量名称调用。

实例一:

// 自定义(匿名)函数
// The function ends with a semicolon(分号) because it is a part of an executable statement.
var x = function (a, b) {
     return a * b;
};
// 匿名函数的调用:
var z = x(4, 3);


实例二:

// 自定义(匿名)函数
var fn = function() {
  alert(123);  
};
// 匿名函数的调用:
fn();

// 括号()中直接写函数体,直接执行(匿名)函数
// 直接执行(匿名)函数,一般用于框架的封装
(function() {
    alert(123);
})


实例三:

// 直接执行 setInterval() 方法中的括号中匿名函数
// 直接执行 setInterval() 方法
// 3 秒后弹框显示 Hello
setInterval(function() {
    alert("Hello");
}, 3000);



附:匿名函数通常作为(内部)自调函数使用,是为 JavaScript Function Closures(闭包)

示例: https://github.com/tastejs/todomvc/blob/gh-pages/examples/vue/js/app.js




附:回调函数

当程序跑起来时,一般情况下,应用程序(application program)会时常通过API调用库里所预先备好的函数。但是有些库函数(library function)却要求应用先传给它一个函数,好在合适的时候调用,以完成目标任务。这个被传入的、后又被调用的函数就称为回调函数(callback function)。 打个比方,有一家旅馆提供叫醒服务,但是要求旅客自己决定叫醒的方法。可以是打客房电话,也可以是派服务员去敲门,睡得死怕耽误事的,还可以要求往自己头上浇盆水。这里,“叫醒”这个行为是旅馆提供的,相当于库函数,但是叫醒的方式是由旅客决定并告诉旅馆的,也就是回调函数。而旅客告诉旅馆怎么叫醒自己的动作,也就是把回调函数传入库函数的动作,称为登记回调函数(to register a callback function)。 摘自:https://www.zhihu.com/question/19801131


实例1: 微信小程序中的回调函数(旧文档链接已失效):https://developers.weixin.qq.com/miniprogram/dev/api/device.html 新文档链接:https://developers.weixin.qq.com/miniprogram/dev/api/device/network/wx.getNetworkType.html

// 旧文档写法
wx.getNetworkType({
  // 接口调用成功的回调函数
  success: function(res) {
    var networkType = res.networkType
  }
})

// 新文档写法,回调函数简写
wx.getNetworkType({
  // success函数是接口调用成功的回调函数
  // 可以简写成这样:
  success (res) {
    const networkType = res.networkType
  }
})
// wx.onNetworkStatusChange(function callback)
// 监听网络状态变化事件
// 参数
// function callback
// 网络状态变化事件的回调函数

wx.onNetworkStatusChange(function (res) {
  console.log(res.isConnected)
  console.log(res.networkType)
})


实例2: https://github.com/FatliTalk/vue_json_api/blob/gh-pages/index-new-loading%26filter.html

// --snip--
// 用 Vue.js 实现 filter 过滤器效果
filteredList:function () {
    function useRuler(people) {
        return people.height > 100;
    // 此处 } 后不能有逗号,
    }
    // 此处的useRuler是回调函数,作为参数传入到另一个函数filter中。
    // 可以简单理解为filtet()过滤函数的执行需要一个过滤的标准,useRuler()这个回调函数就是这个标准:身高大于100
    var newList = this.comments.filter(useRuler)
    return newList;
},
// --snip--


实例3:JavaScript 同步回调/异步回调




附一:箭头函数

ES6(ECMAScript 2015)允许使用“箭头” => 定义函数。

1. 关于箭头函数

参考:

箭头函数的渊源可以追溯到上古时期一个叫 Lambda 演算的东西。 数学家们喜欢用纯函数式编程语言,纯函数的特点是没有副作用,给予特定的输入,总是产生确定的输出,甚至有些情况下通过输出能够反推输入。要实现纯函数,必须使函数的执行过程不依赖于任何外部状态,整个函数就像一个数学公式,给定一套输入参数,不管是在地球上还是火星上执行都是同一个结果。 箭头函数要实现类似纯函数的效果,必须剔除外部状态。所以当你定义一个箭头函数,在普通函数里常见的 this、arguments、caller 在箭头函数是没有的。


2. 微信小程序中使用箭头函数

参考:

  1. ES6 中增加了箭头表达式,效果和匿名函数相似,但箭头表达式更为简练,且内部执行时的 this 与外侧一致,不再需要每次都额外增加变量引用了。

  2. 微信小程序里,对每个页面编写的代码逻辑,都作为生命周期钩子函数(如:onLoad, onShow, onUnload)和自定义函数(如:各类组件回调函数)写在 AppService 内。

  3. 这两种函数内,this 都指向当前 Page 对象,在这些函数里做的各种异步操作,回调内的 this 基本都应该仍然保持为当前 Page 对象。在这个情况下,使用箭头表达式可以减少重复的工作、也减少遗漏 this 时出错的几率。

// ES5
var net = require('../public/net');
Page({
    data: {
        list: []
    },
    onShow: function () {
        var self = this;
        net.get('/Index/getList', function (res) {
            res = res || {};
            var status = res.status,
                data = res.data,
                list = data.list;
            if(status == 2) {
                self.setData({list: list});
            }
        });
    }
});

// ES6
import * as net from '../public/net';
Page({
    data: {
        list: []
    },
    onShow: function () {
        net.get('/Index/getList', (res = {}) => {
            let {status, data: {list}} = res;
            if (status == 2) {
                // 此处 this 的指向与 onShow 内一致,无需增加 self 变量
                this.setData({list});
            }
        });
    }
});

再举一个例子,通过微信小程序提供的API wx.getSystemInfo(Object object) 获取屏幕高度。 API文档:https://developers.weixin.qq.com/minigame/dev/api/base/system/system-info/wx.getSystemInfo.html

// ES5
Page({
  data: {
    windowHeight: 0
  },
  onLoad() {
    let _this = this;
    wx.getSystemInfo({
      success: function(res) {
        _this.setData({windowHeight: res.windowHeight});
      }
    });
  }
});

// ES6
Page({
  data: {
    windowHeight: 0
  },
  onLoad() {
    wx.getSystemInfo({
      // 这里的success函数是接口调用成功的回调函数,
      // 并且这里的箭头=>不能省略,不能写成 success (res) {}
      success: (res) => {
        this.setData({windowHeight: res.windowHeight});
      }
    });
  }
});

3. 箭头函数使用场景

  1. 箭头函数适合于无复杂逻辑或者无副作用的纯函数场景下,例如用在 map、reduce、filter 的回调函数定义中;
  2. 不要在最外层定义箭头函数,因为在函数内部操作 this 会很容易污染全局作用域。最起码在箭头函数外部包一层普通函数,将 this 控制在可见的范围内;
  3. 箭头函数最吸引人的地方是简洁。在有多层函数嵌套的情况下,箭头函数的简洁性并没有很大的提升,反而影响了函数的作用范围的识别度,这种情况不建议使用箭头函数。




附二:Javascript 方法

参考:

在一个对象中绑定函数,称为这个对象的方法。

在JavaScript中,对象的定义是这样的:

var student = {
    name: 'Fatli',
    birth: 1990
};

但是,如果我们给 student 绑定一个函数,就可以做更多的事情。比如,写个 age() 方法,返回 student 的年龄:

var student = {
    name: 'Fatli',
    birth: 1996,
    age: function () {
        var y = new Date().getFullYear();
        return y - this.birth;
    }
};

console.log(student.age);    //输出fatli.age的函数体内容
console.log(student.age()); //23