JavaScript如何实现私有变量私有方法

5,346 阅读4分钟

前言

大家都知道JavaScript不像Java这类强类型语言,有特定的字面量(private)来定义参数为类的私有参数;那JavaScript如何能实现私有变量呢,下面来介绍几种实现方式。

一、函数作用域封装局部变量的方式。

1.1 闭包定义局部变量(自执行函数)

var PClass = (function(){
    var a = '私有变量';
    var getA = function(){
    	console.log(a, '私有方法')
	}
    var setA = function(val){
            a = val
        }
    function P (){
    	this.b = '变量b,外部可访问'
    }
    P.prototype = {
        getB : function(){
            console.log(this.b,'--- 获取对象公有属性b ---')
        },
        setType: function(a,val){
            if(a == 'a'){
                setA(val);
            }
        },
        getType: function(a){
            if(a == 'a'){
                getA();
            }
        }
    }
    return P
}())

var pclass = new PClass()

pclass.b            //打印    变量b,外部可访问
pclass.getB()       //打印    变量b,外部可访问 --- 获取对象公有属性b ---
console.log(pclass) //打印    P {b: "变量b,外部可访问"}
// 通过对外开放的另一方法我们可以获取到了私有变量a
pclass.getType('a') //打印    私有变量 私有方法

根据上面的例子我们不难发现,私有变量只能再闭包内部访问,外部无法访问,当时这个方案并不完美,为什么这么说,比如我们再new 一个 pclass1 , 然后修改a属性的值,你会发现pclass的a值也发生了变化

var pclass1 = new PClass();
pclass1.setType('a', '修改私有属性a的值');
pclass1.getType('a');  //打印    修改私有属性a的值  私有方法
pclass.getType('a');  //打印    修改私有属性a的值  私有方法

那么针对上面的问题我们怎么解决呢;其实很简单,我们只要将这个闭包改成非自执行函数就OK了

1.2 闭包定义局部变量(非自执行函数)

var pClass = function(){
	var a = '私有变量';
	var getA = function(){
		console.log(a, '私有方法')
	}
	var setA = function(val){
		a = val
	}
    function P (){
		this.b = '变量b,外部可访问'
    }
    P.prototype = {
        getB : function(){
            console.log(this.b,'--- 获取对象公有属性b ---')
        },
        setType: function(a,val){
            if(a == 'a'){
                setA(val);
            }
        },
        getType: function(a){
            if(a == 'a'){
                getA();
            }
        }
    }
    return P
}

var PClass1 = pClass(); //开辟了一个函数作用域
var PClass2 = pClass(); //重新开辟了一个函数作用域
var pclass1 = new PClass1();
var pclass2 = new PClass2();
pclass1.setType('a', '修改私有属性a的值');
pclass1.getType('a');   //打印    修改私有属性a的值  私有方法
pclass2.getType('a');   //打印    私有变量 私有方法

二、ES6 定义私有变量,私有方法

2.1 使用ES6扩展的类型symbol类型定义

先来解释下symbol类型;Symbol值通过Symbol函数生成。这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的Symbol类型。凡是属性名属于Symbol类型,就都是独一无二的,可以保证不会与其他属性名产生冲突;类似与UUID的用法。

Symbol('1') == Symbol('1') //打印  false
var sy = sb = Symbol('a');
sy === sb //打印  true   说明该数据类型以引用的方式传值。

解释了Symbol 类型数据,那下面来写一个基于Symbol类型的私有变量,私有属性吧。

var Pclass = (function(){
    const a = Symbol('a');
    const m = Symbol('m');
    class  Pclass {
        constructor(){
            this[a] = 'a这是私有变量';
            this.b = '变量B-外部可访问';
            this[m] = function(){
                console.log('私有方法');
            }
        }
        getA(){
            console.log(this[a]);
        }
        getM(){
            console.log(this[m]);
        }
    }
    return Pclass
}())

let pc = new Pclass() 
console.log(pc)   //打印 Pclass {b: "变量B-外部可访问", Symbol(a): "这是私有变量", Symbol(m): ƒ}

由上述代码我们可以发现 只要不把 a = Symbol('a'); m = Symbol('m') 这两个引用对外暴露,外部是无法访问到定义的私有变量a,和私有方法m, 因为他们的真实属性名称是a, m 这两个引用,而且是唯一的。

2.1 使用ES6扩展的类型WeakMap类型定义

先来解释下WeakMap类型, 该类型数据是一个键-值(key-val)对的集合,只不过他的键(key)是一个引用,不同于一般的键-值。WeakMap 的使用如下。

const wm = new WeakMap();
const a = {}, b = {};
wm.set(a, '这是a对象键的值');
wm.set(b, '这是b对象键的值');
console.log(wm.get(a)) //打印 这是a对象键的值
console.log(wm.get(b)) //打印 这是b对象键的值

解释了WeakMap类型数据,那下面来写一个基于WeakMap类型的私有变量,私有属性吧。

var Pclass = (function(){
    const aa = new WeakMap();
    const mt = new WeakMap();
    class  Pclass {
        constructor(){
            this.b = 'b这是公有变量';
            aa.set(this, '私有变量aa')
            mt.set(this, function(){
                console.log('私有方法mt')
            })
        }
        getA(){
            console.log(aa.get(this));
        }
        getM(){
            console.log(mt.get(this));
        }
    }
    return Pclass
}())
let pc = new Pclass() 
console.log(pc) // Pclass {b: "b这是公有变量"}

同上述代码我们可以发现 只要不把 aa = new WeakMap(); m = new WeakMap()这两个集合对外开放 外部是无法访问到定义的私有变量a,和私有方法m, 因为他们的真实属性名称是a, m 这两个引用,而且是唯一的。