LOADING

函数

2022/1/23

介绍

使用函数切割流程,不仅可以减少重复代码、还可以有效的降低整体复杂度,增加可维护性
函数具有三要素:函数名、参数、返回值

函数包括
参数:表示完成流程所需的必要信息
返回值:表示完成流程后产生的结果
函数体:表示具体的流程

  1. 定义函数时,只需要考虑这个函数如何实现即可,完全不需要考虑其他无关的东西。
  2. 调用函数时,只需要考虑向其传递什么参数,如何使用它的返回结果,完全无需考虑函数的具体实现。

函数注释

/\*\*

- 得到某个数的阶乘
- 如果数小于了 1,则得到 0
- @param {number} n 要求阶乘的数
- @return {number} 阶乘结果
  \*/

image.png
调用的时候这样就可以有提示

函数也是一个对象,每个函数都有一个属性 prototype(原型),往往构造函数才会用他,其他也有,但不使用

function m() {}

m.a = 1;

函数定义方式

//变量保存函数,函数表达式(本质是匿名函数)
var b = function () {};

//函数声明,使用函数声明可以将代码提到顶部
function c() {

}

构造函数

当我们需要通过函数去创建一个对象,通常使用构造函数

    function Person(name , age , gender){
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.sayName = function(){
            alert(this.name);
        };
    }
//调用Person
    person_obj = new Person('张三',18,'男');
    person_obj.name

普通函数是直接调用,而构造函数需要使用 new 关键字来调用

构造函数与普通函数相比只是少了定义 obj 和返回 obj,以及把 obj 改为了 this

function Person(firstName, lastName) {
  // var this = {};

  this.firstName = firstName;
  this.lastName = lastName;
  this.fullName = firstName + lastName;
  this.sayHi = function () {
    console.log("我的名字叫做:" + this.fullName);
  };

  // return this;
}

JS 所有的对象,都是通过构造函数产生的

对象,数组,函数

// 语法糖
// var obj = {
//   a: 1,
//   b: 2,
// };

// var obj = new Object(); // 创建一个空对象
// obj.a = 1;
// obj.b = 2;

// console.log(obj);

// var arr = [1, 2, 3];

// var arr = new Array(1, 2, 3); // 创建一个数组

// console.log(arr);

function sum(a, b) {
  return a + b;
}

// var sum = new Function('a', 'b', 'return a+b');

console.log(sum(1, 2));

回调函数

如果需要获取一个函数中异步操作的结果,则必须通过回调函数来获取

    function fn(callback) {
        setTimeout(function() {
            var data = 'hello'
            callback(data)
        }, 1000)
    }

    fn(function(data) {
        console.log(data)
    })

如果想取到 data 的数据进行处理,就只能进行回调函数,因为定时器是异步的,函数不会等待执行完毕就直接结束了,只有在调用的时候传递一个函数给里面的定时器才行


1). 你定义的
2). 你没有调
3). 但最终它执行了(在某个时刻或某个条件下)

实例
数组过滤函数,通过回调可以使得使用时十分灵活
image.png

如果箭头函数函数体只有一句话,那么这个句话可以不带大括号,而且这句话就是返回值(可以不用写 return)

立即执行函数和匿名函数

匿名函数

应用场景

1、事件

var sub = document.querySelector("#sub");
//给按钮增加点击事件。
sub.onclick=function(){
    alert("当点击按钮时会执行");
}

2、对象

var obj = {
    name:"lynn",
    age:18,
    fn:function(){
        return "我叫"+this.name+"今年"+this.age+"岁了!";
    }
};

console.log(obj.fn());//我叫lynn今年18岁了!

3、函数表达式

var fn = function(){
    return "2020"
}

立即执行函数

IIFE,其全称为 immediately invoked function expression,即“立即调用的函数表达式”:

立即执行函数就是声明一个匿名函数,并马上调用这个匿名函数

立即执行函数的用途
创建一个独立的作用域,这个作用域里面的变量,外面访问不到(即避免”变量污染”)

立即执行函数往往只会执行一次

//两种写法
(function() {
    // ...
})();

(function() {
    // ...
}());


(function(a,b){
  console.log("a = "+a);
  console.log("b = "+b);
})(123,456);


let a = (function() {
    // ...
    return "ad";
}());

这样可以把立即执行函数里面的数据返回出来一部分你所需要的

闭包函数

当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时, 就产生了闭包
函数嵌套
内部函数引用了外部函数的数据(变量/函数)

function fn1 () {
  var a = 2
  var b = 'abc'
  function fn2 () { //执行函数定义就会产生闭包(不用调用内部函数)
      console.log(a)
  }
  // fn2()
}

function showDelay(msg, time) {
  setTimeout(function () {
      alert(msg)
  }, time)
}

作用

  • 使用函数内部的变量在函数执行完后, 仍然存活在内存中(延长了局部变量的生命周期)
  • 让函数外部可以操作(读写)到函数内部的数据(变量/函数)

比如希望第一次调用和第二次调用相同函数拥有不一样的返回结果

缺点

  • 函数执行完后, 函数内的局部变量没有释放, 占用内存时间会变长
  • 容易造成内存泄露

解决

  • 能不用闭包就不用
  • 及时释放
 f = null //让内部函数成为垃圾对象-->回收闭包

闭包是什么就不解释了,直接写一个闭包函数:

var f = function () {
  var num = 0;
  return function () {
    return (num += 1);
  };
};

js 中,函数是一等公民,定义一个函数 f,它返回另一个可执行函数function() { return num += 1; };

js 中的作用域,都是一层一层向上找的,在 f 内部函数里面,他的 num 向上找到父函数的作用域。
现在,我们执行一下:

f()()
// 1
f()()
// 1
????? 感觉 不太对?为什么num没有自增呢??
  • 内存回收机制
    为什么上面执行结果不太对,因为执行f()()后,f 函数已经执行完毕了,没有其他资源引用 f,ta 会被立即释放,也就是说,f()()执行完后,立即就释放了。
    如何才不释放呢?
// 创建f的引用
var fn = f();
fn()
// 1
fn()
// 2
fn()
// 3

这下就对了,num 成了私有变量,f 拥有了私有作用域。

完了吗?
f 有了 fn 的引用,内存一直得不到释放,咋办呢?这样的函数多了是不是会造成内存溢出?
手动释放一下:

var fn = f();
fn()
// 1
fn()
// 2
fn()
// 3
// 手动释放f的引用
fn = null
// f的引用fn被释放了,现在f的作用域也被释放了。num再次归零了。
var fn = f()
fn()
// 1

箭头函数

如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。
箭头函数体内的 this 对象,就是定义时所在的对象,而不是使用时所在的对象。

var f = () => 5;
// 等同于
var f = function (){
    return 5
};

var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
    return num1 + num2;
}

函数方法

我们所创建的每一个函数,解析器都会向函数中添加一个属性 prototype
这个属性对应着一个对象,这个对象就是我们所谓的原型对象
原型对象中有一个属性 constructor, 它指向函数对象

console.log(Date.prototype.constructor === Date)