【官方地址】
http://developer.appcelerator.com/blog/2012/02/what-is-a-titanium-proxy-object.html
如果你经常关注我们的开发Blog,而且阅读了我们基于wiki的文档,你可能会多次看到过“proxy对象”的说法。简单来说,proxy是一个特殊的JavaScript对象,用来代替原生(iOS或者Android,在mobile web是不需要这个)代码的相应对象。
//这里的"button"就是一个proxy对象
var button = Ti.UI.createButton({
title:'push me!',
height:50,
width:200
});
如果你想详细的知道什么是proxy?它如何工作的?可以查看Module开发指南,它详细讲解了为iOS或者Android开发你自己Module的整个过程。
任何时候你访问Ti.* JavaScript命名空间中的函数或者属性,或者使用从这个命名空间的函数返回的对象时,这个对象就是某种类型的proxy。
为了更好的实现JavaScript Conext和原生代码的“桥接”,这些proxy对象被赋予了特殊的功能:允许原生代码(Java, C, 或Objective-C)拦截对象的分配以及函数的调用。这种交互的模式允许开发者能使用JavaScript编写原生代码,使Titanium有别于许多其他跨平台工具。
Proxy是特殊的
作为Titanium平台的使用者,理解这些对象的是很有帮助的,至少在Titanium1.X或者可能在Titanium2.X的很长一段时间里,proxy对象不会总是顺从JavaScript的规则。那么让我们来看看一些没有延续JavaScript规则的地方。
嵌套对象
对于普通的JavaScript对象,你可以为对象(只要这些属性对象存在)设置嵌套属性,比如“meaning.of.life = 42;”,但对于Proxy对象来说是不可以的。一个比较有代表性的例子,你会用到Label的“font”属性,比如如下例子中,“l”是一个Proxy对象,它有一个内嵌的属性“font”,然后为它设置 fontSize。当我们试着直接给它设置嵌套属性fontSize的时候,它是不能够运行的,而是必须设置整个font对象给这个值。
var w = Ti.UI.createWindow({
backgroundColor:'white'
});
var l = Ti.UI.createLabel({
text:'here is a label',
font: {
fontFamily:'Trebuchet MS'
}
});
w.add(l);
//运行不了
l.font.fontSize = 24;
//可以正常运行
l.font = {
fontFamily:'Trebuchet MS',
fontSize: 24
};
w.open();
覆盖属性
Proxy对象的另外一个特殊地方是,对于作为proxy对象的公开接口的函数(Titanium预定使用函数和属性)是不能被复写的。以下是一个典型的例子:
var window = Ti.UI.createWindow({
backgroundColor:'red'
});
window.open = function() {
alert('overriding open!');
};
window.open();
当你运行这段代码时,你会发现,“open”是Proxy对象的一个公共接口函数,我们写得代码只是残留着,并不是我们想想的这个函数被覆盖了。
Getters 和 Setters
特殊的函数名有他们特殊的用处。任何以“get”和“set”开头的函数都被委托给原生代码,所以任何你想设置给Proxy的自定义getter和setter都将会被忽略。
var window = Ti.UI.createWindow({
backgroundColor:'red'
});
window.getSomething = function() {
alert('getter function');
};
window.setSomething = function(something) {
alert('trying to set '+something);
};
window.open();
//被忽略
window.getSomething();
window.setSomething('foo');
.apply 和.call
一般你可以使用JavaScript的.apply 和.call的来调用函数,因为函数是一个类对象。但是对于Titanium的Proxy对象来说,由于其有着特殊的构造,是不允许这样被调用的。以下代码虽然是合理的,但是运行之后会出错。
var window = Ti.UI.createWindow.call(this,{
backgroundColor:'red'
});
window.open();
包装Proxy
有时候这种行为比较烦人,因为对于Ti.*命名空间下的对象,我们想使用JavaScript的所有技巧。当我们使用Proxy处理问题时,一般我们都是在普通的JavaScript对象中包装一个Proxy对象,不直接访问原生Proxy。比如下边这个简单的例子,他可以放到Titanium的根目录下。
Resources/WrappedWindow.js
//Proxy wrapper
function WrappedWindow(args) {
this.proxy = Ti.UI.createWindow(args);
}
WrappedWindow.prototype.open = function(args) {
this.proxy.open(args);
};
WrappedWindow.prototype.setBackgroundColor = function(color) {
//this works because WrappedWindow is just a plain ol' JS object!
this.proxy.animate({
backgroundColor:color,
duration:4000
});
};
module.exports = WrappedWindow;
Resources/app.js
var WrappedWindow = require('WrappedWindow');
var w = new WrappedWindow({
backgroundColor:'white'
});
w.open();
w.setBackgroundColor('red');
使用这个技巧,你可以避开Proxy对象的特殊行为,因为直接操作他们是很麻烦的。
总结
Proxy对象是你使用JavaScript编写原生代码的一个桥梁。然而他们有一些特殊的规则适用于他们。需要特别注意的是:
- 不能设置嵌套属性
- 不能复写Proxy对象公开API的属性
- 以“”和“”开发的函数是被拦截的
- Proxy对象的.call 和 .apply是不能运行的