JavaScript中私有变量实现

2019-06-12120次阅读javascript

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”内访问
上一篇: 网站icon尺寸大小做多大合适?  下一篇: CSS实现元素的宽高比  

JavaScript中私有变量实现相关文章