动态加载文件

  业务背景:在加载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 协议 ,转载请注明出处!