1

我将使用以下 JavaScript 代码来演示我正在尝试做的事情。

var App = function() {

    var url
    var width
    var height

    function init()
    {
        url = window.location.href
        width = document.body.clientWidth
        height = document.body.clientHeight
    }

    function run()
    {
        init()
        alert(url + ' - ' + width + 'x' + height)
    }

    return {
        run: run
    }
}()

App.run()

(在以下位置运行此代码:http: //jsfiddle.net/zePq9/

上面的代码实现了三件事:

  1. 避免使用全局变量和函数(App故意暴露给全局命名空间的全局对象除外)。
  2. 隐藏url,widthheight变量以及init()匿名函数中的函数。
  3. 仅公开run()匿名函数之外的函数。

但是,我不太喜欢这种方法,因为init()andrun()函数必须使用url,width和等变量height。在更复杂和更大的 JavaScript 代码中,很难url在函数中看到变量并知道它来自哪里。必须进行大量滚动才能回答以下问题:它是局部变量吗?是全局变量吗?它仅对匿名功能是本地的吗?

现在我正在用下面的代码解决这个问题。

var App = function() {

    var self = {
        url: '',
        width: 0,
        height: 0
    }

    function init()
    {
        self.url = window.location.href
        self.width = document.body.clientWidth
        self.height = document.body.clientHeight
    }

    function run()
    {
        init()
        alert(self.url + ' - ' + self.width + 'x' + self.height)
    }

    return {
        run: run
    }
}()

App.run()

(在以下位置运行此代码:http: //jsfiddle.net/cXALf/

现在,我可以清楚地看到self.url并告诉这个变量来自self对象,我知道的范围仅限于匿名函数,因为我的编码约定是使用self对象将匿名函数中所有函数共享的变量保存在一起。请注意,self.url它仍然隐藏在匿名函数中,并且在它之外不可见。

理想情况下,我想使用this.url,this.width等而不是self.url,self.width等。但我无法找到一个解决方案,使this.url,this.width等在匿名函数之外保持不可见。可能吗?

4

3 回答 3

2

根据您认为不可见的内容,您可以使用多种解决方案之一。但是,每种解决方案都有其自身的缺陷。

您必须考虑的是为什么要使用与this“私有变量”相关的关键字。在 JavaScript 中,实际上并没有私有变量的概念(稍后会详细介绍)。任何可以从一个上下文访问的东西都可以从另一个上下文访问。

绑定到闭包绑定变量

此解决方案最接近您最初的解决方案,但Function.prototype.bind用于强制上下文成为闭包内部对象。

function App() {
  var self = {
    url: '',
    width: 0,
    height: 0
  };

  var init = function() {
    this.url = window.location.href;
    this.width = document.body.clientWidth;
    this.height = document.body.clientHeight;
  }.bind(self);

  var run = function() {
    init();
    console.log(this.url + ' - ' + this.width + 'x' + this.height);
  }.bind(self);

  return {
    run: run
  };
}

这种方法的缺点是每个“成员”函数都必须是.bind()to self,否则这些函数的上下文将不是self您声明的闭包内部。这种方法也不是非常Javascript-y,因为必须创建相当于构造函数的每个方法和属性。这消除了 JavaScript 具有的原型继承系统。

使用 ES5 属性定义

如果您愿意只坚持使用符合 ES5 的 JS 引擎(这主要意味着 IE 不低于 9),您可以使用 ES5Object.defineProperty()来隐藏对象的属性。您仍然可以从其他上下文访问它们,但至少它们不会出现在对象属性的枚举中。

function App() {
  this.url = window.location.href;
  this.width = document.body.clientWidth;
  this.height = document.body.clientHeight;
}

Object.defineProperties(App.prototype, {
  url: {
    enumerable: false,
    configurable: true,
    writable: true,
    value: ""
  },
  width: {
    enumerable: false,
    configurable: true,
    writable: true,
    value: 0
  },
  height: {
    enumerable: false,
    configurable: true,
    writable: true,
    value: 0
  }
});

App.prototype.run = function() {
  console.log(this.url + ' - ' + this.width + 'x' + this.height);
};

new App().run();

这种技术的优点是您能够准确地控制哪些属性是可枚举的(像for...inor之类的枚举“可见” Object.keys()),哪些不是。此外,App对象的构造发生在其实际的构造函数中,并且run()现在可以被子类覆盖。缺点是这需要一个 ES5 兼容的引擎,并且这不会阻止外部代码访问或修改url,widthheight, 如果他们有权访问App.

在 JavaScript 中获得真正隐私的唯一方法是在范围内声明变量,并提供也在同一范围内定义的访问器。JavaScript 中的作用域机制只是使从作用域之外无法引用这些变量。同样,匿名实例几乎可以做同样的事情......如果您从不使用App唯一调用之外的实例run,例如:

new App().run();

然后创建的实例new App()将无法在其他任何地方访问。这不是真正的隐私,因为引用该实例仍然允许您访问其所有属性,但这可能就足够了。

于 2013-07-31T15:39:04.220 回答
1

您可以使用bind永久设置this每个函数的值:

var App = function() {

    var self = {
        url: '',
        width: 0,
        height: 0
    };

    var init = function()
    {
        this.url = window.location.href;
        this.width = document.body.clientWidth;
        this.height = document.body.clientHeight;
    }.bind(self);

    var run = function()
    {
        init();
        alert(self.url + ' - ' + self.width + 'x' + self.height);
    }.bind(self);

    return {
        run: run
    }
}();

请注意,旧版浏览器不支持绑定,但您可以使用 polyfill将其添加到不自动提供它的浏览器中。

请注意,我不一定认为这段代码是个好主意,但它确实可以满足您的需求。self我个人认为将变量用作显式容器并没有错,而使用它this可能会使您的代码更难被其他人理解(因为他们可能认为App这意味着要有实例)。

于 2013-07-31T14:56:35.433 回答
-1

您的应用程序可以转换为这样的东西(它使用this并且没有闭包):

编辑这个解决方案忽略了隐藏的部分,我跳过了问题的第一部分。IMO 真正的变量隐藏不值得在 JS 中付出努力和开销。这段代码比其他代码更精简,开销更少,但没有隐藏。

function App() {
    this.init();
}

App.prototype = {
    url: '',
    width: 0,
    height: 0,

    init: function() {
        this.url = window.location.href
        this.width = document.body.clientWidth
        this.height = document.body.clientHeight
    },

    run: function() {
        console.log(this.url + ' - ' + this.width + 'x' + this.height)
    }
}

new App().run();
于 2013-07-31T14:55:00.623 回答