JavaScript 中的 Function

一、Function 是什么?

 
在 JavaScript 中,函数是一等公民,而 Function 是所有函数的构造函数(所有函数本质上都是 Function 的实例)。你可以把它理解为:
 
  • 普通函数:function fn() {}new Function() 的语法糖
  • 函数本身是对象:可以像对象一样添加属性、作为参数 / 返回值
 

二、Function 的核心用法

 

1. 定义函数的 3 种方式

 
方式 1:函数声明(最常用)
// 语法:function 函数名(参数) { 逻辑 }
function sum(a, b) {
  return a + b;
}
console.log(sum(1, 2)); // 输出:3
  • 特点:有函数提升(可以在声明前调用)
 
方式 2:函数表达式
// 语法:const 变量名 = function(参数) { 逻辑 }
const multiply = function(a, b) {
  return a * b;
};
console.log(multiply(2, 3)); // 输出:6
  • 特点:无函数提升(必须先声明后调用),可定义匿名函数
 
方式 3:new Function () 构造函数(底层方式)
// 语法:new Function(参数1, 参数2, ..., 函数体字符串)
const subtract = new Function('a', 'b', 'return a - b');
console.log(subtract(5, 2)); // 输出:3

特点:

    • 函数体以字符串形式传入,执行时会重新解析(性能较差)
    • 作用域是全局作用域(而非当前作用域),慎用

在 JS 中,所有函数都是 Function 的实例每个函数都继承自 Function.prototype,因此拥有如 .call().apply().bind() 等方法。

2. Function 的核心特性

 
(1)函数的属性和方法
 
所有函数都继承了 Function.prototype 的属性 / 方法:
function fn(a, b) {
  return a + b;
}

// 核心属性
console.log(fn.name); // 输出:fn(函数名)
console.log(fn.length); // 输出:2(形参个数)
console.log(fn.prototype); // 输出:{ constructor: fn }(原型对象)

// 核心方法
const fn2 = fn.bind(null, 10); // 绑定参数,返回新函数
console.log(fn2(20)); // 输出:30

fn.call(null, 1, 2); // 立即执行,参数逐个传入
fn.apply(null, [1, 2]); // 立即执行,参数以数组传入

运行结果如下

image

(2)函数的作用域与闭包
 
    • 作用域:函数内可访问自身变量、外层变量(词法作用域),外层无法访问函数内变量
    • 闭包:函数嵌套时,内层函数可以访问外层函数的变量,即使外层函数执行完毕
function outer() {
  const num = 10;
  // 内层函数形成闭包,保留对 num 的引用
  return function inner() {
    console.log(num);
  };
}

const innerFn = outer();
innerFn(); // 输出:10(外层函数已执行,但 num 仍可访问)
(3)箭头函数(Function 的简化形式)
 
ES6 新增的箭头函数是 Function 的语法糖,但有特殊规则:
// 普通函数 vs 箭头函数
const normalFn = function(a, b) {
  return a + b;
};
const arrowFn = (a, b) => a + b; // 单行可省略 {} 和 return

// 箭头函数的特殊点:
// 1. 没有自己的 this(继承外层 this)
// 2. 没有 arguments 对象
// 3. 不能作为构造函数(不能 new)

3. Function 的进阶用法

 
(1)作为参数(回调函数)
// 数组遍历中传入函数作为参数
const arr = [1, 2, 3];
arr.forEach(function(item) {
  console.log(item); // 输出:1 2 3
});

(2)作为返回值(高阶函数)

function createAdder(num) {
  // 返回一个函数,动态生成加法逻辑
  return function(x) {
    return x + num;
  };
}

const add5 = createAdder(5);
console.log(add5(3)); // 输出:8

三、Function 的可覆盖性

Function 本质上是挂载在全局对象(浏览器中是 window,Node.js 中是 global)上的一个属性,而全局属性默认是可写的,所以你可以直接重新赋值覆盖它。

先看一个普通函数的覆盖写法,代码如下

function greet(name) {
  console.log("Hello, " + name + "!");
}

// 调用原函数
greet("Alice"); // 输出: Hello, Alice!

// 2. 覆盖重写这个函数(使用函数表达式)
greet = function(name, greeting = "Hi") {
  console.log(greeting + ", " + name + "!");
};

// 再次调用
greet("Bob");           // 输出: Hi, Bob!
greet("Charlie", "Hey"); // 输出: Hey, Charlie!

运行结果如下

image

🔍 说明:

  • 原函数 greet(name) 只接受一个参数。
  • 覆盖后的函数 greet(name, greeting = "Hi") 接受两个参数,并为第二个参数设置了默认值(ES6 特性)。

下面 ,再看一下将普通函数改造为构造函数并使用new的情况

 

// 1. 原始的普通函数
function greet(name) {
  console.log("Hello, " + name + "!");
}

greet("Alice"); // Hello, Alice!

// 2. 用 new 调用会出问题(不推荐,但 JS 允许)
let jiaGreet = new greet("Liudehua"); // 也会执行,但返回一个空对象,log 依然会打印,逻辑混乱
console.log('jiaGreet is :',jiaGreet)

// 3. 重写 greet 为构造函数(这才是“使用 new 的重写”)
greet = function(name) {
  // 如果被当作构造函数调用(即用 new),this 指向新创建的对象
  this.name = name;
  this.sayHello = function() {
    console.log("Hello, " + this.name + "!");
  };
};

// 现在用 new 调用
const person = new greet("Bob");
person.sayHello(); // Hello, Bob!
console.log('greet is :',greet);

// 直接调用(不推荐,会导致 this 指向全局对象,在严格模式下会报错)
// 经chenlight实测,在浏览器和node环境下均没有输出任何内容;
// greet("Charlie"); // 不安全!

运行结果如下

image

image

有了上面普通函数重写覆盖以及普通函数改造为构造函数的示例,下面我们看一下Function函数的覆盖重写的情况:

1. 基础覆盖示例

// 保存原始的 Function 构造函数(避免完全丢失)
const OriginalFunction = Function;

// 覆盖全局的 Function
Function = function(...args) {
  console.log("自定义 Function 被调用了!");
  // 调用原始 Function 创建真正的函数(核心逻辑)
  const fn = new OriginalFunction(...args);
  // 可以给新函数添加自定义逻辑/属性
  fn.customProp = "自定义属性";
  return fn;
};

// 测试覆盖后的效果
const myFn = new Function('a', 'b', 'return a + b');
console.log(myFn(1, 2)); // 输出:3
console.log(myFn.customProp); // 输出:自定义属性

运行结果如下

image

 

2. 极端覆盖(完全替换)

 
⚠️ 注意:这种方式会破坏所有函数的创建逻辑,严禁在生产环境使用
 
// 完全替换 Function,不再调用原始构造函数
Function = function() {
  return function() {
    console.log("所有新函数都被我接管了!");
  };
};

// 测试:所有新函数都会执行自定义逻辑
const testFn = new Function();
testFn(); // 输出:所有新函数都被我接管了!

// 甚至函数表达式/声明也会受影响吗?❌ 重点:
// function 声明/表达式 是语法层面的,不会受覆盖后的 Function 影响
function normalFn() {
  console.log("我是普通函数声明,不受影响");
}
normalFn(); // 输出:我是普通函数声明,不受影响
运行如下
image
 

覆盖 Function 的核心影响

 

1. 受影响的场景

 
    • 通过 new Function() 创建的函数,会执行你自定义的逻辑;
    • 一些动态生成函数的库(如模板引擎、动态编译工具),如果底层用了 new Function(),会被影响。
 

2. 不受影响的场景

 
    • 函数声明function fn() {}(语法糖,底层不依赖全局 Function);
    • 函数表达式const fn = function() {}(同上);
    • 箭头函数const fn = () => {}(ES6 语法,独立于 Function 构造函数);
    • 内置函数:Array.prototype.forEachObject.keys 等(引擎内置,不受全局 Function 影响)。

四、合理的替代方案(而非直接覆盖)

 
如果你想对函数创建做统一处理,更安全的方式是:封装自定义函数创建工具,而非覆盖全局 Function
// 推荐:封装自定义函数创建方法
function createCustomFunction(...args) {
  const fn = new Function(...args);
  // 统一添加自定义逻辑
  fn.logCall = function() {
    console.log(`函数 ${fn.name} 被调用了`);
    return fn.apply(this, arguments);
  };
  return fn;
}

// 使用自定义工具创建函数
const add = createCustomFunction('a', 'b', 'return a + b');
console.log(add(4,5));  // 输出:9
console.log(add.logCall(2, 3)); // 输出:函数  被调用了 → 5
运行结果如下
image
 
posted @ 2026-01-30 19:36  chenlight  阅读(4)  评论(0)    收藏  举报