JavaScript模仿块级作用域与私有变量

一、块级作用域

JS没有块级作用域的概念,定义在块语句中的变量,实际上是在包含函数中而非语句中创建的,如下面例子,变量i和j创建之后即便离开了块语句,只要还在包含函数func中,就可以访问到这两个变量。必须等到函数func执行完毕销毁作用域之后,变量i和j才会被销毁。

function func(count) {
  for (var i = 0; i < count; ++i) {
    console.log(i);
  }
  console.log(i); // 可以访问到i,输出5
  {
    var j = '2333';
  }
  console.log(j); // 可以访问到j,输出2333
}

func(5);

使用一个匿名立即调用函数就可以解决这个问题,模拟出块级作用域的效果。具体的语法如下,将块级作用域的内容放在这个匿名函数的函数体中,在这个匿名函数中定义的变量在函数执行完毕之后都会销毁,因此,外部访问不到块级作用域中的变量。这种技术经常在全局作用域中被用在函数外部,避免向全局作用域中添加过多的变量和函数。在多人协作开发的项目中,过多的全局变量和函数名常常会导致命名冲突,通过这种技术可以创建自己的私有作用域,不用再担心变量的命名,也不会搞乱全局作用域。

(function() {
  // 模拟块级作用域
})();

二、私有变量

JS没有私有成员的概念,所有对象的属性都是公有的,外部都可以访问到。不过倒是有私有变量的概念,例如函数中定义的变量都可以被认为是私有变量,因为只能在函数内部访问到这些变量。下面介绍几种JS中创建私有变量常用的模式。

1. 构造函数内构建闭包

通过在函数内部创建闭包,那么闭包通过自己的作用域链可以访问这些私有变量,利用闭包,创建用于访问私有变量的公有方法(特权方法)。如下面代码示例,privateVar和privateFunc都位于闭包函数publicFunc的作用域链上,外部访问不到,只能通过publicFunc函数来访问。这种在构造函数中定义特权方法的方式有一个缺点,这个缺点就是构造函数模式下,每次创建对象的时候构造函数中的每个方法(无论公有或私有)都会被重新创建一遍。

function MyObject() {
  // 私有变量和函数
  var privateVar = 1;
  function privateFunc() {
    return true;
  }

  // 特权方法
  this.publicFunc = function() {
    privateVar += 1; // 访问私有变量
    if (privateFunc()) { // 调用私有方法
      console.log(privateVar);
    }
  };
}

var obj = new MyObject();
obj.publicFunc(); // 输出2

2. 静态私有变量

这种模式创建了一个私有作用域,并在其中封装了一个构造函数及相应的方法。在私有作用域中定义了私有变量和私有函数,然后定义构造函数和公有方法,公有方法在原型上定义。定义构造函数时不使用函数声明,而是直接使用函数表达式,并且不使用var关键字定义,目的是为了让构造函数变成一个全局函数,在私有作用域之外可以访问到构造函数,这样我们才可以创建对象。(严格模式下不允许给未经声明的变量赋值,因此不能使用这种方法)。这种模式下,特权方法在原型上定义,因此所有实例都使用同一个函数,这个特权方法作为一个闭包也总是引用私有作用域中的函数和变量,因此,这种模式下私有变量与函数都是有实例共享的,解决了构造函数模式下的多次创建问题。

// 私有作用域
(function() {
  // 静态私有成员和方法
  var staticPrivateVar = '2333';
  function staticPrivateFunc() {
    console.log('static');
  }
  // 构造函数
  MyObject = function() {}
  // 公有方法
  MyObject.prototype.publicFunc = function() {
    console.log(staticPrivateVar); // 访问私有变量
    staticPrivateFunc(); // 调用私有方法
  }
})();

// 全局作用域
var obj = new MyObject();
obj.publicFunc(); // 输出"2333"和"static"

3. 模块模式

模块模式的存在是为了给单例创建私有变量和方法使单例得到增强,这种模式的语法如下,使用一个返回对象的匿名立即调用函数,在这个函数内部,首先定义了私有变量和函数,然后将一个对象字面量作为函数的返回值返回。返回的对象字面量中仅包含可以公开的属性方法。如果需要创建一个对象并对某些数据进行初始化,同时还要公开一些能够访问私有数据的方法,那么就可以使用模块模式。通过这种模式创建的对象都是Object的实例,没有特定属于某一种类型,因此不适用与instanceof之类的操作符。

var singleton = (function() {
  // 私有变量和函数
  var privateVar = '2333';
  function privateFunc() {
    return true;
  }
  // 公有变量和函数
  return {
    publicVar: 'hehe~',
    publicFunc: function() {
      if (privateFunc()) { // 调用私有函数
        console.log(privateVar); // 访问私有变量
      }
    }
  };
})();

singleton.publicFunc(); // 输出'2333'
console.log(singleton.publicVar); // 输出'hehe~'

4. 增强的模块模式

这种模式解决了上面说到的模块模式无法检查类型的问题,具体做法就是使用某种类型的实例,然后在匿名立即调用函数返回之前给对象加入属性方法,具体语法如下。这种模式创建的单例对象属于某种特定的类型,可以使用instanceof操作进行检测。

// 某种自定义类型
function SomeType() {}
var singleton = (function() {
  // 私有变量和函数
  var privateVar = '2333';
  function privateFunc() {
    return true;
  }
  // 创建对象
  var object = new SomeType();
  // 公有变量和函数
  object.publicVar = 'hehe~';
  object.publicFunc = function() {
    if (privateFunc()) { // 调用私有函数
      console.log(privateVar); // 访问私有变量
    }
  }
  // 返回对象
  return object;
})();

singleton.publicFunc(); // 输出'2333'
console.log(singleton.publicVar); // 输出'hehe~'
console.log(singleton instanceof SomeType); // 检测类型,输出'true'
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值