After watching the local derby between Manchester United and Manchester City, there are still two long hours before we can watch the anticipated national derby. I was very bored and had nothing to do, so I came to the forum to hang out. I saw a blog post about image preloading, the code is as follows:
function loadImage(url, callback) {
var img = new Image(); //Create an Image object to pre-download images
img.src = url;
if (img. complete) { // If the image already exists in the browser cache, directly call the callback function
callback(img);
return; // Return directly without processing the onload event
}
img. onload = function () { //Asynchronously call the callback function when the image is downloaded.
callback(img);
};
};
I searched for related articles on the Internet, and they generally have this idea.
The function of this method is OK, but there are some hidden dangers.
1 Create a temporary anonymous function as the onload event handler of the image, forming a closure.
I believe you have all seen articles about memory leak patterns under IE. One of the patterns is circular reference, and closures have the ability to save the external operating environment (depending on the implementation of the scope chain), so img. The onload function also saves a reference to img, which forms a circular reference and causes a memory leak. (This mode of memory leak only exists in lower versions of IE6. The patched IE6 and higher versions of IE have solved the memory leak problem caused by circular references).
2 only considers the loading of static images, ignoring dynamic images such as gifs, which may trigger onload multiple times.
It is very simple to solve the above two problems. In fact, it is very simple. The code is as follows:
img.onload = function () {
//Asynchronously call the callback function when the image is downloaded.
img.onload = null;
callback(img); };
This can not only solve the problem of memory leaks, but also avoid the problem of multiple triggering of dynamic picture events.
In some related blog posts, some people also noticed that img.onload should be set to null, but the timing is wrong. Most of the articles set img.onload to null after the callback is run, which can solve the problem. There is a problem of circular reference, but for dynamic pictures, if the callback is time-consuming to run, there is still a hidden danger of being triggered multiple times.
The hidden danger has been eliminated after the above modifications, but this code still has room for optimization:
if (img.complete) {
// If the image already exists in the browser cache, directly call the callback function
callback(img);
return; / / Return directly without processing the onload event
}
Regarding this code, please read the description in the relevant blog post. The reasons are as follows:
After testing on multiple browser versions, I found that under IE and Opera, after the image has been loaded once, if there is another request for the image, since the browser has already cached the image, it will not initiate a new request, but load it directly from the cache. come over. For Firefox and Safari, they try to make the two loading methods transparent to the user, which will also cause the onload event of the image. However, IE and Opera ignore this identity and will not cause the onload event of the image. Therefore, the above code is No effect can be achieved within them.
Indeed, under IE and Opera, the initial state of cached images is different from that under Firefox, Safari and Chrome (if you are interested, you can test it in different browsers and give img The status of img before src is assigned the URL of the cached image), but the triggering of the onload event is consistent, no matter what browser it is. The root cause of this problem is that the binding of img's src value and onload event is in the wrong order (under IE and Opera, src is assigned first, and then onload is assigned, because the image is cached, and the onload event is missed). You should bind the onload event first, and then assign a value to src. The code is as follows:
function loadImage(url, callback) {
var img = new Image(); //Create an Image object to pre-download images
img.onload = function(){
img .onload = null;
callback(img);
}
img.src = url;
}
In this way, memory leaks and dynamic image loading problems have been solved, and also with A unified way to implement callback invocation.