JavaScript Function类型详解:定义、内部属性与方法

作者:闭包旅人时间:2026-10-30 10:08:51浏览:350

本文深入解析JavaScript中的Function类型,涵盖函数声明与表达式两种定义方式及其区别。详细阐述arguments、this、caller、length及prototype等内部属性,并重点讲解call、apply和bind方法在改变函数执行上下文中的作用与差异,帮助开发者掌握函数式编程核心概念。

Function类型概述

Function类型是JavaScript中最基础且核心的概念之一。尽管它是初学者最容易上手的特性,但由于其底层机制复杂,也是JS中最难深入理解和掌握的部分。

在JavaScript中,Function属于引用类型。每个函数本质上都是Function类型的实例,拥有独立的属性和方法。由于函数被视为对象,函数名实际上是指向该函数对象的指针。

函数定义方式

在JavaScript中,定义函数主要有以下几种常用方式。

第一种方式是函数声明。


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

第二种方式是函数表达式。


var sum = function(){
return a+b; 
}; //注意分号
 //两种方式的区别:
 //解释器会率先读取函数声明,并使其在执行之前可以访问,而使用表达式则必须等到解析器执行到它所在的代码行,才会真正被解释执行(变量声明提前,而值留在原地)
alert (sum (10 ,10));
function sum(a ,b){
  return a+b;
}
//↑上面的代码会正常执行,因为在代码执行前,解析器通过函数声明提升,读取并将函数声明添加到执行环境中,放到代码树的顶部
alert (typeof sum);
alert(sum(10 , 10));
var sum = function (a ,b){
  return a+b;
}
//↑报错,原因在于函数位于一个初始化语句中,不是一个函数声明,不会被提前,而只会把var sum提前,用typeof操作符显示sum是undefined,所以报错

由于函数名仅保存指向函数对象的指针,它与其他包含对象指针的变量并无本质区别。这意味着一个函数对象可以被多个变量引用,即拥有多个名称。


function sum(a , b ){
return a+b;
}
console.log(sum(2 ,3)); //5
var anotherSum = sum; //变量anotherSum也指向了同一个函数对象
console.log(anotherSum(4 , 5)); //9
sum = null; //sum变量不再保存函数对象的指针了
console.log(anotherSum(1 , 3)); //anotherSum这个变量仍能调用

JavaScript中的重载问题

需要明确的是,JavaScript语言规范中并不支持传统意义上的函数重载。


function add(a){
return a+3 ;
}
function add(a){
return a+5;
}
var result = add(3); //8
//两个函数同名了,结果只能是后一个函数覆盖前一个,所以不能重载

函数的内部属性

在函数内部,存在两个特殊的内置对象:arguments和this。

首先来看arguments对象。

arguments是一个类数组对象,用于存储传入函数的所有参数。它包含一个名为callee的属性,该属性是一个指针,指向拥有此arguments对象的函数本身。


function foo (){
var a =arguments.callee; 
return a.toString();
}
foo();
/*
返回结果:
  "function sum(){
  var a =arguments.callee; 
  return a.toString();
  }"
也就是说,一个函数内部,arguments.callee指的就是这个函数本身。这个函数在递归调用时有点用,有许多缺陷,在ES5严格模式被移除
*/

其次来看this关键字。简而言之,this指向函数执行时的环境对象,即函数在哪个对象中执行,this就指向该对象。关于this的详细机制较为复杂,将在后续文章中单独展开讨论。

//TODO:

ES5规范引入了另一个函数属性:caller。该属性指向调用当前函数的外部函数。


function inner(){
console.log(inner.caller);
} 
function outer(){
inner();
}
outer(); 
//function outer(){
inner();
}

length属性表示函数定义时期望接收的参数个数。


function add(a ,b ,c){
return a+b+c;
}
add.length; //3

著名的prototype属性是一个对象,它是通过构造函数创建实例时生成的,包含由特定类型的所有实例共享的属性和方法。关于prototype的详细机制较为复杂,将在后续文章中单独展开讨论。

//TODO:

改变函数执行上下文的方法

函数提供了两个重要方法:call()和apply()。它们的主要作用是在特定的作用域中调用函数,本质上是设置函数内部的this值。

首先是call()方法。它与apply()类似,主要区别在于参数接收方式不同:call()要求将参数逐个列出。

其次是apply()方法。它接收两个参数:第一个是函数运行的作用域,第二个是参数数组(可以是真正的数组,也可以是arguments类数组对象)。


function sum(a , b){
return a+b;
}
function callSum(a , b){
return sum.apply(this , arguments);
}//第二个参数是一个类数组对象arguments
function callSum1(a , b){
return sum.apply(this, [a , b]);
} //第二个参数是一个数组
console.log(callSum(2 , 3)); //5 
console.log(callSum1(3 ,5)); //8

传递参数并调用函数并非call()和apply()的核心用途,它们真正强大的地方在于能够扩充函数运行的作用域。


var color = 'red';
var obj = {
color :'blue' 
}
function foo(){
console.log(this.color); 
}
foo(); //'red'
foo.call(this);//'red'
foo.call(obj); //'blue'
//最后一次调用foo()函数的执行环境变了,其中的this指向了obj对象,所以是'blue'

使用call()和apply()扩充作用域的最大优势,在于实现了对象与方法之间的解耦。

ES5定义了一个新方法:bind()。该方法返回一个新函数,且该新函数中的this值会被永久绑定到传给bind()函数的值上。


var x = 9; 
var module = {
x: 81,
getX: function() { return this.x; }
};
module.getX(); // 81
var retrieveX = module.getX;
retrieveX(); // 9, 因为在这种情况下, "this" 指向全局变量
var boundGetX = retrieveX.bind(module);//把retrieveX()函数中的this永远与module绑定,再调用这个函数永远都是在module对象中运行
boundGetX(); // 81

以上是对JavaScript中Function类型的详细介绍,希望能帮助读者深入理解。如需了解更多内容,敬请关注本站后续更新。

作者最新文章