[目录]
- 面向过程写函数
- 什么是面向过程?什么是面向对象?
- 【面向过程】和【面向对象】写函数的不同
- 【面向过程】写函数的优缺点
- 面向对象写函数
- 单个对象写函数
- 构造函数写函数(类)
- 给构造函数的原型对象写函数
面向过程写函数
什么是面向过程?什么是面向对象?
面向过程和面向对象都是一种编程的思想,也可以理解为一种代码的组织形式。面向过程主要关注于解决问题需要一步一步实现的过程,而面向对象主要关注于把解决的不同问题分给不同的对象管理,面向对象不关注于解决问题的步骤,只关注于解决问题的对象。
面向过程和面向对象写函数的不同
- 面向对象是相对于面向过程发展来的;
- 面向过程是在代码量比较少、功能简单的场景中适用;面向过程一般适用于项目规则较大,代码量较多;
- 面向过程所有的事情都是需要我们自己来完成的,亲历亲为;面向对象是完成一件事情,只需要找到某个对象,让它帮我们完成即可;
- 面向过程中我们处于执行者的角色;面向对象中我们处于一个调度者的角色;
- 面向过程的方式要比面向对象的方式代码执行效率高;但是面向对象对于大型项目来说,有利于多人合作开发、项目代码的维护和更新迭代
以js和jq为例进行以下简单分析:
//js面向过程实现一个将一个标签添加到页面中的一个功能
//第一步:创建一个p标签
var p = document.createElement('p');
//第二步: 用最原始的方式创建一个文本节点
var txt = document.createTextNode('creat TEXT');
//第三步:将文本节点添加到p标签中
p.appendChild(txt);
//ps:这个方法我们平时不常用,因为我们经常使用innerText方法,可以看作innerText中封装了createTextNode方法
//第四步:将p标签添加到页面中去
document.body.appendChild(p);
//jq是面向对象编程的
//找到jq对象,使用jq对象的append方法
$('body').append('<p>create TEXT</p>');
【面向过程】写函数的优缺点
- 优点:面向过程的方式要比面向对象的方式代码执行效率高,简单易懂,没有复杂的逻辑结构。
- 缺点:不利于合作开发,不利于代码的维护和扩展。最重要的是会造成严重的全局污染。
写方法我们一般会经历下面的几个步骤:
//一开始我们写函数都是这样声明的
function checkName(){
console.log("name");
}
function checkEmail(){
console.log("email");
}
function checkPassword(){
console.log("key");
}
//同理,也可以使用变量声明
var checkName = function(){
console.log("name");
}
var checkEmail = function(){
console.log("email");
}
var checkPassword = function(){
console.log("key");
}
//调用方式
checkName();//name
这两种方式效果都是一样的,但是这样声明了3个全局变量。全局污染很容易造成命名冲突的问题,声明全局变量就会有定义了相同的方法,后面的就把原来的进行覆盖了。
比如:
function happy(){
console.log("hello!");
}
function happy(){
console.log("world!");
}
happy();
//输出结果为 world! 后面的方法将前面的覆盖了,导致前面的不能使用
不仅如此,全局污染 的问题是开发中值得关注的问题。
所以我们使用一个对象,对象有自己的属性和方法,如果我们要访问它的属性和方法,访问这个对象点出来属性和方法就可以了,所以我们可以创建一个对象,然后把方法放在里面:
var CheckObj = {
checkName:function(){
console.log("name");
},
checkEmail:function(){
console.log("email");
},
checkPassword:function(){
console.log("key");
}
}
//这样我们使用的时候就用对象点方法
CheckObj.checkName(); //name
当然还有另一种形式,因为在js中,函数也是对象
var CheckObj = function(){};
CheckObj.checkName = function(){
console.log("name");
}
CheckObj.checkEmail = function(){
console.log("email");
}
CheckObj.checkPassword = function(){
console.log("key");
}
//使用的方式还是和以前是一样的
CheckObj.checkName(); //name
这样虽然可以满足我的需求,但是当别人想用我写的方法时就麻烦了。因为这个对象不能复制,或者说在new一个实例对象出来的时候,并不能去继承这些方法。
所以要想使用这些方法,就要写到一个函数对象(构造函数)中:
var CheckObj = function(){
return {
checkName : function(){
console.log("name");
},
checkEmail : function(){
console.log("email");
},
checkPassword : function(){
console.log("key");
}
}
}
//这样写的原理是,每次在调用这个函数的时候,都把对象返回出来,每次调用这个函数就返回一个新的对象。
//这样在执行的是CheckObj,但实际上返回了一个新的对象。这样使用的时候就互不影响了。
var user = CheckObj();
user.checkName(); //name
//new 也可以继承了,但是一般不这么写
var user1 = new CheckObj();
user1.checkEmail(); //name
虽然这个创建了新的对象完成了我们的需求,但是他不是一个真正意义上类的创建方式,并且上面创建的user和CheckObj没有任何的关系
所以需要进行改造:
var CheckObj = function(){
this.checkName = function(){
console.log("name");
}
this.checkEmail = function(){
console.log("email");
}
this.checkPassword = function(){
console.log("key");
}
}
//这样就是一个类了,也是我们说的构造函数,下面的user就是这个构造函数的实例对象,每一个new出来的实例对象都继承了CheckObj的属性和方法
var user = new CheckObj();
user.checkName(); //name
我们将所有的方法放到函数的内部,通过this定义,每一个实例对象创建的时候都会对类的this上的属性进行复制(这种复制是一种深拷贝的复制)。所以这些新创建的对象都有一套自己的方法,验证如下:
var user = new CheckObj();
user.checkName(); //name
user.checkEmail = function(){
console.log("happy")
} //对user的checkEmail方法重新进行声明
var user1 = new CheckObj();
user1.checkEmail(); //email
user.checkEmail(); //happy
//由此可以看出,修改其中一个实例对象的方法的时候,并没有影响原来的方法,复制是深拷贝的。
深拷贝的话,有时候会造成不必要的消耗,以为会复制很多的相同的对象
解决的办法就是将这些方法都放到这个构造函数的原型对象上去:
var CheckObj = function(){};
CheckObj.prototype.checkName = function(){
console.log("name");
}
CheckObj.prototype.checkEmail = function(){
console.log("email");
}
CheckObj.prototype.checkPassword = function(){
console.log("key");
}
//简洁一点也可以这么写
CheckObj.prototype = {
checkName : function(){
console.log("name");
},
checkEmail : function(){
console.log("email");
},
checkPassword : function(){
console.log("key");
}
}
//这样创建出来的对象所拥有的方法就是一个了,因为它们都要到这个对象的原型链上去寻找其原型对象的方法。
写个例子明白一下:
var Happy = function(){};
Happy.prototype = {
hello : function(){
console.log("hello");
},
world : function(){
console.log("world");
}
}
var happy = new Happy();
var unHappy = new Happy();
happy.hello(); // hello
unHappy.hello(); //hello
Happy.prototype.hello = function(){
console.log("heheda");
}
happy.hello(); //heheda 这说明刚才那个方法把前面的原型对象里面的方法覆盖掉了,所以进行不要混用。
unHappy.hello(); //heheda
//这样也可以说明了,happy和unHappy用的是一个方法
上面的声明,如果要进行函数的调用,会连续写很多次,可以进行修改,链式调用
var CheckObj = function(){};
CheckObj.prototype = {
checkName : function(){
console.log("name");
return this;
},
checkEmail : function(){
console.log("email");
return this;
},
checkPassword : function(){
console.log("key");
return this;
}
}
//使用
var check = new CheckObj();
check.checkName().checkEmail().checkPassword(); //name email key
上面基本上就是面向对象的一种方式,下面是更高级的
prototype.js
是一款前端框架,里面可以封装很多方法,其最大的特点就是对源生对象的扩展,源生对象有什么?Function,Array,Object
在原型链中,每一个function 都是由Function对象new出来的,所以如果把方法写到Function的原型对象中,那么只要是函数,就可以继承这个方法。上个例子:
//在Function的原型对象上面声明一个方法
Function.prototype.checkEmail = function(){
console.log("email");
}
//声明一个函数,里面就可以继承这个方法
var f = function(){};
f.checkEmail();
//或者
var ff = new Function();
ff.checkEmail();
But!!!!!!这种方式是不允许的,因为这样会污染原生对象!!!!!
所以要抽象出一个统一添加方法的功能方法:
Function.prototype.addMethod = function(name,fn){
this[name] = fn;
return this; //返回的是Function所创建出来的实例对象
}
//添加方法(函数式的调用方式)
var method = function(){}; //或者 var method = new Function();
var m = method.addMethod('checkName',function(){
console.log("name");
})
console.log(m); // m接收的是method对象
//所以可以进行链式编程
method.addMethod('checkName',function(){
console.log("name");
return this; //this返回的是method函数对象
}).addMethod('checkEmail',function(){
console.log("email");
return this;
}).addMethod('checkPassword',function(){
console.log("key");
return this;
})
//调用
methods.checkEmail().checkName();// email name 之所以可以链式编程是因为返回的还是
//-------------------------------------------------------------------
//!!!!!类的调用方式!!!!!
Function.prototype.addMethod = function(name,fn){
this.prototype[name] = fn;//给Methods的原型对象上面添加方法
return this; //返回的是Method,Function创建出来的实例对象
}
var Methods = function(){}; //Function创建出来构造函数Methods
Methods.addMethod('checkName',function(){//之所以可以链式编程是因为返回的还是Methods
console.log("name");
return this; //返回的是m,Methods创建出来的实例对象
}).addMethod('checkEmail',function(){
console.log("email");
return this;
});
var m = new Methods(); //Methods构造函数创建出来实例对象m,m继承了所有Methods的原型对象的所有方法
m.checkEmail().checkName();// email name 之所以可以链式编程是因为返回的还是m
下面完成作业
1.链式编程的话在函数体里面记得写return this
2.给函数添加多个方法的addMethod方法
Function.prototype.addMethod = function(name,fn){
this[name] = fn;
return this;
}
var me = function(){};
me.addMethod("happy",function(a){
console.log("happy"+a);
return this;
}).addMethod("unHappy",function(){
console.log("rain");
return this;
})
me.happy(" time").unHappy(); //happy time , rain
3.既可以为函数原型添加方法又可以为其自身添加方法的addMethod的方法
Function.prototype.addMethod = function(name,fn){
this[name] = fn;
this.prototype[name] = fn;
return this;
}
var Me = function(){};
Me.addMethod("happy",function(a){
console.log("hello" + a);
return this;
}).addMethod("unHappy",function(){
console.log("no");
return this;
})
Me.happy("Me").unHappy(); //helloMe no
var m = new Me();
m.happy("m").unHappy(); //hellom no
但是上面那种方法,调用一次addMethod方法会在函数自身和原型对象上加两个相同的方法,这样是不科学的。我目前想到的方法是再传一个参数,判断是给原型对象加还是给自己加,但是我觉得应该是addMethod里面有两个不同的方法,然后调用的时候调用addMethod中的不同方法就可以了。
下面你们有什么高见,可以题出来探讨 ^ ^