Crockford的安全对象模块

时间:2018-06-11 21:56:05

标签: javascript

我理解有很多关于" Javascript:The Good Parts"但是我试图理解书中一个句子的含义并且不能完全理解它。在第41-42页中,他定义了serial_maker函数:

var serial_maker = function (  ) {

// Produce an object that produces unique strings. A
// unique string is made up of two parts: a prefix
// and a sequence number. The object comes with
// methods for setting the prefix and sequence
// number, and a gensym method that produces unique
// strings.

    var prefix = '';
    var seq = 0;
    return {
        set_prefix: function (p) {
            prefix = String(p);
        },
        set_seq: function (s) {
            seq = s;
        },
        gensym: function (  ) {
            var result = prefix + seq;
            seq += 1;
            return result;
        }
    };
};

var seqer = serial_maker();
seqer.set_prefix('Q');
seqer.set_seq(1000);
var unique = seqer.gensym(); // unique is "Q1000"

然后他说:

  

这些方法不使用 this 。因此,没有办法妥协 sequer 。获取或更改前缀 seq 是不可行的,除非方法允许

我应该如何使用这个和/或那个打破这种封装?我看不到它

由于

2 个答案:

答案 0 :(得分:2)

考虑以下变化:

var serial_maker = function () {
    return {
        prefix: '',
        seq: 0,

        set_prefix: function (p) {
            this.prefix = String(p);
        },
        set_seq: function (s) {
            this.seq = s;
        },
        gensym: function (  ) {
            var result = this.prefix + this.seq;
            this.seq += 1;
            return result;
        }
    };
};

现在,sequer的预期用途如下:

var sequer = serial_maker();
sequer.set_prefix('right');
sequer.set_seq(1000);

但是,在我上面发布的版本中,你也可以这样做:

var sequer = serial_maker();
sequer.prefix = 'wrong';
sequer.seq = -500;

甚至这个:

delete sequer.prefix;

由于prefixseq都作为sequer对象的属性公开,因此JavaScript中的属性始终是公共的。任何有权访问该对象的代码都至少可以读取其属性,并且通常也会修改它们(除非您使用Object.defineProperty()提供的某些功能)。

至于that:在引入箭头函数之前,这是一个非常常见的问题,定义为方法的函数无法访问创建它们的上下文。

考虑以下示例:

var ButtonInitializer = {
    message: 'Hello!'

    init: function() {
        for (let button of document.querySelectorAll('button')) {
            button.onclick = function() {
                alert(this.message);
            }
        }
    }
};

ButtonInitializer.init();

对象ButtonInitializer在文档中搜索<button>元素,并设置其onclick事件侦听器以显示警报;我们的目的是显示ButtonInitializer.message中定义的消息。但是,如果您运行上述代码,则会发现警报为“undefined”。这是因为我们分配给button.onclick的函数成为按钮的方法,因此函数内的this关键字现在将引用按钮,而不是ButtonInitializer

今天,我们可以通过箭头功能来解决这个问题:

button.onclick = () => {
    alert(this.message);
}

箭头功能没有自己的this范围,因此警报会显示ButtonInitializer.message,正如我们所说。在引入箭头函数之前,这是一个常见的解决方法:

var that = this;
button.onclick = function() {
    alert(that.message);
}

这种技术通常与闭包一起使用,并且允许对象的方法可以访问的“私有”成员的有限实现,但是不能从外部代码直接看到。

答案 1 :(得分:2)

有两种流行的方法可以在JavaScript中使用函数创建对象:

  • factory模式
  • constructor模式(使用ES6 class更好地说明)

factory模式

在此示例中,他正在使用并引用factory模式,该模式不使用new关键字,也不会对新创建的this绑定宾语。它只创建一个新对象并将其作为函数表达式的值返回,从而将其称为工厂。

工厂函数是在JavaScript中创建真实对象私有属性的最佳方式,因此使用闭包进行封装。由于没有this绑定,因此无法访问工厂函数中封装的变量prefixseq。使用此模式是在JavaScript中创建完全私有的封装对象“property”的唯一方法(与Java使用private相比。)

  

我应该如何使用这个和/或那个打破封装?

您可以使用 Constructor模式(使其成为构造函数)重新考虑该代码,如下所示(使用ES6):

class SerialMaker {
    // Creates a `this` binding to the instance of the class
    // No actual encapsulation on the private properties
    constructor () {
        // PSEUDO-PRIVATE PROPERTIES
        this.__prefix__ = '';
        this.__seq__ = 0;
        // METHODS
        this.set_prefix: function (p) {
            this.__prefix__ = String(p);
        };
        this.set_seq: function (s) {
            this.__seq__ = s;
        };
        gensym: function (  ) {
            var result = this.__prefix__ + this.__seq__;
            this.__seq__ += 1;
            return result;
        };
    }

现在,您可以使用new运算符

创建新对象
var seqer = new SerialMaker(); // Calls the constructor and creates a new instance object
seqer.set_prefix('Q'); // Set on this instance only: this.__prefix__
seqer.set_seq(1000); // Set on this instance only: this.__seq__
var unique = seqer.gensym(); // unique is "Q1000"

到目前为止,问题是......

sequer.__prefix__ // => 'Q' // What??? This was supposed to be private!
sequer.__seq__ = 2000 // Works with no error

... 封装完全被破坏。 seqer受到了损害。