JavaScript(或ECMAScript)中没有“直接”创建私有变量的方式。C ++,C#,PHP,Java等语言中都可以使用关键字“private / protected”来实现私有变量。
var关键字
在函数内var关键字声明的变量称之为局部变量,只能被函数中的代码访问。当在一个函数外或“全局”中声明的变量称之为全局变量。注意:var关键字拥有“提升”的过程。
// 定义一个全局变量a
var a = 123;
// 在函数内定义一个局部变量b
(function() {
console.log(b); //=> var关键字提升,打印undefined
var b = 456;
})();
console.log(a); // => 123
console.log(b); // 局部变量不能在函数外部访问,抛出错误
ES6之let和const
2015年,ES6/ES2015正式发布,随之而来的是两个新的变量关键字:let和const。两者都是块范围的,这意味着for循环,Function函数,if语句,{}大括号等块作用域内声明的let和const变量在块级作用域外不能访问。
const a = 123;
// 块范围示例
#1
if (true) {
const b = 345;
}
// 块范围示例
#2
{
const c = 678;
}
console.log(a); // 123
console.log(b); // 无法从块范围外访问
console.log(c); // 无法从块范围外访问
模块设计模式
模块设计模式在JavaScript中非常有用,因为它结合了公共和私有组件,通过“封装”的过程暴露程序的一部分供外部访问,未暴露的部分我们可以称之为私有变量或私有组件。
const CarModule = () => {
let milesDriven = 0;
let speed = 0;
const accelerate = (amount) => {
speed += amount;
milesDriven += speed;
}
const getMilesDriven = () => milesDriven;
// 使用“return”关键字只对外暴露accelerate、getMilesDriven两个方法
return {
accelerate,
getMilesDriven
}
};
const testCarModule = CarModule();
testCarModule.accelerate(5);
testCarModule.accelerate(4);
console.log(testCarModule.getMilesDriven());
这里的speed、milesDriven就是私有变量,因为它们只能访问同一个块范围内的代码。再看看下面这个方法:
function CarModule() {
let milesDriven = 0;
let speed = 0;
//在这种情况下,我们使用“this”关键字,指向CarModule
this.accelerate = (amount) => {
speed += amount;
milesDriven += speed;
}
this.getMilesDriven = () => milesDriven;
}
const testCarModule = new CarModule();
testCarModule.accelerate(5);
testCarModule.accelerate(4);
console.log(testCarModule.getMilesDriven());
上例中添加了this关键字。speed、milesDriven同样也是私有变量。
ES6类构造函数中声明私有变量
class CarModule {
constructor() {
let milesDriven = 0;
let speed = 0;
this.accelerate = (amount) => {
speed += amount;
milesDriven += speed;
}
this.getMilesDriven = () => milesDriven;
}
}
const testCarModule = new CarModule();
testCarModule.accelerate(5);
testCarModule.accelerate(4);
console.log(testCarModule.getMilesDriven());
console.log(testCarModule.speed); // undefined -- We have true variable privacy now.
只有constructor构造函数中声明的实例方法才能访问到私有变量,这样确实不够明智。
使用WeakMap
使用WeakMap(),虽然听起来很相似Map,但两者却截然不同。虽然map可以将任何类型的值作为键,但WeakMap只能获取对象并删除WeakMap对象键被垃圾收集时的值。此外,WeakMap不能迭代,这意味着您必须有权访问对象键的引用才能访问值。这使得它对于创建私有变量非常有用,因为变量实际上是不可见的。
const CarModule = (function() {
const Private = new WeakMap();
return class CarModule {
constructor() {
Private.set(this, {
speed: 0,
milesDriven: 0
});
}
accelerate(value) {
let p = Private.get(this);
p.speed += value;
p.milesDriven += p.speed;
}
getMilesDriven() {
return Private.get(this).milesDriven;
}
}
})();
使用Symbol唯一值
Symbol本质上是可以表现为唯一值的实例,除了它自己的唯一实例之外,它们永远不会等于其他任何值。这是一个实际的例子:
const CarModule = (function(){
// symbols hidden in a IIFE
const speedKey = Symbol("speedKey");
const milesDrivenKey = Symbol("milesDrivenKey");
return class CarModule {
constructor() {
this[speedKey] = 0;
this[milesDrivenKey] = 0;
}
accelerate(amount) {
this[speedKey] += amount;
this[milesDrivenKey] += this[speedKey];
}
getMilesDriven() {
return this[milesDrivenKey];
}
}
})();
const testCarModule = new CarModule();
testCarModule.accelerate(5);
testCarModule.accelerate(4);
TC39私有类字段提案
最近,引入了一个新的提议,将私有变量引入类。它相当简单:#在变量名称之前添加一个变量,它变为私有。无需额外的结构变化。
class CarModule {
#speed = 0
#milesDriven = 0
accelerate(amount) {
// It's virtually impossible for this data to be
// accidentally accessed.
this.#speed += amount;
this.#milesDriven += speed;
}
getMilesDriven() {
return this.#milesDriven;
}
}
const testCarModule = new CarModule();
testCarModule.accelerate(5);
testCarModule.accelerate(4);
console.log(testCarModule.getMilesDriven());
console.log(testCarModule.speed); //=> undefined -- we would need to access the internal keys to access the variable.
私有类字段提议不是标准的,目前还不能使用Babel,因此您必须稍等一下才能在主要浏览器上使用它。
Typescript实现
Typescript提供了private关键字,如果您尝试使用私有属性,它将在编译时提醒您。
class CarModule {
private speed:number;
private milesDriven:number;
constructor(){
this.speed = 0;
this.milesDriven = 0;
}
accelerate(amount:number) {
this.speed += amount;
this.milesDriven += this.speed;
}
getMilesDriven(){
return this.milesDriven;
}
}
const testCarModule = new CarModule();
testCarModule.accelerate(5);
testCarModule.accelerate(4);
console.log(testCarModule.getMilesDriven());
console.log(testCarModule.speed); // undefined 属性“speed”是私有的,只能在类“CarModule”内访问