我正在学习如何使用构造函数在JavaScript中实现OOP,并且我对如何创建子类型感到困惑。
我目前的理解是,新关键字是在幕后执行以下操作:
- 创建空
this
对象 - 将
this
对象的__proto__
属性设置为指向构造函数上的原型属性 - 返回
this
对象
这就是为什么我们不必从构造函数显式返回对象,new
关键字为我们执行此操作。
在下面的示例中,我正在创建User
类型的PaidUser
子类型:
function User(name) {
this.name = name;
}
function PaidUser(name, balance) {
User.call(this, name);
this.balance = balance;
}
在我看到的示例之后,我从PaidUser
类型中的User
类型属性进行扩展,并在User
构造函数中设置this
的上下文,以指向带有User.call(this,...)
的词法this
我的困惑是User.call()
如何影响new
关键字从PaidUser
构造函数隐式返回的内容?
例如,在PaidUser
构造函数中,我将this.balance
属性定义为this
对象,现在我的理解是使用new
关键字调用时将从PaidUser
构造函数返回的this
对象。User.call(this, name)
如何返回或影响从PaidUser
构造函数返回的对象。
目前根据我的理解,这就是幕后发生的想象:
function PaidUser(name, balance) {
this = {
__proto__:PaidUser.prototype
};
this.balance = balance;
User.call(this, name); // {this.name:name}; //lexical this
// why is the object returned from User.call() just floating around
// neither assigned to a variable or returned ?
return this; // how does this.name ever become part of the returned object?
}
我非常感谢有关调用PaidUser
new
构造函数时 关键字所采取的步骤的任何帮助和解释。
非常感谢和赞赏。
我的困惑是
User.call()
如何影响PaidUser
构造函数中new
关键字隐式返回的内容?
理解这种情况发生的关键是了解传递给函数的对象可能会在函数外部受到影响:
//function that takes an object and mutates it
function fn(o) {
o.hello = "universe";
}
//a simple object
var obj = {
hello: "world"
};
console.log("before calling the function", obj);
//call the function with the object
fn(obj);
console.log("after calling the function", obj);
因此,对象可以在函数内部发生变异,也会在函数外部影响它们。
当您调用User
构造函数的主体this.name = name
User.call(this, name);
这会改变this
上下文时,也会发生这种情况。由于this
上下文是一个对象,因此它将在函数外部发生突变。由于它有效地改变了new PaidUser
的this
上下文,因此代码会这样做:
function PaidUser(name, balance) {
this.name = name; //contents of User.call(this, name);
this.balance = balance;
}
所以,它实际上没有看起来那么神奇。new
关键字在整个过程中扮演的唯一部分是new PaidUser
将创建一个新对象并将其设置为this
上下文。您可以通过执行以下操作来模拟类似的效果:
function User(name) {
this.name = name;
}
function PaidUser(name, balance) {
User.call(this, name);
this.balance = balance;
}
var obj = {};
PaidUser.call(obj, "Fred", 1234);
console.log(obj);
这不会做new
关键字所做的一切 - 它不会设置原型,不会让构造函数隐式返回this
,但它仍然创建一个新对象并将其通过所有赋值。由于我们引用了对象,我们可以在之后检查它,并看到代码确实有效地完成了obj.name = name
和obj.balance = balance
。