Skip to content

函数

JS 中函数是核心概念,函数最初通过 function 声明创建,ES6 新增了箭头函数。

  • 参数:通过 arguments 对象访问 (箭头函数除外),函数的所有参数都是值传递
  • 返回值:默认为 undefined (也就是 void(0)), 可自定义返回值

具名函数

js
console.log(a); // 打印:Uncaught ReferenceError: a is not defined ❌

console.log(b); // 打印:ƒ b() {} ✅ 函数有声明提升能力

console.log(b()); // 打印:b() 声明前执行成功 flag ✅ 函数允许声明前调用 b()

function b() {
    console.log("b() 声明前执行成功");
}
// 函数支持多次声明
function b() {
    console.log("b() 声明前执行成功 flag");
}
console.log(a); // 打印:Uncaught ReferenceError: a is not defined ❌

console.log(b); // 打印:ƒ b() {} ✅ 函数有声明提升能力

console.log(b()); // 打印:b() 声明前执行成功 flag ✅ 函数允许声明前调用 b()

function b() {
    console.log("b() 声明前执行成功");
}
// 函数支持多次声明
function b() {
    console.log("b() 声明前执行成功 flag");
}

匿名函数

js
console.log(c); // 打印:undefined 💡 var 变量声明提升
var c = function () {};
c(); // 只能在声明后调用函数
console.log(c); // 打印:undefined 💡 var 变量声明提升
var c = function () {};
c(); // 只能在声明后调用函数

箭头函数

js
// 语法
(() => 1)();
var arrow = () => {};
var arrow = () => 1;
var arrow = () => [];
var arrow = () => ({ a: 1 });
// prettier-ignore
var arrow = a => a;
var arrow = ({ a }) => a;
var arrow = (...args) => args;

// ===== 箭头函数的 this =====
function b() {
    console.log(this);
    return () => console.log(this);
}
const c = () => console.log(this);
const d = b.call({ this: 1 }); // 打印:{ this: 1 }
d(); // 打印:{ this: 1 } ✅ 尖头函数的 this 是定义时外部上下文的 this
c.call({ this: 1 }); // 打印:Window ✅ 尖头函数本身没有 this, call 无效
// 语法
(() => 1)();
var arrow = () => {};
var arrow = () => 1;
var arrow = () => [];
var arrow = () => ({ a: 1 });
// prettier-ignore
var arrow = a => a;
var arrow = ({ a }) => a;
var arrow = (...args) => args;

// ===== 箭头函数的 this =====
function b() {
    console.log(this);
    return () => console.log(this);
}
const c = () => console.log(this);
const d = b.call({ this: 1 }); // 打印:{ this: 1 }
d(); // 打印:{ this: 1 } ✅ 尖头函数的 this 是定义时外部上下文的 this
c.call({ this: 1 }); // 打印:Window ✅ 尖头函数本身没有 this, call 无效

箭头函数的特点

  • 可以直接返回一条语句
  • 直接返回对象需要使用括号包起来
  • 用来简化回调函数
  • 没有自己的 this,内部 this 默认指向定义时上下文的 this,是固定不可变得, 也就不可以作为构造函数
  • 没有 arguments 对象,可以使用参数扩展运算符代替
  • 不可以使用 yield 命令,因此箭头函数不能用作 Generator 函数。
  • 不适合用作定义对象属性的函数,不适合作为 DOM 事件监听回调函数
  • 用于数学运算(阶乘等),简化方程式

立即执行函数

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

js
(() => 1)(); // 返回 1
(function () {
    console.log("立即执行函数"); // 打印:立即执行函数
})();
(() => 1)(); // 返回 1
(function () {
    console.log("立即执行函数"); // 打印:立即执行函数
})();

闭包

当一个内部函数被外部函数包裹并作为返回值返回,即便外部函数执行完毕, 它依然保持对外部函数作用域的引用,访问外部函数的变量,使其不被垃圾回收。

这种形式我们称之为闭包,主要用于封装私有变量、实现模块化、延迟函数的执行等。

js
const closure = (function () {
    this.a = "上下文变量";
    const b = "私有变量";
    console.log("立即执行函数,创建私域,返回值内部函数,创建闭包");
    return function () {
        console.log("闭包持有定义时上下文引用:%s %s", this.a, b);
        return { a: this.a, b };
    };
})();
// 打印:立即执行函数,创建私域,返回值内部函数,创建闭包
let res = closure();
// 打印:闭包持有定义时上下文引用:上下文变量 私有变量
console.log(res);
// 打印: {a: '上下文变量', b: '私有变量'}
const closure = (function () {
    this.a = "上下文变量";
    const b = "私有变量";
    console.log("立即执行函数,创建私域,返回值内部函数,创建闭包");
    return function () {
        console.log("闭包持有定义时上下文引用:%s %s", this.a, b);
        return { a: this.a, b };
    };
})();
// 打印:立即执行函数,创建私域,返回值内部函数,创建闭包
let res = closure();
// 打印:闭包持有定义时上下文引用:上下文变量 私有变量
console.log(res);
// 打印: {a: '上下文变量', b: '私有变量'}

函数参数

箭头函数不支持 arguments 对象

js
function a(a) {}
function a(a, b) {}
function a(a = 1, b = 2) {}
function a({ a, b }) {}
function a(...args) {}
function a(a, ...rest) {}
function a() {
    var args = [...arguments];
}

// ===== 示例1 =====
const a = { num: 1 };
function fn(a) {
    console.log(fn.name); // fn
    console.log(fn === arguments.callee); // true
    console.log(JSON.stringify([a])); // [{"num":1}]
    console.log(JSON.stringify([...arguments])); // [{"num":1}]
    a.num = 2;
    console.log(a.num); // 2
    a = { num: 3 };
    console.log(a.num); // 3
}
fn(a);
console.log(a); // {num: 2}

// ===== 示例2 =====
const a = { num: 1 };
function fn(a) {
    a.num = 2;
    function a() {}
    console.log(a); // ƒ a() {}
    console.log(a.num); // 2
}
fn(a);
console.log(a); // { num: 1 }
function a(a) {}
function a(a, b) {}
function a(a = 1, b = 2) {}
function a({ a, b }) {}
function a(...args) {}
function a(a, ...rest) {}
function a() {
    var args = [...arguments];
}

// ===== 示例1 =====
const a = { num: 1 };
function fn(a) {
    console.log(fn.name); // fn
    console.log(fn === arguments.callee); // true
    console.log(JSON.stringify([a])); // [{"num":1}]
    console.log(JSON.stringify([...arguments])); // [{"num":1}]
    a.num = 2;
    console.log(a.num); // 2
    a = { num: 3 };
    console.log(a.num); // 3
}
fn(a);
console.log(a); // {num: 2}

// ===== 示例2 =====
const a = { num: 1 };
function fn(a) {
    a.num = 2;
    function a() {}
    console.log(a); // ƒ a() {}
    console.log(a.num); // 2
}
fn(a);
console.log(a); // { num: 1 }

this

this 的值取决于它出现的上下文:函数全局

  • 在方法中,this 表示该方法所属的对象。
  • 如果单独使用,this 表示全局对象。
  • 在函数中,this 表示全局对象。
  • 在函数中,在严格模式下,this 是未定义的(undefined)。
  • 在事件中,this 表示接收事件的元素。
  • 类似 call() 和 apply() 方法可以将 this 引用到任何对象。
  • bind 方法来设置函数的 this 值,而不用考虑函数如何被调用的。
  • 箭头函数不提供自身的 this 绑定(this 的值将保持为闭合词法上下文的值)。
  • 在非严格模式下,this 总是指向一个对象,在严格模式下可以是任意值。
js
var name = 0;
var obj = {
    name: 3,
    fn() {
        console.log(this.name);
        return function () {
            console.log(this.name);
        };
    },
};
const fn = obj.fn;
obj.fn()(); // 3 0
fn()(); // 0 0
fn.call({ name: 4 })(); // 4 0
fn.apply({ name: 5 })(); // 5 0
const bindFn = fn.bind({ name: 6 });
bindFn()(); // 6 0
var name = 0;
var obj = {
    name: 3,
    fn() {
        console.log(this.name);
        return function () {
            console.log(this.name);
        };
    },
};
const fn = obj.fn;
obj.fn()(); // 3 0
fn()(); // 0 0
fn.call({ name: 4 })(); // 4 0
fn.apply({ name: 5 })(); // 5 0
const bindFn = fn.bind({ name: 6 });
bindFn()(); // 6 0

函数作用域

作用域是当前的执行上下文,值和表达式在其中“可见”或可被访问。作用域也可以堆叠成层次结构,子作用域可以访问父作用域,反过来则不行。

字符串函数

js
// new Function(functionBody)
// new Function(arg0, functionBody)
// new Function(arg0, arg1, functionBody)
// new Function(arg0, arg1, /* …, */ argN, functionBody)

// 字符串创建一个函数
const adder = new Function("a", "b", "return a + b");
console.log(adder(1, 2)); // 3

// 运行一个函数字符串
const fnStr = adder.toString();
const runFnStr = (...args) => {
    let fnStr = args.pop();
    return new Function(`return (${fnStr})(${args.join(",")})`)();
};
console.log(runFnStr(1, 2, adder.toString())); // 3

// 运行一个 jsFile
let jsFile = "function adder(a, b) { return a + b }; console.log(adder(1, 2));";
new Function(`return (()=>{${jsFile}})()`)();
// new Function(functionBody)
// new Function(arg0, functionBody)
// new Function(arg0, arg1, functionBody)
// new Function(arg0, arg1, /* …, */ argN, functionBody)

// 字符串创建一个函数
const adder = new Function("a", "b", "return a + b");
console.log(adder(1, 2)); // 3

// 运行一个函数字符串
const fnStr = adder.toString();
const runFnStr = (...args) => {
    let fnStr = args.pop();
    return new Function(`return (${fnStr})(${args.join(",")})`)();
};
console.log(runFnStr(1, 2, adder.toString())); // 3

// 运行一个 jsFile
let jsFile = "function adder(a, b) { return a + b }; console.log(adder(1, 2));";
new Function(`return (()=>{${jsFile}})()`)();

函数是方法吗?

方法是一种特殊类型的函数,它是与对象关联的函数。 方法定义在对象的属性中,并且可以通过对象来调用。 在方法内部,关键字 this 引用的是调用该方法的对象本身。

相关资料

༼ つ/̵͇̿̿/’̿’̿ ̿ ̿̿ ̿̿◕ _◕ ༽つ/̵͇̿̿/’̿’̿ ̿ ̿̿ ̿̿ ̿̿