【面向对象一】从面向过程到面向对象写函数方法的进化

[目录]

  • 面向过程写函数
    • 什么是面向过程?什么是面向对象?
    • 【面向过程】和【面向对象】写函数的不同
    • 【面向过程】写函数的优缺点
  • 面向对象写函数
    • 单个对象写函数
    • 构造函数写函数(类)
    • 给构造函数的原型对象写函数

面向过程写函数

什么是面向过程?什么是面向对象?

面向过程和面向对象都是一种编程的思想,也可以理解为一种代码的组织形式。面向过程主要关注于解决问题需要一步一步实现的过程,而面向对象主要关注于把解决的不同问题分给不同的对象管理,面向对象不关注于解决问题的步骤,只关注于解决问题的对象。

面向过程和面向对象写函数的不同

  1. 面向对象是相对于面向过程发展来的;
  2. 面向过程是在代码量比较少、功能简单的场景中适用;面向过程一般适用于项目规则较大,代码量较多;
  3. 面向过程所有的事情都是需要我们自己来完成的,亲历亲为;面向对象是完成一件事情,只需要找到某个对象,让它帮我们完成即可;
  4. 面向过程中我们处于执行者的角色;面向对象中我们处于一个调度者的角色;
  5. 面向过程的方式要比面向对象的方式代码执行效率高;但是面向对象对于大型项目来说,有利于多人合作开发、项目代码的维护和更新迭代

以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中的不同方法就可以了。

下面你们有什么高见,可以题出来探讨 ^ ^

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容

  • 博客内容:什么是面向对象为什么要面向对象面向对象编程的特性和原则理解对象属性创建对象继承 什么是面向对象 面向对象...
    _Dot912阅读 1,391评论 3 12
  • 本章内容 理解对象属性 理解并创建对象 理解继承 面向对象语言有一个标志,那就是它们都有类的概念,而通过类可以创建...
    闷油瓶小张阅读 834评论 0 1
  • 昨天因为考试成绩,我和孩子都很不开心,我告诉自己必须每天交作业,所以匆忙中发泄了一下,交了“作业”;今...
    饴逸阅读 172评论 0 6
  • 2017年9月21日星期四 今天要好好感赏儿子。中午儿子放学回来,面带笑容,看得出很高兴,我开心地拥抱儿子,问有什...
    若兰幽香阅读 230评论 2 2
  • 摘抄: 活在信仰里将有助于减轻我们的压力,帮助我们更好地享受人生。坚定信念与信心是拥有信仰的前提,我相信这可以并且...
    芹菜晴空阅读 282评论 0 1