强制上下文
Forcing the context
我有这样一个类,其中我有一个私有属性和一个用于访问的公共方法:
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);
}
}
我的class变成了:
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.Name
not
f().apply(scope);
f.apply(scope);
(f
后无()
)您希望在函数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);
}
}
生活例子
注意,这个函数的通常名称,以及它在新的ECMAScript5标准中的名称,是bind
(第15.3.4.5节;ECMAScript5的bind
也允许您curry参数,这在本实现中没有实现)。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;
};
生活例子
但是使用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;
};
}
生活例子
可以看到,这非常简单,并且保留了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
对象都有自己的访问器函数的副本。如果有很多这样的对象,那可能是内存问题。如果只有几个,那也没关系。
首先,我认为正确的方法是"闭包"方法,因为语法更容易理解,更有意义,大多数用Javascript编写的面向对象代码都是以这种方式编写的。另一件要注意的事情是,在你的方法中,"private"成员可以通过访问Person.Name
(而不是(new Person()).Name
)从外部访问。
话虽这么说,似乎你想要类似Prototype.JS的bind方法的东西,它允许你将函数引用绑定为对特定对象的方法调用,并正确传递所有参数(包括允许预加载参数)。
查看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);
};
};
很难理解你想要达到的目的。但是,如果我猜你正试图创建一个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"
不需要将任何上下文绑定到此方法,因此您可能会寻找其他方法。如果你能说明你的需要,我很乐意修改我的答案。
- 将函数的上下文应用于javascript变量
- Twitter Bootstrap typeahead:使用“this”获取上下文/调用元素
- 使用JQuery的动态上下文菜单
- 如何访问UIWebView'的子窗口上下文
- JQuery在单击正文时隐藏上下文菜单
- JQuery上下文菜单显示/隐藏问题
- 如何从HTTP上下文对象中获取Post数据
- HTML字符串作为上下文
- 为什么我的上下文选择器和.buttonset()在ie中花费了这么长时间
- 丢失对象“;这个“;方法中的上下文
- 防止在移动Safari(iPad/iPhone)中长按/长按默认上下文菜单
- d3防止在上下文菜单上触发mouseout
- 是否可以在不更改上下文的情况下调用函数.apply
- 调用$.each()函数时上下文发生变化
- 何时可以;我的用户脚本在Javascript中触发右键单击(上下文菜单)
- 如何向onClick事件处理程序传递一个接受参数的函数,并且仍然将该函数绑定到组件's”;这个“;上下文
- Ember.js:将Em.$.getJSON转换为promise并将响应绑定到控制器上下文的正确方法
- File Upload事件上下文和javascript
- 使用JSTree上下文菜单捕获新创建的节点
- 访问函数对象的上下文属性|如何