函数的内置属性
在函数中有一些可使用的内置属性,通过这些属性可以获得与函数相关的一部分信息。下面将分别介绍这些内置属性。
arguments
通过 arguments
属性获取函数在调用时的参数值,这个参数可以用数组的形式访问。例如,arguments.length
代表传入参数的个数,arguments[0]
代表第 1 个参数,arguments[1]
代表第 2 个参数。
使用 arguments
的示例代码如下,为了表现出对不同参数的影响,test()
函数中分别定义了 4 个参数——一个必选参数,一个默认参数,一个可选参数和一个剩余参数。
function test(num1: number, num2: number = 0, num3?: number, ...restParas: string[]) {
console.log("共传入了多少个参数:" + arguments.length);
for (let i = 0; i < arguments.length; i++) {
console.log(`第${i + 1}个参数值为${arguments[i]}`);
}
}
test(1);
test(1, 2);
test(1, 2, 3);
test(1, 2, 3, "a", "b", "c");
输出结果如下,可以看到 arguments
的参数数量只与实际传入多少个参数值有关,与函数定义了多少个参数无关。
> 共传入了多少个参数:1
> 第1个参数值为1
> 共传入了多少个参数:2
> 第1个参数值为1
> 第2个参数值为2
> 共传入了多少个参数:3
> 第1个参数值为1
> 第2个参数值为2
> 第3个参数值为3
> 共传入了多少个参数:6
> 第1个参数值为1
> 第2个参数值为2
> 第3个参数值为3
> 第4个参数值为a
> 第5个参数值为b
> 第6个参数值为c
在实际编程中并不推荐使用 arguments
属性,因为这种方式的可读性和可维护性较差,只有在极其特殊的情况下才会使用。
caller
caller
是 ECMAScript 5 新增的一个属性,调用方式为 “函数名称.caller”,该属性引用可调用当前函数的函数。如果在全局作用域中调用该函数,则会返回 null
。示例代码如下。
function test()
{
console.log(test.caller);
}
function outerFunc()
{
test();
}
test(); //由于在全局作用域中调用,没有上层函数,因此输出null
outerFunc(); //输出"ƒ outerFunc() { test(); }"
由于通过这种方式调用 caller
属性需要有函数名称,但部分函数是使用表达式形式声明的,因此还可以使用 arguments.callee.caller
属性来获取调用当前函数的函数,示例代码如下。
let test = function () {
console.log(arguments.callee.caller);
}
let outerFunc = function () {
test();
}
test(); //由于在全局作用域中调用,没有上层函数,因此输出null
outerFunc(); //输出"ƒ () { test(); }"
this
this
是函数在运行时,在函数体内部自动生成的一个对象,它只能在函数体内部使用。this
并不是变量,它属于关键词。this
的值通常是调用该函数的上下文对象,是该函数的 “拥有者”。
在不同的场合下,this
的值各不相同,下面将详细介绍。
-
直接调用函数时的
this
当函数直接被调用时,属于全局性调用,某个全局性对象拥有并调用了这个函数,因此
this
将指向这个全局性对象globalThis
(后面会详细介绍)。基于不同的运行环境,全局对象
globalThis
会有所区别。示例代码如下。function test() { console.log(`${this.name}`); console.log(`${this}`); } globalThis.name = "Alina"; test();
浏览器运行环境中的全局对象是
window
,因此globalThis
对象将指向window
对象,输出结果如下。> Alina > [object Window]
可以看到在函数体中通过
this
成功读取全局对象及其属性。Node.js
运行环境中的全局对象是global
,因此globalThis
对象将指向global
对象,输出结果如下。> Alina > [object global]
-
以对象方法的形式调用函数时的this
当函数作为某个对象的方法时,该对象将称为函数的拥有者,通过该对象调用函数时,
this
将指向这个对象。以下代码声明一个名为
person
的对象,该对象拥有一个名为name
的字符串类型的属性和一个名为selfIntroduction () => void
类型的方法,声明之后分别为name
属性和selfIntroduction()
方法赋值,selfIntroduction()
方法指向introduction()
方法。function introduction() { console.log(`Hello ${this.name}`); } let person: { name: string, selfIntroduction: () => void } = { name: "", selfIntroduction: null }; person.name = "Rick"; person.selfIntroduction = introduction; //以下代码输出"Hello undefined",因为当前全局对象中还没有定义name属性 introduction(); //以下代码输出"Hello Rick" person.selfIntroduction();
可以看到,调用者不同,
this
对象也不同。当在全局作用域中调用introduction()
方法时,this
指向globalThis
对象(浏览器运行环境中globalThis
对象指向window
对象,Node.js
运行环境中globalThis
对象指向global
对象),通过person
对象调用introduction()
方法时,this
指向调用者person
对象本身,因此this.name
将使用person
对象的name
属性,输出"Hello Rick"
。 -
箭头函数中的
this
箭头函数中的
this
是一个特例,在箭头函数中,this
引用的是声明箭头函数时的上下文对象,而非实际调用箭头函数时的上下文对象。如果将上一个示例中的
selfIntroduction()
方法改为箭头函数,结果将变得不同。修改后的代码及运行结果如下。let person: { name: string, selfIntroduction: () => void } = { name: "", selfIntroduction: null }; person.name = "Rick"; person.selfIntroduction = () => { console.log(`Hello ${this.name}`); }; //以下代码输出"Hello undefined",因为当前全局对象中没有定义name属性 person.selfIntroduction();
-
约束或禁用 this
当函数的拥有者不是全局对象时,你可以限定函数中
this
的类型。具体方式是在声明函数时,把首个参数声明为 “this:类型描述”,如果还有其他参数,则必须放到this
参数后面。例如,在以下代码中,sum()
函数的声明中包含this
参数,其类型为{num1:number}
,sum()
函数的拥有者是sumCalculator
对象,因此this
指向sumCalculator
对象,这就要求sumCalculator
对象至少拥有一个num1
属性。function sum(this: { num1: number }, num2) { return this.num1 + num2; } let sumCalculator = { num1: 1, selfIntroduction: sum }; let result = sumCalculator.selfIntroduction(2); //result值为3
如果
sumCalculator
对象不具有num1
属性,则会引起编译错误,示例代码如下。let num = { selfIntroduction: sum }; //类型为"{ selfIntroduction: (this: { num1: number; }, num2: any) => any; }" //的 "this" 上下文不能分配给类型为"{ num1: number; }"的方法的 "this" let result = num.selfIntroduction(2); //result值为3
函数也可以禁用
this
,只需要将函数的首个参数声明为 “this:void” 即可,这样在函数体中就不允许使用this
,否则会引起编译错误。示例代码如下。function sum(this: void, num2) { //编译错误:类型"void"上不存在属性"num1"。ts(2339) return this.num1 + num2; }