Web 技术研究所

我一直坚信着,Web 将会成为未来应用程序的主流

封装黑魔法之 caller

  caller是被很多人讨厌的一个古老JavaScript特性,在ES5的严格模式中已经无法使用,也行将来它会被从JavaScript中删掉也说不定。但就目前而言它还是全兼容的。它可以用来判断函数的调用者,我们可以以此来判断,将某些方法定义为只有内部可以调用的。
  首先,我们有这样的问题
<script>
var MyClass;
(function(){
  //构造器
  MyClass=function(name){
    //TODO
  };
  //原型
  MyClass.prototype.printName=function(){
    console.log(/*name*/); //此处如何显示私有属性?
  };
})();
</script>
  当然如果只是要解决是很容易的,把属性定义到实例上作为共有属性就可以从外部访问。或者像NodeJS一样把私有属性加个前缀来定义。又或者,直接把printName这个函数定义为OwnProperty(这会造成原型上的方法失去通用性)。
  这个问题说白了就是如何在原型上定义需要访问私有属性的通用方法。如果是现代浏览器的话,用Map很容易解决。比如这样
<script>
var MyClass;
(function(){
  var map=new Map;
  //构造器
  MyClass=function(name){
    //私有属性
    map.set(this,{name:name});
  };
  //原型
  MyClass.prototype.printName=function(){
    console.log(map.get(this).name); //此处如何显示私有属性?
  };
})();
</script>
  这是真正的完美解决!但Map目前的兼容性太糟糕,而模拟Map实现需要时间复杂度至少O(n)的算法,所以Map的模拟版基本没法用。
  我们定义一个方法来获取构造器中的变量如何?而且,为了防止这个定义的方法被外部使用,我们对其做调用限制
<script>
var MyClass;
(function(){
  //构造器
  MyClass=function(name){
    this.getName=getName;
    function getName(){
      if(getName.caller!=printName)
        throw Error("调用你妹!");
      return name;
    };
  };
  //原型
  MyClass.prototype.printName=printName;
  function printName(){
    console.log(this.getName());
  };
})();
</script>

  当然,这么做的话接口上会多出一个用于获取数据的属性名,不过比起其它方法已经安全多了。要是浏览器更先进点,也许可以这么写 <script>
var MyClass;
(function(){
  //构造器
  MyClass=function(name){
    this.__proto__=Object.create(this.__proto__);
    Object.defineProperty(this.__proto__,"valueOf",{
      value:valueOf
    });
    function valueOf(){
      if(valueOf.caller!=printName)
        return this;
      return name;
    };
  };
  //原型
  MyClass.prototype.printName=printName;
  function printName(){
    console.log(this.valueOf());
  };
})();
</script>

  虽然除了直接使用Map之外都没法完美解决,但这已经毫无违和感了不是吗?
网名:
3.84.186.*
电子邮箱:
仅用于接收通知
提交 悄悄的告诉你,Ctrl+Enter 可以提交哦
神奇海螺
[查看全部主题]
各类Web技术问题讨论区
发起新主题
本模块采用即时聊天邮件通知的模式
让钛合金F5成为历史吧!
次碳酸钴的技术博客,文章原创,转载请保留原文链接 ^_^