强制上下文

发布于 2024-11-19 20:59:19 字数 1029 浏览 1 评论 0 原文

我有一个类,其中有一个私有属性和一个公共访问方法:

Person = function () {
    this.Name = "asd";

    var _public = new Object();
    _public.Name = function (value) {
        if (value == undefined) { //Get
            return this.Name
        } else {
            this.Name = value; //Set
        }
    };

    return _public;
};

我想强制 _public.Name 中的上下文来访问 this.Name

我知道关闭技术,但我想看看是否可以强制上下文。

我找到了一种技术来做到这一点,扩展对象功能:

Function.prototype.setScope = function (scope) {
    var f = this;

    return function () {
        f().apply(scope);
    }
}

我的类变成:

Person = function () {
    this.Name = "asd";

    var _public = new Object();
    _public.Name = function (value) {
        if (value == undefined) {
            return this.Name
        } else {
            this.Name = value;
        }
    }.setScope(this);

    return _public;
};

所以我可以正确地强制上下文,但我无法传递 value 并且不能返回 this。名称

I have this class where I have a private property and a public method for access:

Person = function () {
    this.Name = "asd";

    var _public = new Object();
    _public.Name = function (value) {
        if (value == undefined) { //Get
            return this.Name
        } else {
            this.Name = value; //Set
        }
    };

    return _public;
};

I want to force the context in _public.Name for access a this.Name.

I know the technique of closure, but I want to see if I can force a context.

I found a technique to do it, extend object Function:

Function.prototype.setScope = function (scope) {
    var f = this;

    return function () {
        f().apply(scope);
    }
}

And my class becomes:

Person = function () {
    this.Name = "asd";

    var _public = new Object();
    _public.Name = function (value) {
        if (value == undefined) {
            return this.Name
        } else {
            this.Name = value;
        }
    }.setScope(this);

    return _public;
};

So I can force correctly the context, but I can not pass value and can not, however, return this.Name.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

飘然心甜 2024-11-26 20:59:19

不仅仅是

f().apply(scope);

f 之后没有

f.apply(scope);

()。)您想在函数 f 对象上使用 apply 函数,而不是调用函数f并在其返回值上访问apply

要传递 setScope 中的函数接收的参数,请添加以下内容:

f.apply(scope, arguments);

arguments 是所有函数的隐式参数,它是传递的实际参数的伪数组到运行时的函数。 apply 接受任何类似数组的东西作为其第二个参数,以指定调用底层函数时要使用的参数。

我还希望它返回返回值:

return f.apply(scope, arguments);

因此 setScope 变为:

Function.prototype.setScope = function (scope) {
    var f = this;

    return function () {
        return f.apply(scope, arguments);
    }
}

Live example

请注意此函数的常用名称以及新 ECMAScript5标准,是bind(第15.3.4.5节;ECMAScript5的bind还允许您柯里化 参数,此实现未完成)。 setScope 是一个特别不幸的名字,因为它不设置范围,而是设置上下文

话虽如此,您没有理由在 Person 构造函数中需要 setScope。你可以这样做:

Person = function () {
    var self = this;

    this.Name = "asd";

    var _public = new Object();
    _public.Name = function (value) {
        if (value == undefined) {
            return self.Name;
        } else {
            self.Name = value;
        }
    };

    return _public;
};

Live example

但是使用 bind (又名 setScope) 在您不希望在执行该操作的上下文中使用新的闭包的地方非常有用。


离题:指定 Person 的方式会破坏人们可能期望的某些功能,例如:

var p = new Person();
alert(p instanceof Person); // Expect "true", but in your case will be "false"

...因为您要替换对象 new 为您创建,但从构造函数中返回不同的对象(覆盖默认值)。

不要创建一个新对象并在构造函数中返回该对象,而是允许由 new 为您构造的对象作为该对象(从而保持 Person 关系),但是您仍然可以获得真正的私有变量并使用访问器:

function Person() {
    // Private variable
    var name = "asd";

    // Accessor function
    this.Name = function(value) {
        if (typeof value === "undefined") {
            return name;
        }
        name = value;
    };
}

Live example

如您所见,这要简单得多,它保留了 instanceof 关系。请注意,我们根本没有限定 Name 中对 name 的引用,因此我们在构造函数调用中使用局部变量,其中 Name创建了关闭它的 函数。

我还冒昧地给构造函数起了一个名称,因为我不喜欢匿名函数。我还应该给访问器一个名称:

function Person() {
    // Private variable
    var name = "asd";

    // Accessor function
    this.Name = Person_Name;
    function Person_Name(value) {
        if (typeof value === "undefined") {
            return name;
        }
        name = value;
    }
}

离题 2:JavaScript 代码中压倒性的约定是仅在构造函数的函数名称上使用首字母大写(例如Person),而不是其他类型的函数(例如 Name)。当然,您可以自由地做任何您喜欢的事情,但我想我应该提到约定,因为它使其他人更容易阅读您的代码。


值得注意的是:所有这些技术都会导致每个Person对象都有其自己的访问器函数副本。如果有很多这些对象,则可能是内存问题。如果只有几个,那也没关系。

Not

f().apply(scope);

just

f.apply(scope);

(No () after f.) You want to use the apply function on the function f object, not call the function f and access apply on its return value.

To also pass on the arguments that your function in setScope receives, add this:

f.apply(scope, arguments);

arguments is an implicit argument to all functions, which is a pseudo-array of the actual arguments passed to the function at runtime. apply accepts any array-like thing as its second parameter to specify the arguments to use when calling the underlying function.

I'd also have it return the return value:

return f.apply(scope, arguments);

So setScope becomes:

Function.prototype.setScope = function (scope) {
    var f = this;

    return function () {
        return f.apply(scope, arguments);
    }
}

Live example

Note that the usual name for this function, and the name it has in the new ECMAScript5 standard, is bind (Section 15.3.4.5; ECMAScript5's bind also lets you curry arguments, which isn't done by this implementation). setScope is a particularly unfortunate name, because it doesn't set the scope, it sets the context.

Having said all that, there's no reason you need setScope in your Person constructor. You can just do this:

Person = function () {
    var self = this;

    this.Name = "asd";

    var _public = new Object();
    _public.Name = function (value) {
        if (value == undefined) {
            return self.Name;
        } else {
            self.Name = value;
        }
    };

    return _public;
};

Live example

But using bind (aka setScope) can be useful in places where you don't want a new closure over the context in which you're doing it.


Off-topic: The way you're specifying Person will break certain things people might expect to work, such as:

var p = new Person();
alert(p instanceof Person); // Expect "true", but in your case will be "false"

...because you're replacing the object new created for you, but returning a different object out of your constructor (which overrides the default).

Rather than creating a new object and returning that in your constructor, allow the object constructed for you by new to be the object (and thus the Person relationship is maintained), but you can still get truly private variables and use accessors:

function Person() {
    // Private variable
    var name = "asd";

    // Accessor function
    this.Name = function(value) {
        if (typeof value === "undefined") {
            return name;
        }
        name = value;
    };
}

Live example

As you can see, this is dramatically simpler, and it preserves the instanceof relationship. Note that we're not qualifying our references to name within Name at all, and so we're using the local variable in the constructor call in which our Name function, which closes over it, was created.

I've also taken the liberty there of giving the constructor function a name, because I'm not a fan of anonymous functions. I should have given the accessor a name as well:

function Person() {
    // Private variable
    var name = "asd";

    // Accessor function
    this.Name = Person_Name;
    function Person_Name(value) {
        if (typeof value === "undefined") {
            return name;
        }
        name = value;
    }
}

Off-topic 2: The overwhelming convention in JavaScript code is to use initial caps on function names only for constructor functions (like Person), and not on other kinds of functions (like Name). You're free to do whatever you like, of course, but I thought I'd mention the convention, as it makes it easier for other people to read your code.


Worth noting: All of these techniques result in every single Person object having its own copy of the accessor function. If there are going to be a lot of these objects, that could be a memory issue. If there are only going to be a few, that's fine.

始于初秋 2024-11-26 20:59:19

首先,我认为正确的方法是“闭包”方法,因为语法更容易理解,也更有意义,并且大多数用 Javascript 编写的面向对象代码都是这样编写的。另一件需要注意的事情是,在您的方法中,可以通过访问 Person.Name (而不是 (new Person()).Name 从外部访问“私有”成员)。

话虽这么说,你似乎想要像 Prototype.JS 的绑定方法 这样的东西,它允许您可以将函数引用绑定为对特定对象的方法调用,并且还正确传递所有参数(包括允许预加载参数)。

查看 Prototype.JS 源代码以获取完整的实现,但此语义的简单实现可能如下所示:

Function.prototype.bind = function(context) {
    var callee = this;
    var args = Array.prototype.slice.call(arguments,1);
    return function() {
        var newargs = args.concat(Array.prototype.slice.call(arguments,0));
        return callee.apply(context, newargs);
    };
};            

First thing, I think the correct way to go about this is the "closure" method, as the syntax is easier and simpler to understand and makes more sense and most object oriented code written in Javascript is written that way. Another thing to note is that in your method, the "private" member can be accessed from outside by accessing Person.Name (instead of (new Person()).Name).

That being said, it seems that you want something like Prototype.JS's bind method, which allows you to bind a function reference as a method call to a specific object, and also passes all the arguments correctly (including allowing preloaded arguments).

Look at Prototype.JS source for the complete implementation, but a simple implementation of this semantic might look like this:

Function.prototype.bind = function(context) {
    var callee = this;
    var args = Array.prototype.slice.call(arguments,1);
    return function() {
        var newargs = args.concat(Array.prototype.slice.call(arguments,0));
        return callee.apply(context, newargs);
    };
};            
呆橘 2024-11-26 20:59:19

很难理解你想要实现的目标。但是,如果我猜测您正在尝试创建一个带有 name 方法的 Person 类来获取/设置人名,那么我的建议是:

function Person() {
  this._name = undefined; // not required but is better than assigning a fake name

  return this;
}

Person.prototype.name = function( _name ) {
  if ( _name === undefined ) return this._name; // get
  return this._name = _name; // set
}

请注意,我已经使用小写首字母定义了 name 函数。这是 JavaScript 中的标准做法,通常只有构造函数是大写的。要使用此类,您需要执行以下操作:

person = new Person();

person.name( "Ermes Enea Colella" );

alert( person.name ); // displays "Ermes Enea Colella"

无需使用此方法绑定任何上下文,因此您可能正在寻找其他内容。如果您能澄清您的需求,我将很乐意编辑我的答案。

我希望这有帮助。

It is difficult to understand what you are trying to achieve. But if I guess that you are trying to create a Person class with a name method to get/set the person's name, here is my suggestion:

function Person() {
  this._name = undefined; // not required but is better than assigning a fake name

  return this;
}

Person.prototype.name = function( _name ) {
  if ( _name === undefined ) return this._name; // get
  return this._name = _name; // set
}

Note that I have defined the name function with a lower case first letter. This is standard practice in JavaScript where only constructors are usually capitalized. To use this class you do:

person = new Person();

person.name( "Ermes Enea Colella" );

alert( person.name ); // displays "Ermes Enea Colella"

There is no need to bind any context with this method, so you may be looking for something else. If you can clarify your need, I'll be happy to edit my answer.

I hope this helps.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文