了解闭包
打开一个新的函数体会创建一个新的作用域。这种作用域在 JS 中称为闭包。在该范围内创建的变量可以在其所有子范围中访问。这意味着任何var
-创建的变量将对子函数可见。在此示例中,myTemporaryVar 可在 subScope 内访问。
function myParentScope() {
var myTemporaryVar = "sample";
function subScope() {
console.log(myTemporaryVar);
}
return subScope();
}
当您将函数与new
关键字,为当前实例创建一个新的闭包。在该构造函数中创建的任何函数都将保留对范围变量的访问。在下一个示例中,函数 sayHi 可以访问临时变量 myName。
function Person(name) {
var myName = name;
this.sayHi = function() {
console.log("Hi, my name is " + myName + ".");
};
}
p = new Person("Bob");
p.sayHi(); // Hi, my name is Bob.
其实传递的参数是一样的var
-创建的变量。构造函数的参数可以在任何子函数中访问。所以前面的例子可以简化为:
function Person(name) {
this.sayHi = function() {
console.log("Hi, my name is " + name + ".");
};
}
p = new Person("Bob");
p.sayHi(); // Hi, my name is Bob.
这是 JavaScript 的一个非常独特的特性,因为它意味着var
- 只要仍然存在访问它们的方法,创建的变量在函数结束后仍然存在。
模拟基于类的 OOP 隐私
闭包可以被“滥用”来创建具有 getter 和 setter 函数的私有成员。
function Person(name) {
this.getName = function() {
return name;
};
this.setName = function(newname) {
name = newname;
};
}
p = new Person("Bob");
console.log(p.getName()); // "Bob"
p.setName("Alice");
console.log(p.getName()); // "Alice"
p.name; // undefined
为什么这不是真正的隐私
必须在构造函数中创建 getter 和 setter 才能访问var
-多变的。以普通原型扩展方式添加的方法无法访问它们。原型方法也必须使用 setter 和 getter,这使得此类变量的隐私性变得毫无用处。
Person.prototype.sayGoodMorning = function() {
console.log("Good morning, my name is " + this.getName() + ".");
}
在方法中直接访问变量的唯一方法是在构造函数中实际创建它。但是将所有方法放入构造函数中效率极低,因为将为每个实例创建方法的新副本。这就是为什么许多人更喜欢简单地使用自定义符号来识别潜在的私人成员。这谷歌 JavaScript 风格指南建议在变量名称末尾添加下划线。
function Person(name) {
this.name_ = name;
}
Person.prototype.getName = function() {
return this.name_;
}
Person.prototype.setName = function(name) {
this.name_ = name;
}
Person.prototype.sayGoodMorning = function() {
console.log("Good morning, my name is " + this.name_ + ".");
}
程序员的责任是不要愚蠢地访问潜在的私有成员。请注意,这与克罗克福德的观点完全矛盾,但每个人都有自己的观点。我在 Python 之后学习了 JS,所以下划线隐私对我来说就像是第二天性。