JavaScript中arguments和this对象用法分析
本文详细解析JavaScript中arguments和this两个特殊对象的用法。重点阐述arguments类数组对象的特性及callee属性的使用限制,深入剖析this动态绑定的四种调用方式(普通函数、对象方法、构造函数、call/apply),并探讨this优先级与丢失问题的解决方案。
本文通过具体实例解析JavaScript中arguments和this对象的使用机制,供读者参考学习。
在JavaScript函数内部存在两个特殊的对象:arguments和this。
arguments对象详解
1、arguments对象
js函数不关心定义了多少参数,也不在乎传递进来多少参数,也就是说,即使定义的函数只接收2个参数,在调用时候也未必传递2个参数,因为js的函数参数在内部使用一个数组表示的,在函数体内可以通过arguments对象访问此参数数组。因此,js函数可以不显式地使用命名参数。
当函数被调用时,传入的参数会保存在arguments类数组对象中。通过arguments可以访问函数被调用时传递的所有参数列表。
arguments是一个类数组对象,因为它支持通过方括号语法访问元素,并拥有length属性,但它缺乏真正的数组方法,所以并非标准数组。
利用arguments可以实现一个通用的求和函数:
function add() {
var sum = 0;
for (var i = 0, len = arguments.length; i < len; i++)
sum += arguments[i];
return sum;
}
虽然arguments主要用于存储函数参数,但该对象还包含一个callee属性。该属性是一个指针,指向拥有当前arguments对象的函数本身。
利用arguments.callee属性可以实现阶乘函数:
function factorial(num) {
if (num <= 1)
return 1;
else
return num * arguments.callee(num - 1);
}
注意:
在严格模式下,无法使用arguments.callee属性,也不能对arguments对象进行赋值操作,更不能用其跟踪参数变化。
可以通过命名函数表达式实现相同效果:
var factorial = (function func(num) {
if (num <= 1)
return 1;
else
return num * func(num - 1);
));
由于JavaScript函数没有签名(即不定义接受参数的类型和数量),因此JavaScript不支持函数重载。同名函数会被后定义的覆盖。不过,可以通过检查传入参数的类型和数量来模拟方法重载的行为。
this对象动态绑定机制
2、this对象
与其他语言不同,JavaScript中的this始终指向一个对象,具体指向哪个对象是在运行时根据函数的执行环境动态绑定的,而不是在函数声明时确定。
- this是执行上下文的一个属性,其值在进入上下文时确定,且在上下文运行期间保持不变。
- this采用动态绑定机制,即在运行期确定绑定对象。
- this可以指向全局对象、当前对象或任意对象,具体取决于函数的调用方式。常见的调用方式包括:作为普通函数调用、作为对象方法调用、作为构造函数调用,以及使用
call()和apply()调用。
(1)作为普通函数调用
当函数不作为对象的属性被调用(即直接调用)时,this会绑定到全局对象。在浏览器环境的JavaScript中,该全局对象是window对象。
var name = "Alice";
function getName (name) {
return this.name;
}
alert(getName()); // 输出:Alice
var name = "Alice";
var obj = {
name: 'Bruce',
getName: function(name) {
return this.name;
}
};
var getName = obj.getName();
alert(getName()); // 输出:Alice
在上述两个示例中,this均被绑定到了全局对象。
var firstname = "A";
var lastname = "B";
var person = {
firstname : "Alice",
lastname : "Bruce",
setName : function(firstname, lastname) {
var setFirstName = function(firstname) {
this.firstname = firstname;
};
var setLastName = function(lastname) {
this.lastname = lastname;
};
setFirstName(firstname);
setLastName(lastname);
}
};
person.setName("Cindy", "David");
alert(firstname);//Cindy
alert(lastname);//David
alert(person.firstname);//Alice
alert(person.lastname);//Bruce
问题:在函数内部定义的函数,其this也可能指向全局对象,而通常希望内部函数的this能绑定到外部函数对应的对象上。
原因:内部函数无法直接访问外部函数中的this变量。
解决方法:在外部函数体中,进入内部函数之前,将this保存到一个变量中,然后在内部函数中使用该变量。
var firstname = "A";
var lastname = "B";
var person = {
firstname: "Alice",
lastname: "Bruce",
setName: function(firstname, lastname) {
var that = this;
var setFirstName = function(firstname) {
that.firstname= firstname;
};
var setLastName = function(lastname) {
that.lastname= lastname;
};
setFirstName(firstname);
setLastName(lastname);
}
};
person.setName("Cindy", "David");
alert(firstname);//A
alert(lastname);//B
alert(person.firstname);//Cindy
alert(person.lastname);//David
(2)作为对象方法调用
当函数作为对象的方法被调用时,this会绑定到当前对象。
var firstname = "A";
var lastname = "B";
var person = {
firstname : "Alice",
lastname : "Bruce",
setName : function(firstname, lastname) {
this.firstname = this.firstname + firstname;
this.lastname = this.lastname + lastname;
}
};
person.setName("Cindy", "David");
alert(firstname);//A
alert(lastname);//B
alert(person.firstname);//AliceCindy
alert(person.lastname);//BruceDavid
此时this被绑定到了当前对象,即person对象。
(3)作为构造函数调用
JavaScript中没有类的概念,但可以从构造器中创建对象,并提供new运算符,使构造器看起来更像类。
利用构造函数创建新对象时,可以使用this来指向新创建的对象,避免函数中的this指向全局对象。
var name = "Alice";
function Person(name) {
this.name = name;
}
var person = new Person("Bruce");
alert(name);//Alice
alert(person.name);//Bruce
利用构造函数创建新对象person时,this指向了person。
使用new调用构造器时需注意一个问题:若构造器显式返回了一个object类型的对象,构造器返回的结果将是该对象,而不是this。
function Person() {
this.name = "Alice"
return {
name: "Bruce"
}
}
var person = new Person();
alert(person.name);//Bruce
(4)call()和apply()调用
call()和apply()用于切换函数执行的上下文环境,即改变this绑定的对象;this指向的是apply()和call()中的第一个参数。
function Person(name) {
this.name = name;
this.setName = function(name) {
this.name = name;
}
}
var person1 = new Person("Alice");
var person2 = {"name": "Bruce"};
alert("person1: " + person1.name); // person1: Alice
person1.setName("David");
alert("person1: " + person1.name); // person1: David
alert("person2: " + person2.name); // person2: Bruce
person1.setName.apply(person2, ["Cindy"]);
alert("person2: " + person2.name); // person2: Cindy
apply()将person1的方法应用到person2上,此时this也被绑定到person2上。
this优先级与丢失问题
3、this优先级
(1)函数是否在new中调用?如果是,this绑定到新创建的对象。
(2)函数是否通过call、apply调用?如果是,this绑定到指定对象。
(3)函数是否在某个上下文对象中调用?如果是,this绑定到该上下文对象。
(4)若以上都不是,使用默认绑定:在严格模式下绑定到undefined,否则绑定到全局对象。
4、this丢失的问题
示例1:
var person = {
name: "Alice",
getName: function() {
return this.name;
}
};
alert(person.getName()); // Alice
var getName = person.getName;
alert(getName()); // undefined
当调用person.getName()时,getName()方法是作为person对象的属性被调用的,因此this指向person对象;
当用另一个变量getName来引用person.getName,并调用getName()时,是作为普通函数被调用,因此this指向全局window。
示例2:
<div id="div">正确的方式</div>
<script>
var getId = function(id) {
return document.getElementById(id);
};
alert(getId('div').innerText);
</script>
<div id="div">错误的方式</div>
<script>
var getId = document.getElementById;
alert(getId('div').innerText); // 抛出异常
</script>
问题:第一段调用正常,但第二段调用会抛出异常。
原因:许多引擎的document.getElementById()方法的内部实现中需要用到this,this本来期望指向的是document。当第一段代码在getElementById()方法作为document对象的属性被调用时,方法内部的this确实是指向document的。而第二段代码中,用getId来引用document.getElementById之后,再调用getId,此时就成了普通函数调用,函数内部的this指向了window,而不是原来的document。
解决方法:利用apply把document当作this传入getId函数,修正this。
<div id="div">修正的方式</div>
<script>
document.getElementById = (function(func) {
return function() {
return func.apply(document, arguments);
};
})(document.getElementById);
var getId = document.getElementById;
alert(getId('div').innerText); // 抛出异常
</script>
更多关于JavaScript相关内容感兴趣的读者可查看本站专题:《javascript面向对象入门教程》、《JavaScript常用函数技巧汇总》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript遍历算法与技巧总结》及《JavaScript数学运算用法总结》
希望本文所述对大家JavaScript程序设计有所帮助。
怎么用 Promise 封装 JavaScript sleep 函数实现异步延时
2026-11-05 17:49:14
什么时候该用JS构造器:与工厂模式、字面量的选择对比
2026-11-05 16:18:22
推荐几款轻量级JS照片墙开源库及对比
2026-11-05 15:56:19
JavaScript递归写法有哪些常见陷阱及避免方法
2026-11-05 14:53:38
JS数组浅拷贝与深拷贝怎么选?场景与方法总结
2026-11-05 14:41:02
