ahaow/Blog

js 编写原生插件方式

Opened this issue · 0 comments

ahaow commented

js 编写原生插件

1、

整个插件写在一个立即执行函数里,也可以称为是一个闭包;保证里面的变量不会与外界互相影响,头部的win,doc都是底部window,document的映射,也可以将win,doc理解为函数的形参,window,document为实参,后面的undefined可以不写,写了的话表示保证再出现的undefined是未定义的意思;不被其他赋值;分号则是为了防止js文件合并,可能会发生语法混淆;

;(function(win,doc,undefined){


} (window,document))

2、

unction前的 +(加号或者 ! ~);可以把function 直接转换为可执行的;和用括号括起来一样的作用, 因为!+-()这些符号的运算符是最高的,所以会把其后或者其中的当做表达式处理;故函数会自执行,而直接写function(){}() 会报错,function(){}只是匿名函数声明;而写 var b = function(){}; b()是可以的

+function(){   }()
!function(){   }()
~function(){   }()
viod function(){   }()
(function(){   })()
(function(){   }())

需求

滚动到 页面高度 1400px时,显示出文本 “返回顶部” 的div,并且点击之后滚动到底部;

; (function(win, doc) {
    //开启严格模式,规范代码,提高浏览器运行效率
    "use strict";

    //定义一个类,通常首字母大写
    var Render = function(dom) {

        // 插件名,调用的时候直接new一下插件名就行了并传参数或者传对象
        //(一般这个函数名手写字母大写比较好,构造函数嘛,其实也是函数)        
        //很明显我要传id名;这里传什么都可以的其实; 
        this.dom = document.getElementById(dom);

        //为什么把获取的id传给this.div呢?this的指向为调用的实例;
        //我们此时姑且认为this就指向这个函数;因为这样我们之后再想获取这个
        //div就可以直接用this.div了好吗;而不是在document.getElementById(。。。。) 

        this.dom.style.width = "50px";
        this.dom.style.height = "50px";
        this.dom.style.backgroundColor = "red";
        this.dom.style.position = "fixed";
        this.dom.style.right = "20px";
        this.dom.style.bottom = "20px";
        this.dom.innerHTML = "返回顶部";
        this.dom.style.fontSize = "12px";
        this.dom.style.display = "none";

        //你也可以写一些其他的默认的东西;比如默认的变量啦;方便下面调用;
        //这里写了什么都不会报错;只是有用没用的问题这行可以忽略    
        this.num = 0;
        this.author = "lianxiaozhuang";

        // 执行的函数
        this.scroll();
        this.goTop();
    }

    // 给构造函数render对象原型里添加属性(方法)   
    render.prototype = {
        //给函数写方法;这里可能不止一个函数;你还记得你在全局里写一个个
        //的function吗;贼乱找也不好找;把一个个函数都写到对象的属性里;
        //调用函数就直接调用对象的属性; 
        constructor: render,

        //构造器指向构造函数;这行其实不写没啥毛病;不过防止构造器
        //指向Object的情况;你还是装逼写上吧; 因为当 addHtml.prototype = {}
        //的时候;把一个字面量的对象付给了他的原型上,而{}是由Object产生;
        //故此时addHtml的prototype指向了Object,所以要从新指向

        // 获取scrollTop的兼容写法
        getScrollTop: function() {
            let scrollTop = document.documentElement ? document.documentElement.scrollTop: document.body.scrollTop;
            console.log(scrollTop);
            return scrollTop;
        },

        // scroll滚动监听
        scroll: function() {
            let _self = this; // 把this保存下来防止在局部函数内部取不到(局部函数内部取得this可能
            是别的)window.onscroll = function() {
                let scrollTop = _self.getScrollTop();
                if (scrollTop > 1200) {
                    _self.dom.style.display = "block";
                } else {
                    _self.dom.style.display = "none";
                }
            }

        },

        // 点击返回顶部
        goTop: function() {
            let _self = this;
            let timer = null;
            _self.dom.addEventListener("click",
            function() {
                console.log(66) cancelAnimationFrame(timer);
                timer = requestAnimationFrame(function fn() {
                    let oTop = document.body.scrollTop || document.documentElement.scrollTop;
                    if (oTop > 0) {
                        document.body.scrollTop = document.documentElement.scrollTop = oTop - 100;
                        timer = requestAnimationFrame(fn);
                    } else {
                        cancelAnimationFrame(timer);
                    }
                })
            })
        }

    }

    window.Render = Render ; //把这个对象附给window底下的 名字叫Render 的对象;要不你调用的时候 ,new Render () 怕在window的环境下找不到; 

} (window, document))
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
        <style>
            body {
                height: 2000px;
            }
        
        </style>
    </head>
    <body>
        <div id="scrollTop"></div>
        <script src="./2.js"></script>
        <script>
            let a = new render("scrollTop");
			//这里是实例调用插件的代码    
			//是不是明白为什么要写插件了;要封装;两个相同组件即使有相同的class名在dom操作的时候
			//也不会相互冲突;因为他们都new出来了个自的实例;有自己的this;有自己的一套方法了
			//(其实方法都在原型里是公用的;操作各自的dom)
        </script>

    </body>
</html>
	

3、具有安全作用域的构造函数

function Fn(name){
   this.a = name;
}
Fn.prototype = {
    constructor:Fn,
    getF:function(){
        console.log(1);
    }
}
new Fn();
//new 出来的Fn就是一个构造函数
//倘若有人忘记写new,直接调用Fn();此时var p = Fn();可见实例p没有getF的方法;
//为了解决这种问题;引入了安全作用域的构造函数概念

function Fn(name){
  if(!(this instanceof Fn)){
      //只要不是new的,用Fn()直接调用的,这里的this绝对不指向Fn;让它从
      //新new一下;直到下一次代码走else里的内容,故把this.a 放到了else里
      return new Fn(name)
  }else{
      this.a = name;
  }
}
Fn.prototype = {
    constructor:Fn,
    getF:function(){
        console.log(1);
    }
}

4、默认参数

//我们用过一些插件,是可以传一些配置参数的,当然如果你不传的话它有默认的值,
 function Fn(params){
    var defaults = {
        width:100,
        color:"#000"
    };
    var params = params||{}; 
    for (var x in defaults) {
        if (typeof params[x] === 'undefined') {
            params[x] = defaults[x];
            //对于使用时,没有设置的参数;用默认参数代替
        }  
    }
    this.params= params;//得到的this.params,在方法中调用;
   
 }
 Fn.prototype = {

 }

5、方法到底写到this里还是prototype 里

function Fn(){
    this.getC = function(){
        //...
    }
}

function Fn(){}
Fn.prototype.getC = function(){}
//大家可能都会有这样的纠结,getC到底写到this里还是原型里
//这要从内存说起;写到原型上,每执行一个实例,getC不需要开辟新的内存
//故,可以把一些纯计算的方法,写原型上,如果方法和实例本身有关,应该写道this中

6、方法名防止冲突处理

//如果在引入你的插件之前,window下已经有Fn的变量;怎么办,你如果这么搞; 岂不是把别人写的Fn搞掉了
//此时应该把Fn的控制权交出,自己用Fn2输出
(function(){
    var Fn=function(){
            console.log(000)
        }
        Fn.prototype =  {
            
        }

        if(window.Fn){
           // throw Error("Fn已经存在,请使用Fn2")
            window.Fn2 = Fn	;
           
        }else{
                window.Fn2 = Fn	;
        }
}())

7、兼容 CommonJS 和 AMD/CMD 规范

//兼容CommonJs规范
if (typeof module === 'object' && module && module.exports) { module.exports =Render }

//兼容AMD/CMD规范
if (typeof define === 'function') define(function() { return MyPlugin; });


//注册全局变量,兼容直接使用script标签引入该插件
global.MyPlugin = MyPlugin;
 
//this,在浏览器环境指window,在nodejs环境指global
//使用this而不直接用window/global是为了兼容浏览器端和服务端
//将this传进函数体,使全局变量变为局部变量,可缩短函数访问全局变量的时间

8、完整写法

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
        <style>
            body {
                height: 2000px;
            }
        
        </style>
    </head>
    <body>
        <div id="scrollTop"></div>
        <script src="./2.js"></script>
        <script>
            let a = Render("scrollTop", {
                width: "50px",
                height: "50px",
                backgroundColor: "pink"
            });
        </script>

    </body>
</html>
;(function (global, doc) {
    //开启严格模式,规范代码,提高浏览器运行效率
    "use strict";

    var Render = function (dom,params) {
        // 具有安全作用域的构造函数
        if(!(this instanceof Render)) {
            return new Render(dom,params)
        } else {
            // 默认参数
            var defaults = {
                width: "100px",
                height: "100px",
                backgroundColor: "green"
            }
            var params = params || {}; 
            for(var x in defaults) {
                if(typeof params[x] === 'undefined') {
                    params[x] = defaults[x];
                }
            }
            this.params = params;
            this.dom = document.getElementById(dom);
            this.dom.style.width = this.params.width;
            this.dom.style.height = this.params.height;
            this.dom.style.backgroundColor = this.params.backgroundColor;
            this.dom.style.position = "fixed";
            this.dom.style.right = "20px";
            this.dom.style.bottom = "20px";
            this.dom.innerHTML = "返回顶部";
            this.dom.style.fontSize = "12px";
            this.dom.style.display = "none";
            this.scroll();
            this.goTop();
        }
        
    }

    Render.prototype = {
        constructor: Render,

        getScrollTop: function () {
            let scrollTop = document.documentElement ? document.documentElement.scrollTop : document.body.scrollTop;
            console.log(scrollTop);          
            return scrollTop;
        },

        scroll: function () {
            let _self = this;
            window.onscroll = function () {
                let scrollTop = _self.getScrollTop();
                if(scrollTop > 1200) {
                    _self.dom.style.display = "block";
                } else {
                    _self.dom.style.display = "none";
                }
            }
        },
        aa() {
            console.log(6666666)
        },

        goTop: function() {
            let _self = this;
            let timer = null;
            _self.dom.addEventListener("click",function() {
                console.log(66)
                cancelAnimationFrame(timer);
                timer = requestAnimationFrame(function fn() {
                    let oTop = document.body.scrollTop || document.documentElement.scrollTop;
                    if(oTop > 0) {
                        document.body.scrollTop = document.documentElement.scrollTop = oTop - 100;
                        timer = requestAnimationFrame(fn);
                    } else {
                        cancelAnimationFrame(timer);
                    }
                })
            })
        }
    }
    // 兼容 CommonJs
    if (typeof module === 'object' && module && module.exports) { module.exports = Render }
    // 兼容 AMD/CMD
    if (typeof define === 'function') define(function() { return MyPlugin; });
    // 防止命名冲突
    if(global.Render) {
        global.Render2 = Render;
    } else {
        global.Render = Render;
    }

    // this
}(this, document))