类与原型的结合使用
function Fn(){}
Fn.run=function(){} //这种写法与ES6中的静态方法一样
Fn.prototype.play=function(){} //这种写法就是正常的执行方法
实例化时会将构造函数的prototype对象下的所有方法和属性添加在实例化对象的原型链中
类的原型等于实例化对象的原型链
定义一个父类,并利用原型设置类中的方法,同时将被覆盖的构造函数重新设定出来
function Box(){
}
Box.prototype={
a:1,
run:function(){
},
play:function(){
}
}
Object.defineProperty(Box.prototype,"constructor",{
value:Box
})
var b=new Box();
console.log(Box.prototype===b.__proto__); //true
类的原型(Box.prototype)与实例化的对象的原型链(b.proto)相同。
所以:
在任何类的原型上设置了方法,就能在该类的实例化对象中调用该方法。
例如:
数组的原型=数组的实例化, 所以在原型中添加了方法,在所有实例化的数组中的原型链中就存在了该方法。
Array.prototype.setWidth=funtion(){} 这就像等于给所有实例化的数组增加了setWidth这个方法。
给Div的类中添加多个方法,使实例化的div能够随意调用
Object.defineProperties(HTMLDivElement.prototype,{
_drag:{
writable:true,
value:false,
},
width:{
set:function(_v){
this.style.width=_v.toString().indexOf("px")>-1 ? _v : _v+"px";
},
get:function(){
return parseFloat(getComputedStyle(this).width); //经过计算后的css样式
}
},
height:{
set:function(_v){
this.style.height=_v.toString().indexOf("px")>-1 ? _v : _v+"px";
},
get:function(){
return parseFloat(getComputedStyle(this).height);
}
},
bgColor:{
set:function(_v){
this.style.backgroundColor=(typeof _v==="string") ? _v : "#"+_v.toString(16).padStart(6,"0");
},
get:function(){
return parseFloat(getComputedStyle(this).backgroundColor);
}
},
drag:{
set:function(_v){
this._drag=_v; //通过drag的bool值控制是否能够拖拽
if(_v){
this.style.position="absolute";
return this.addEventListener("mousedown",this.dragHandler);
//这里加return的原因:添加了侦听事件后直接跳出,否则会就地移除刚侦听的事件
}
this.removeEventListener("mousedown",this.dragHandler);
},
get:function(){
return this._drag;
}
},
dragHandler:{
value:function(e){
if(e.type==="mousedown"){
e.preventDefault();
//因为下面侦听的源头变成了document,所以要将div的this存储下来。
document.target=this;
document.offset={x:e.offsetX,y:e.offsetY};
document.addEventListener("mousemove",this.dragHandler)
document.addEventListener("mouseup",this.dragHandler)
}else if(e.type==="mousemove"){
//this.target是上面存的document.target,this.offset是上面存的document.offset.
this.target.style.left=e.clientX-this.offset.x+"px";
this.target.style.top=e.clientY-this.offset.y+"px";
}else if(e.type==="mouseup"){
document.removeEventListener("mousemove",this.target.dragHandler)
document.removeEventListener("mouseup",this.target.dragHandler)
}
}
}
})
给数组原型添加方法
Array.prototype.some1=function(fn){
for(var i=0;i<this.length;i++){
if(fn(this[i],i,this)) return true;
}
return false;
}
var arr=[2,4,6,8,10];
var b=arr.some1(function(item,index,arr){
return item%2===1;
});
console.log(b);
数组的原型方法
var arr=Array.prototype.slice.call(document.querySelectorAll(“div”)); //转换为数组 使用call的目的只是为了改变slice中的this。
var arr=Array.prototype.concat.apply([],document.querySelectorAll(“div”)); //将空数组与后面的参数连接。
给函数封装一个柯里化函数
Function.prototype.currying=function(){
var arr=[];
var self=this;
return function(){
if(arguments.length>0){
arr=arr.concat(Array.from(arguments));
return arguments.callee;
}
return self.apply(null,arr);
}
}
function getSum(){
return Array.from(arguments).reduce((value,item)=>value+=item);
}
var f=getSum.currying();
f(1);
f(2,3,4);
console.log(f());
给函数封装一个反柯里化函数
Function.prototype.unCurrying=function(){
var self=this; //存一下push中的this
return function(){
return Function.prototype.call.apply(self,arguments);//arguments [[1,2,3,4],5]
/* 当前实例的函数为数组中的push方法函数,所以这里就相当于
Function.prototype.call.apply(push,arguments)//这里可以理解为将Function.prototype实例化为了push。
push.call(arguments) ==> push.call([1,2,3,4],5)
[1,2,3,4].push(5);
*/
// push.call.apply(this,[[1,2,3,4],5]);
// this.push.call([1,2,3,4],5);
// [1,2,3,4].push(5)
// call(arguments[0],arguments[1])
}
}
var push=Array.prototype.push.unCurrying(); //这里Array.prototype.push将相当于一个实例化的Function
/* push就相当于function(){
return Function.prototype.call.apply(self,arguments);//arguments [[1,2,3,4],5]
}
但此时push里面的this是一个数组,而不是想要改变的arguments。
*/
function getSum(){
push(arguments,5); //push的参数带入了push.unCurrying()中
console.log(arguments)
}
getSum(1,2,3,4);
~function(){
}()
与下面的写法意思相同
(function(){
})();
给元素封装一个侦听事件的兼容写法
EventTarget.prototype.on=function(type,handler,bubbles){
try{
this.addEventListener(type,handler,bubbles);
}catch(e){
this.attachEvent("on"+type,handler);
}
}
则侦听的时候无论是否兼容,可以直接:
(div的逐层原型中也有EventTarget)
div.on("click",clickHandler);
function clickHandler(e){
}
给对象的原型中增加克隆的方法
/*
这里参数中省略了要复制的对象,因为在对象的原型增加了克隆方法后可由源对象直接调用该方法,则此时该方法中this即为要克隆的源对象。
*/
Object.prototype.cloneObject=function(target){
if(target==undefined) target={};
var names=Object.getOwnPropertyNames(this);
for(var i=0;i<names.length;i++){
var desc=Object.getOwnPropertyDescriptor(this,names[i]);
if(typeof desc.value==="object" && desc.value!==null){
var obj;
switch(true){
case desc.value.constructor===Date:
obj=new Date(desc.value.toString());
break;
case desc.value.constructor===RegExp:
obj=new RegExp(desc.value.source,desc.value.flags);
break;
case HTMLElement.isPrototypeOf(desc.value.constructor):
obj=document.createElement(desc.value.nodeName);
break;
default:
obj=new desc.value.constructor()
}
Object.defineProperty(target,names[i],{
enumerable:desc.enumerable,
writable:desc.writable,
configurable:desc.configurable,
value:obj
});
desc.value.cloneObject(obj);//要递归时直接将要复制的对象.cloneObject(目标对象)即可。
为什么此时的目标对象是obj呢?因为上面的switch语句根据源对象的类型创建好了一个obj空壳,此时要将真实的数据复制进去。
}else{
Object.defineProperty(target,names[i],desc)
}
}
return target;
}