动态加载文件
业务背景:在加载js或css文件时通常在页头使用head标签加载,然而有的情况需要动态加载文件,例如监听某个事件,当触发时执行加载代码,可以使用如下的方法。
loadedScripts: string[] = [];//已经加载过的资源文件
/**
* @param sctipts
* 某一组件中需要同步加载的js数组 如['a.js',['b.js,c.js'].'d.js']表示a加载完成后,加载b和c,最后加载d
* @param callback
* 全部加载完成后的回调函数
*/
loadScript(path,scripts,callback){
let _t = this;
if(scripts.length){
let item = scripts.shift();
if (typeof item === 'string') {
if (_t.loadedScripts.indexOf(item)>=0) {
_t.loadScript(path,scripts,callback);
}else{
let onload = () => {
_t.loadedScripts.push(item);
_t.loadScript(path,scripts,callback);
}
let name = item.split('.css');
let isCss = name.length===2 && !name[1];
if (isCss) {
let link = document.createElement('link');
document.head.appendChild(link);
link.rel = 'stylesheet';
link.onload = onload;
link.type = 'text/css';
link.href = './assets/lib' + path + '/' + item;
} else {
let script = document.createElement('script');
document.head.appendChild(script);
script.onload = onload;
script.src = './assets/lib' + path + '/' + item + '.js';
}
}
}else if (Object.prototype.toString.call(item)==="[object Array]") {
if (item.length) {
let loadCount = 0;
item.forEach(s=>{
if (_t.loadedScripts.indexOf(s)>=0) {
loadCount++;
_t.loadedScripts.push(s);
if (loadCount===item.length) {
_t.loadScript(path,scripts,callback);
}
} else {
let onload = ()=>{
loadCount++;
_t.loadedScripts.push(s);
if (loadCount===item.length) {
_t.loadScript(path,scripts,callback);
}
}
let name = item.split('.css');
let isCss = name.length===2 && !name[1];
if (isCss) {
let link = document.createElement('link');
document.head.appendChild(link);
link.rel = 'stylesheet';
link.onload = onload;
link.type = 'text/css';
link.href = './assets/lib' + path + '/' + item;
} else {
let script = document.createElement('script');
document.head.appendChild(script);
script.onload = onload;
script.src = './assets/lib' + path + '/' + item + '.js';
}
}
})
} else {
_t.loadScript(path,[],callback);
}
}
}else{
callback && callback(window);
}
P.S. 为什么要用Object.prototype.toString.call(obj)检测对象类型?
首先想到的是typeof,但是它只能检测六种基本数据类型,当需要区分同为Object的各种引用类型时不适用。
其次想到的是instanceof,它是用来用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性;
let arr = [];
let str = "";
arr instanceof Array; //true
str instanceof Array; //false
这样可以达到检测引用类型值的目的,但是不能直接返回值的类型。
所以使用Object.prototype.toString.call(obj)
这里引入另一个问题,同样是将对象转换为字符串,为什么不能使用obj.toString()呢?
因为toString为Object的原型方法,而Array 、Function等类型作为Object的实例,都重写了toString方法。不同的对象类型调用toString方法时,根据原型链的知识,调用的是对应的重写之后的toString方法(Function类型返回内容为函数体的字符串,Array类型返回元素组成的字符串等等),而不会去调用Object上原型toString方法(返回对象的具体类型),所以采用obj.toString()不能得到其对象类型,只能将obj转换为字符串类型;因此,在想要得到对象的具体类型时,应该调用Object上原型toString方法。
let arr=[1,2,3];
console.log(Array.prototype.hasOwnProperty("toString"));//true
console.log(arr.toString());//1,2,3
delete Array.prototype.toString;//delete操作符可以删除实例属性
console.log(Array.prototype.hasOwnProperty("toString"));//false
console.log(arr.toString());//"[object Array]"
当删除Array重写的toString()后,arr.toString()将不再屏蔽Object原型方法的实例方法,而是沿着原型链去调用Object的toString方法,返回结果与Object.prototype.toString.call(arr)相同。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!