通过上下文提供对API的访问与通过参数提供对API的访问有什么区别?

What is the difference between offering access to an API through the context or offering it through a parameter?

本文关键字:API 访问 区别 什么 上下文 参数      更新时间:2023-09-26

给定以下API:

SomeAPI = (function() {
  var secretKey = 'passw0rd',
      foo = 'foo',
      bar = 'bar';
  return {
    use : function(callback) { /* Provide the API to the callback */ }
  };
})();

它由几个值组成,最重要的是通过它的use()公共方法使用,该方法允许用户通过回调来使用API。回调可以通过以下两种方式之一访问API:

1)通过this

callbackcall ed或appl[i] ed。使用this完成API:

SomeAPI.use(function() {
  doStuffWith(this.foo);
});
2)通过参数

callback只是用包含API的参数调用。使用给定的参数来消费API:

SomeAPI.use(function(api) {
  doStuffWith(api.bar);
});

这两种模式我都见过。这两者之间有什么实际的区别吗?其中一个可能,而另一个不可能?

两者之间有什么实际的区别吗?其中一个可能,而另一个不可能?

我相信有很多不同之处,但我将尽我所能详细说明最重要的不同之处。

对于第一个示例,将变量赋值给私有对象,然后将私有对象赋值给回调的this

这样做的好处是,我们实际上传递了一个对私有对象的引用,因此我们可以修改该对象。

这样做的缺点是,我们实际上传递了一个对私有对象的引用,因此用户可以随意更改该对象,这从来都不是一件好事。

SomeAPI = (function() {
    var private = {};
    private.secretKey = 'passw0rd';
    private.foo = 'foo';
    private.bar = 'bar';
    return {
        use : function(callback) { callback.call(private); }
    };
})();
console.log(SomeAPI.private); 
// undefined
SomeAPI.use(function(){
    console.log(this.secretKey, this.foo, this.bar); 
    // passw0rd foo bar
    console.log(this.constructor); 
    // Object() { [native code] }
    console.log(this);
    // Object {secretKey: "passw0rd", foo: "foo", bar: "bar"}
    this.hello = 'world';
    this.foo = 'bar';
    this.bar = 'foo';
});
SomeAPI.use(function(){
    console.log(this.secretKey, this.foo, this.bar, this.hello);
    // passw0rd bar foo world
    console.log(this);
    // Object {secretKey: "passw0rd", foo: "bar", bar: "foo", hello: "world"}
});

让我们来盘点一下这显示了什么。

  1. 私有对象实际上是私有的。
  2. this为私有对象。
  3. this包含所有私有属性。
  4. 对私有对象的任何更改都会保留到下一次API调用。

对于第二个示例,我们将每个私有属性的值传递给回调。

这种方法的优点是前一种方法的缺点。此方法锁定所有内容,因此只有您显式允许更改/访问的内容才能更改/访问。

这个方法的缺点是你被锁定在API定义的内容中。

OtherAPI = (function() {
    var secretKey = 'passw0rd',
        foo = 'foo',
        bar = 'bar';
    return {
        use : function(callback) { callback(secretKey, foo, bar); },
        setFoo : function(val) { foo = val },
        setBar : function(val) { bar = val }
    };
})();
console.log(OtherAPI.foo);
// undefined
OtherAPI.use(function(secretKey, foo, bar){
    console.log(secretKey, foo, bar);
    // passw0rd foo bar
    console.log(this.constructor);
    // Window() { [native code] }
    foo = 'bar';
    bar = 'foo';
    console.log(secretKey, foo, bar);
    // passw0rd bar foo
});
OtherAPI.use(function(secretKey, foo, bar){
    console.log(secretKey, foo, bar);
    // passw0rd foo bar
});
OtherAPI.setFoo('bar');
OtherAPI.setBar('foo');
OtherAPI.use(function(secretKey, foo, bar){
    console.log(secretKey, foo, bar);
    // passw0rd bar foo
});

好的,这告诉我们什么?

  1. 私有属性实际上是私有的
  2. 我们只能通过提供的参数读取私有属性的值。
  3. 对回调参数所做的任何更改都将而不是保留到下一次API调用。
  4. 如果我们在返回的API对象中定义函数来更改私有属性的值,我们可以更改单个值。这些更改保留到下一次API调用。

最后还有一个例子,它是两者之间的折衷,如果做得好,可以比前面的任何一个例子都更通用。

这个方法的好处是你可以保护你想保护的东西,并选择用户如何访问私有变量。

这个方法的缺点是你必须考虑用户可能会滥用get和set函数

OtherAPI = (function() {
    var private = {};
    private.secretKey = 'passw0rd';
    private.foo = 'foo';
    private.bar = 'bar';
    private.noaccess = 'No Access!';
    return {
        use : function(callback) { callback.call(this); },
        get : function(prop) { 
            if(private[prop] && prop != 'noaccess') return private[prop];
            return false;
        },
        set : function(prop, val) { 
            if(private[prop] && prop != 'noaccess') return (private[prop] = val);
            return false;
        },
        noaccess : function() { return private.noaccess }
    };
})();
console.log(OtherAPI.secretKey);
// undefined
console.log(OtherAPI.get('secretKey'));
// passw0rd
OtherAPI.use(function(){
    console.log(this.get('secretKey'), this.get('foo'), this.get('bar'), this.get('noaccess'), this.noaccess());
    // passw0rd foo bar false No Access!
    console.log(this.constructor);
    // Object() { [native code] }
    // The above object is actually the public scope, not the private scope
    this.set('foo', 'bar');
    this.set('bar', 'foo');
    this.hello = 'world'; // This will be in the public scope
    console.log(this.get('secretKey'), this.get('foo'), this.get('bar'), this.get('noaccess'), this.noaccess(), this.hello);
    // passw0rd bar foo false No Access! world
});
OtherAPI.use(function(secretKey, foo, bar){
    console.log(this.get('secretKey'), this.get('foo'), this.get('bar'), this.get('noaccess'), this.noaccess(), this.hello);
    // passw0rd bar foo false No Access! world
});

这个例子告诉我们什么?

  1. 和之前一样,private还是private
  2. this指向公共对象(返回给OtherAPI的对象)
  3. 我们现在可以设置获取任何私有属性,只要它们当前存在并且我们没有明确拒绝对该变量的访问。
  4. 您还可以进一步控制可以设置或检索的私有属性,以及如何进行。

这意味着您的API可能比第一个示例更安全,同时比第二个示例更具灵活性,并且在set和get函数中添加了安全检查,它可以与第二个示例一样安全或更安全。

最后,这一切都取决于你有多信任你的API用户,以及你有多想保护私有属性。

在函数中,this对象可以引用众多对象中的任何对象。这一切都取决于如何调用给定的函数,以及apply()的第一个参数是什么。

考虑下面的例子:

var fn = function (api) {
    doStuffWith(this.foo);
    doStuffWith(api.foo);
};

两个doStuffWith调用接收相同的参数,当您以以下方式调用fn时,接收相同的字符串对象:

SomeAPI.use(function(api) {
    fn.apply(api, [api]);
});

除此之外(没有双关语的意思),这完全取决于API命名约定和最佳适用逻辑。两个版本都指向(引用)完全相同的对象,使用导致更详细和干净的代码。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this