introduce
As the name suggests, agents are to help others do things. GoF defines the proxy model as follows:
Proxy mode (Proxy), provides a proxy for other objects to control access to this object.
The proxy mode allows the proxy object to control the reference of a specific object. A proxy can be almost any object: a file, a resource, an object in memory, or something that is difficult to copy.
text
Let’s give a simple example. If Dudu wants to send yogurt and little girl roses, but doesn’t know her contact information or is embarrassed, and wants to entrust the uncle to send these roses, then the uncle is an agent (actually, it’s pretty good, you can deduct a few flowers to your wife), so how can we do it?
The code copy is as follows:
// Declare the beauty first
var girl = function (name) {
this.name = name;
};
// This is dudu
var dudu = function (girl) {
this.girl = girl;
this.sendGift = function (gift) {
alert("Hi " + girl.name + ", dudu gives you a gift: " + gift);
}
};
// The uncle is the agent
var proxyTom = function (girl) {
this.girl = girl;
this.sendGift = function (gift) {
(new dudu(girl)).sendGift(gift); // Send flowers for dudu
}
};
The call method is very simple:
The code copy is as follows:
var proxy = new proxyTom(new girl("yogurt girl"));
proxy.sendGift("999 roses");
A practical battle
Through the above code, I believe everyone is very clear about the proxy mode. Let’s take the actual combat: We have a simple playlist, and we need to display the video song introduction and play button below the connection when clicking on a single connection (or select all). When clicking the play button, the video is played. The list structure is as follows:
The code copy is as follows:
<h1>Dave Matthews vids</h1>
<p><span id="toggle-all">Select all/reselect</span></p>
<ol id="vids">
<li><input type="checkbox" checked><a href="http://new.music.yahoo.com/videos/--2158073">Gravedigger</a></li>
<li><input type="checkbox" checked><a href="http://new.music.yahoo.com/videos/--4472739">Save Me</a></li>
<li><input type="checkbox" checked><a href="http://new.music.yahoo.com/videos/--45286339">Crush</a></li>
<li><input type="checkbox" checked><a href="http://new.music.yahoo.com/videos/--2144530">Don't Drink The Water</a></li>
<li><input type="checkbox" checked><a href="http://new.music.yahoo.com/videos/--217241800">Funny the Way It Is</a></li>
<li><input type="checkbox" checked><a href="http://new.music.yahoo.com/videos/--2144532">What Would You Say</a>
</li>
</ol>
Let’s first analyze the following. First, we not only monitor the click event of connection a, but also monitor the click event of “select all/anti-select”, and then request the server to query the video information and assemble the HTML information to display it at the last position of the li element. The effect is as follows:
Then monitor the click event of the play connection, and start playing after clicking. The effect is as follows:
OK, start with, without jQuery, let's customize a selector:
The code copy is as follows:
var $ = function (id) {
return document.getElementById(id);
};
Since Yahoo's json service provides callback parameters, we pass in our custom callback to accept data. The specific query string assembly code is as follows:
The code copy is as follows:
var http = {
makeRequest: function (ids, callback) {
var url = 'http://query.yahooapis.com/v1/public/yql?q=',
sql = 'select * from music.video.id where ids IN ("%ID%")',
format = "format=json",
handler = "callback=" + callback,
script = document.createElement('script');
sql = sql.replace('%ID%', ids.join('","'));
sql = encodeURIComponent(sql);
url += sql + '&' + format + '&' + handler;
script.src = url;
document.body.appendChild(script);
}
};
The proxy object is as follows:
The code copy is as follows:
var proxy = {
ids: [],
delay: 50,
timeout: null,
callback: null,
context: null,
// Set the requested id and callback to trigger the callback during playback
makeRequest: function (id, callback, context) {
// Add to queue dd to the queue
this.ids.push(id);
this.callback = callback;
this.context = context;
// Set timeout
if (!this.timeout) {
this.timeout = setTimeout(function () {
proxy.flush();
}, this.delay);
}
},
// Trigger the request, and http.makeRequest was called using proxy responsibilities
flush: function () {
// proxy.handler is the callback when requesting yahoo
http.makeRequest(this.ids, 'proxy.handler');
// After requesting data, execute the proxy.handler method (there is another callback set inside)
// Clear timeout and queue
this.timeout = null;
this.ids = [];
},
handler: function (data) {
var i, max;
// Callback call for a single video
if (parseInt(data.query.count, 10) === 1) {
proxy.callback.call(proxy.context, data.query.results.Video);
return;
}
// Callback calls for multiple videos
for (i = 0, max = data.query.results.Video.length; i < max; i += 1) {
proxy.callback.call(proxy.context, data.query.results.Video[i]);
}
}
};
The video processing module mainly has three sub-functions: obtaining information, displaying information, and playing videos:
The code copy is as follows:
var videos = {
// Initialize the player code and start playing
getPlayer: function (id) {
return '' +
'<object id="uvp_fop" allowFullScreen="true">' +
'<param name="movie" value="http://d.yimg.com/m/up/fop/embedflv/swf/fop.swf"/////>' +
'<param name="flashVars" value="id=v' + id + '&eID=1301797&lang=us&enableFullScreen=0&shareEnable=1"///>' +
'<param name="wmode" value="transparent"///>' +
'<embed ' +
'height="255" ' +
'width="400" ' +
'id="uvp_fop" ' +
'allowFullScreen="true" ' +
'src="http://d.yimg.com/m/up/fop/embedflv/swf/fop.swf" ' +
'type="application/x-shockwave-flash" ' +
'flashvars="id=v' + id + '&eID=1301797&lang=us&ympsc=4195329&enableFullScreen=1&shareEnable=1"' +
'///' +
'<//object>';
},
// Splice the information to display the content, and then display it in the bottom of append to li
updateList: function (data) {
var id,
html = '',
info;
if (data.query) {
data = data.query.results.Video;
}
id = data.id;
html += '<img src="' + data.Image[0].url + '" ///>';
html += '<h2>' + data.title + '<//h2>';
html += '<p>' + data.copyrightYear + ', ' + data.label + '<//p>';
if (data.Album) {
html += '<p>Album: ' + data.Album.Release.title + ', ' + data.Album.Release.releaseYear + '<br ///>';
}
html += '<p><a href="http://new.music.yahoo.com/videos/--' + id + '">» play</a><//p>';
info = document.createElement('div');
info.id = "info" + id;
info.innerHTML = html;
$('v' + id).appendChild(info);
},
// Get information and display it
getInfo: function (id) {
var info = $('info' + id);
if (!info) {
proxy.makeRequest(id, videos.updateList, videos); //Execute the proxy responsibilities and pass in the videos.updateList callback function
return;
}
if (info.style.display === "none") {
info.style.display = '';
} else {
info.style.display = 'none';
}
}
};
Now we can handle the code for click events. Since there are many A connections, if each connection is bound to an event, there will obviously be a problem with performance. So we bind the event to the <ol> element and then detect whether the clicked connection is an a connection. If it means that the clicked is the video address, and then it can be played:
The code copy is as follows:
$('vids').onclick = function (e) {
var src, id;
e = e || window.event;
src = e.target || e.srcElement;
// If it is not a connection, I won't continue processing
if (src.nodeName.toUpperCase() !== "A") {
return;
}
//Stop bubble
if (typeof e.preventDefault === "function") {
e.preventDefault();
}
e.returnValue = false;
id = src.href.split('--')[1];
//If you click on the link play in the video information area that has been produced, playback will start
// Then return will not continue
if (src.className === "play") {
src.parentNode.innerHTML = videos.getPlayer(id);
return;
}
src.parentNode.id = "v" + id;
videos.getInfo(id); // This is the processing code for displaying video information when clicking for the first time
};
The codes for choosing all and choosing are similar, so we won't explain:
The code copy is as follows:
$('toggle-all').onclick = function (e) {
var hrefs, i, max, id;
hrefs = $('vids').getElementsByTagName('a');
for (i = 0, max = hrefs.length; i < max; i += 1) {
// Ignore the play connection
if (hrefs[i].className === "play") {
continue;
}
// Ignore items that are not selected
if (!hrefs[i].parentNode.firstChild.checked) {
continue;
}
id = hrefs[i].href.split('--')[1];
hrefs[i].parentNode.id = "v" + id;
videos.getInfo(id);
}
};
Summarize
The proxy mode is generally suitable for the following situations:
1. Remote proxy, that is, to provide local representations for an object in different address spaces, so that the fact that an object exists in different address spaces can be hidden, just like the proxy class in the web service.
2. Virtual agents create objects with high overhead as needed, and use them to store real objects that take a long time to instantiate. For example, when rendering the browser, the problem can be displayed first, and the image can be displayed slowly (that is, the virtual agent replaces the real image. At this time, the virtual agent saves the path and size of the real image.
3. Security agent, used to control the permissions when accessing real objects. Generally, objects should have different access permissions.
4. Intelligent guidance, only when the real object is called, the agent handles other things. For example, garbage collection in C# will have references when using an object. If the object has no references, GC can recycle it.
Reference: "Big Talk Design Pattern"