JavaScript核心属性详解:this、prototype与constructor的机制与陷阱
本文深入解析JavaScript中this、prototype和constructor三大核心属性的工作机制。详细阐述this在不同作用域下的动态绑定规则,以及利用apply和call改变上下文的方法。重点剖析prototype作为对象在构造函数中的角色,揭示直接覆盖prototype导致constructor指向Object的常见陷阱,并提供通过Object.assign或手动赋值修复constructor的标准方案,帮助开发者规避原型链扩展时的潜在错误。
this关键字的动态绑定机制
this关键字用于引用当前执行上下文中的对象。当在全局作用域中使用时,this指向window对象(在浏览器环境中);而在函数内部,this的具体指向取决于函数被调用时的上下文环境。开发者可以通过全局方法apply和call来显式指定函数执行时this的绑定对象。
以下示例展示了在全局作用域中使用this的情况:
代码如下:
<script type=>
console.log( === window);
console.log(window.alert === .alert);
console.log(.parseInt(, 10));
</script> 函数内部的this是在运行时动态确定的,而非定义时确定。具体指向取决于调用方式,例如:
代码如下:
foo() {
console.log(.fruit);
}
fruit = ;
foo();
pack = {
fruit: ,
foo: foo
};
pack.foo(); 全局函数apply和call可用于改变函数内部的this指向,示例如下:
代码如下:
foo() {
console.log.fruit);
}
fruit = ;
pack = {
fruit:
};
foo.apply(window);
foo.apply(pack); 注:apply和call两个函数的作用相同,唯一的区别是两个函数的参数定义不同。
由于JavaScript中函数本身也是对象,因此可以观察到以下有趣的行为:
代码如下:
foo() {
( === window) {
console.log();
}
}
foo.boo = () {
( === foo) {
console.log();
} ( === window) {
console.log();
}
};
foo();
foo.boo();
foo.boo.apply(window); prototype
prototype原型对象与构造函数
prototype本质上是一个JavaScript对象。每个函数都拥有一个默认的prototype属性。
当函数用于创建自定义对象时,它被称为构造函数。例如:
代码如下:
Person(name) {
.name = name;
}
Person.prototype = {
getName: () {
.name;
}
}
zhang = Person();
console.log(zhang.getName()); 类比JavaScript内置数据类型(如String、Number、Array、Object、Date等),可以推断这些类型在内部也是通过构造函数实现的,例如:
Array() {
}
arr1 = Array(1, 56, 34, 12);
arr2 = [1, 56, 34, 12];
数组操作的许多内置方法(如concat、join、push)均定义在prototype属性中。
实际上,JavaScript所有内置数据类型都具有只读的prototype属性(这是合理的,因为修改这些类型的prototype会导致预定义方法丢失)。不过,我们可以向其中添加自定义扩展方法。
Array.prototype.min = () {
min = [0];
( i = 1; i < .length; i++) {
([i] < min) {
min = [i];
}
}
min;
};
console.log([1, 56, 34, 12].min());
注意:这里存在一个陷阱。向Array原型添加扩展方法后,在使用for-in循环遍历数组时,该扩展方法也会被枚举出来。
以下代码说明了这一现象(假设已向Array原型扩展min方法):
arr = [1, 56, 34, 12];
total = 0;
( i arr) {
total += parseInt(arr[i], 10);
}
console.log(total);
解决方法很简单:
arr = [1, 56, 34, 12];
total = 0;
( i arr) {
(arr.hasOwnProperty(i)) {
total += parseInt(arr[i], 10);
}
}
console.log(total);
constructor
constructor构造函数属性的陷阱与修复
constructor始终指向创建当前对象的构造函数。例如:
代码如下:
arr = [1, 56, 34, 12];
console.log(arr.constructor === Array);
Foo = () { };
console.log(Foo.constructor === Function);
obj = Foo();
console.log(obj.constructor === Foo);
console.log(obj.constructor.constructor === Function); 然而,当constructor遇到prototype时,会出现一些有趣的行为。
每个函数都有一个默认的prototype属性,该属性的constructor默认指向该函数本身。如下例所示:
代码如下:
Person(name) {
.name = name;
};
Person.prototype.getName = () {
.name;
};
p = Person();
console.log(p.constructor === Person);
console.log(Person.prototype.constructor === Person);
console.log(p.constructor.prototype.constructor === Person); 但是,当我们重新定义函数的prototype时(注意:这里是覆盖而非修改),constructor的行为会变得异常,如下示例:
代码如下:
Person(name) {
.name = name;
};
Person.prototype = {
getName: () {
.name;
}
};
p = Person();
console.log(p.constructor === Person);
console.log(Person.prototype.constructor === Person);
console.log(p.constructor.prototype.constructor === Person); 这是为什么呢?
原因在于,覆盖Person.prototype等价于执行以下代码:
代码如下:
Person.prototype = Object({
getName: () {
.name;
}
}); 由于constructor始终指向创建自身的构造函数,此时Person.prototype.constructor === Object,即:
代码如下:
Person(name) {
.name = name;
};
Person.prototype = {
getName: () {
.name;
}
};
p = Person();
console.log(p.constructor === Object);
console.log(Person.prototype.constructor === Object);
console.log(p.constructor.prototype.constructor === Object); 如何修正这个问题?方法很简单,重新设置Person.prototype.constructor即可:
代码如下:
Person(name) {
.name = name;
};
Person.prototype = Object({
getName: () {
.name;
}
});
Person.prototype.constructor = Person;
p = Person();
console.log(p.constructor === Person);
console.log(Person.prototype.constructor === Person);
console.log(p.constructor.prototype.constructor === Person);JS语法错误会阻止加载吗?区分SyntaxError与资源加载失败
2026-11-05 16:08:46
JavaScript常见SyntaxError语法错误排查与修复方法
2026-11-05 13:58:44
JS中slice、substring和substr截取字符串的区别
2026-11-05 13:43:47
JS 转 TS 手动迁移最佳实践:从配置 tsconfig 到消除 any 类型
2026-11-05 11:53:32
为什么JS数组push后得到的是数字而不是数组?
2026-11-05 09:59:18
