Skip to content
On this page

函数相关

函数是将复用的代码块封装起来的模块,在JS中函数

函数声明

Function声明

在JS中,函数也是对象,是Function类的创建实例,下面的例子可以方便理解函数是对象。

javascript
let fn = new Function("title", "console.log(title)");

fn("标题");

标准语法声明

是同 function 声明

javascript
function fn(title) {
  console.log(title);
}

fn("标题");

匿名函数赋值声明

javascript
const fn = function(title) {
  console.log(title);
}

fn('标题');

对象中声明函数

javascript
let user = {
  name: null,
  getName: function() {
    // this 指向当前调用者,即user对象
    return this.name
  },
  // 简写
  setName(value) {
    this.name = value
  }
}

user.setName("张三");
console.log(user.getName());

函数声明中需要注意的点!

  • 🧠 varfunction 式声明函数都会将函数压入window对象中,所以在声明时需要避免污染window对象中的api。使用 letconst 则不会, 这是语言的前期缺陷造成的。
javascript
function a() {
  console.log("函数A");
}

var b = function() {
  console.log("函数B");
}

let c = function() {
  console.log("函数C");
}

const d = function() {
  console.log("函数D");
}

console.log(window.a()); // 函数A
console.log(window.b()); // 函数B
console.log(window.c()); // window.c is not a function
console.log(window.d()); // window.d is not a function
  • 🧠 标准声明 function 会变量提升,js解析器会优先提取函数并放在代码树顶端,所以标准函数的声明可以不限位置。当然,不建议这样做,这也是前端js语言的缺陷造成的。

例子1:🌰

javascript
console.log(a()); // 函数A
function a() {
  console.log("函数A");
}

例子2:🌰 var 声明的函数不会提升到顶端,所以运行优先级低于function

javascript
console.log(a()); // 函数A

// function a() {} 在解析时直接提升到最顶端
function a() {
  console.log("函数A- function");
}

// var 变量提升
// 运行到这里在 将function() {} 赋值给变量a
var a = function() {
  console.log("函数A- var ");
}
  • 🧠 匿名函数自执行。在es6之前,常使用匿名函数自执行,形成单独的作用域,避免污染全局。

基本语法

javascript
(function() {
  console.log("匿名函数自执行");
})(); // 匿名函数自执行

避免污染全局

javascript
(function(window) {
  var a = "这是变量a的值";

  // 手动挂载到window
  window.globalA = { a };
})(window);

函数参数

形参和实参

形参是在函数声明时设置的参数;实参是指在调用函数时传递的值。

javascript
// 此时 a 和 b 为形参
function sum(a, b) {
  return a + b;
}

// 1 和 2 为 实参
sum(1,2);

// 1. 形参数量大于实参时,没有传值的形参值为undefined
// 2. 实参数量大于形参时,多余的实参将会被忽略且不会报错

默认参数赋值

javascript
// 方式一:
function sum(a, b) {
  a = a || 1;
  b = b || 1;
  return a + b;
}

// 简化方式二:
function sum(a = 1, b = 2) {
  return a + b;
}

回调函数

函数可以作为另一个函数的参数传入。常见的如 .map.foreachaddEventListener 等等。

作为入参的函数被称为 回调函数 调用入参函数的函数被称为 高阶函数

javascript
// .filter这类的函数就被称为高阶函数
let newArr = [1, 2, 3, 4, 5].filter(v=> {
  return v <= 3;
});

console.log(newArr); // [1, 2, 3]

arguments

arguments是函数获得所有参数的集合

javascript
// ... 聚合参数
function sum(...arguments) {
  return arguments.reduce((a, b) => a + b);
};

sum(1,2,3,4);

展开语法作为入参的应用

展开语法 ... 做值时是放,作为接收变量时是收。

javascript
let arr = [1, 2, 3];

// 解构赋值
let [a, b, c] = [...arr];  // 左侧赋值; 右侧浅拷贝
console.log(`a:${a};b:${b};c:${c}`); // a:1;b:2;c:3

// 作为变量接收
[...arr] = [1,2,3,4,5]; // 收值
console.log(arr); // [1,2,3,4,5]

使用展开语法可以替代 arguments 来接收任务数量的参数, 并将其合并为数组

javascript
function fn(...args) {
  console.log(args);
}

fn(1,2,3, '测试展开语法'); // [1, 2, 3, '测试展开语法']

当多个参数时, 必须将展开语法放在最后一位, 不然后置位入参则为 undeined,原因是会将当前位置之后的所有实参都会收集起来。

javascript
function sum(discount = 0, ...prices) {
  let total = prices.reduce((pre, cur) => pre + cur);
  return total * (1 - discount);
}

console.log(sum(0.1, 100, 300, 299)); //

递归调用

递归指函数内部调用自身的方式。

  • 主要用于数量不确定的循环操作
  • 必须要有退出判断否则会陷入死循环

例1🌰:阶乘递归调用

javascript
function factorial(num = 3) {
  return num <= 1 ? num : num * factorial(--num);
}

console.log(factorial(5)); // 120

例2🌰: 累加算法

javascript
function sum(...num) {
  return num.length == 0 ? 0 : num.pop() + sum(...num)
}

console.log(sum(1, 5,  6, 5, 9))

例3🌰: 递归输出倒三角

javascript
/**
 * 输出

******
*****
****
***
**
*

*/

function star(row = 5) {
  if (row === 0) return '';
  document.write('*'.repeat(row) + '<br />');
  star(--row);
}

标签函数

使用函数来解析标签字符串,第一个参数是字符串值得数组,其余得参数作为标签变量

javascript
function returnAbout(strs, ...values) {
  console.log(strs); // ['三国著名人物:', '-', raw: Array(3)]
  console.log(values); // ['卧龙诸葛', '诸葛亮']
}

let nickName = '卧龙诸葛',name = '诸葛亮';

returnAbout `三国著名人物:${nickName}-${name}`

this指向

调用函数时 this 会隐式传递给函数,指向函数调用时得关联对象,也称之为函数得上下文。

全局环境下 this 就是window对象的引用

javascript
console.log(this === window);  // true

使用严格模式时,在全局函数内 thisundefined

javascript
var grilFired = '玛丽亚'

function get() {
  "use strict"
  return this.grilFired;
}

console.log(get()); // Cannot read property 'name' of undefined

方法调用时的this指向:函数为对象的方法时 this 指向该对象。可以使用多种方式创建对象

  • 构造函数
javascript
function User() {
  this.name = "张三";
  this.getName = function() {
    console.log(this); // User {name: "张三", getName: f}
    return this.name;
  }
}

let personal = new User()
console.log(personal.getName()); // 张三
  • 对象字面量
javascript
let personal = {
  name: "张三",
  getName() {
    console.log(this); // personal {}
    function getFistrName() {
      console.log(this); // window
    }

    // 不属于对象方法所以指向 `window`
    getFistrName()
  }
}

personal.getName()

可以在父作用域中定义引用this的变量

javascript
let personal = {
  name: "张三",
  getName() {
    console.log(this); // personal {}
    const _self = this
    function getFistrName() {
      console.log(_self); // personal {}
    }
    getFistrName()
  }
}

personal.getName()

在使用某些函数时,可以直接传this进去

javascript
let Lesson = {
  site: '叮当网',
  lists: ['js', 'css', 'html'],
  show() {
    return this.lists.map(function(title) {
      return `${this.state}-${title};`
    }, this);
  }
}

箭头函数

箭头函数是ES6推出的新的函数声明的简写形式。

javascript
// 标准格式
const sum = () => {
  console.log('sum函数执行');
}

// 只有一个参数时,可以省略括号
const printA = word => {
  console.log(`输出的是:${word}`);
}

// return 的函数体只有一句时, 可以省略 {}
const sun = (a, b) => a + b

箭头函数没有this,也可以理解为箭头函数中的 this 会继承定义函数时的上下文,可以理解为和外层函数指向同一个this。

javascript
let personal = {
  name: "张三",
  getName() {
    console.log(this); // personal {}
    const getFistrName = () => {
      console.log(this); // personal {}
    }
    getFistrName()
  }
}

personal.getName()

apply/call/bind

改变 this 指针,也可以理解为对象借用方法。

例子1🌰: 构造函数中的 this 默认是一个空对象,然后构造函数处理后把这个空对象变得有值

javascript
function User(name) {
  this.name = name;
}

let hd = mew User('张三');

例子2🌰:可以改变构造函数中的空对象,即让构造函数 this 指向到另一个对象。

javascript
function User(name) {
  this.name = name;
}

let Worker = {};

User.call(Worker, '打工人张三');
console.log(Worker); // {name: '打工人张三'}

apply

用于显示的设置函数的上下文,将对象绑定到显示设置的this上,其用 数组 作为参数。与 bind 不同,apply会立即执行。

语法:

javascript
function show(title) {
    alert(`${title+this.name}`);
}
let wangwu = {
    name: '王五'
};
show.apply(wangwu, ['打灯人']);

例子1🌰:找数组中的最大值

javascript
let arr = [1, 4, 5, 7, 56];

console.log(Math.max(arr)); // NaN
console.log(Math.max.apply(Math, arr)); // 56
console.log(Math.max(...arr)); // 56

call

用于显示的设置函数的上下文,将对象绑定到显示设置的this上,其需要分别传递参数。与 bind 不同,call会立即执行。

javascript
function show(title) {
    alert(`${title+this.name}`);
}
let wangwu = {
    name: '王五'
};
show.call(wangwu, '打灯人');

bind

bind是将函数绑定到某个对象上,比如a.bind(fn)可以理解为将a函数绑定到fn对象上,即fn.a()

  • call/apply 不同,bind不会立即执行
  • bind 是复制函数行为会返回新的函数
javascript
let a = function() {};
let b = a;
console.log(a === b); // true

let c = a.bind();
console.log(a === c); // false

绑定参数

javascript
function fn(a, b) {
  return this.f + a + b;
}

//使用bind生成新函数;  第一位参数传递this指向
let newFn = fn.bind({f: 1}, 3);

// this => {f: 1} ;  a => 3;  b => 2
console.log(newFn(2))