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))