面向对象

面向对象不是一门新技术,而是一门解决问题的新思路。

面向对象是相对于面向过程的一种解决问题新思路,在知道面向对象之前亲我们需要先明白什么是面向过程。

面向过程是解决某一件事情时,按照完成某件事所需要的步骤来一步一步解决问题过程,当每一步都完成时,整件事就已经完成了。

面向对象则是站在对象的角度思考问题,我有哪些特征和行为。也可以说是让对象以我们人类认知世界和思考的方式来构造对象。例如:我们刚出生时,在认识一个新事物时,我们将以自身的五官和感知,将这个事物抽象成一个概念型模型并定义一个概念名称。当某些事物符合这个模型的固有特征时,我们就会将它称为这个概念名称。

而这个概念名称我们称为类,而对象是类的实例化,类是对象的抽象化。万物皆对象,我们却将具有相同特征的对象称为一个类。

面向对象的语言:1.封装 2.继承 3.多态 (4.抽象 但是并没有得到大众的认可);面向对象的语言也是一个类。只要一门语言满足这三个特征,这门语言就是面向对象的语言这一个类的实例化对象。

j s得到对象的方法有三种:1. new Object; 2. var 变量 ={} 3. 通过构造函数得到。

通过第一和第二种创建的对象,一旦对象较多,会造成代码的冗余。

通过工厂模式得到的创建对象,虽然解决了代码冗余的问题。但是出现一个新的问题。我们的对象应该是有类的,而工厂模式得到的对象只是对象这一类。

当工厂模式无法满足我们的需求时,j s给出了一个新的解决问题的方法就是构造函数。

p s:判断类别的方法: instanceof 和typeof来判断。

构造的函数首字母应该大写,这是j s 中的潜规则。

构造函数的方法来创建对象:

js在ES6之前是是没有办法来定义一个类的。所以我们使用构造函数来模拟类的概念。

在说构造函数时,我们需要来知道我们在操作这些代码时计算机内存中发生了什么。

我们在定义一个变量时,栈内存将我们定义的变量以二进制的方式存储了起来,而我们变量的内容将会放进堆内存中或数据区内存中。在变量内放着的只是指向我们定义内容的一个以16进制存储的地址。

基于构造函数的创建对象的方式和基于工厂的方式类似

最大的区别就是函数的名称就是类的名称,按照面向对象语句的

function Person(name,age) {

this.name = name;

this.age = age;

this.say = function() {

alert("我的名字是:"+this.name+",我今年"+this.age+"岁了");

}

}

var p1 = new Person("马天鹏",15);

p1.say();

使用构造函数的好处就是可以使用instanceof来判断这个对象的类型了

alert(p1 instanceof Person)

基于构造函数的定义的方式最大的好处除了对象重复使用外,就是让我们还可以判断它

的类型。

此时我们发现基于构造函数的定义对象的方式看似已经很完美了,我们需要的问题它都

可以解决了,但是如果我们仔细的分析这段代码的话,就会发现这样的代码是存在问题的,

为什么呢?

我们通过代码分析得知:say 方法在每个对象创建后都存在了一个方法拷贝(但是我们

发现代码在只有调用时,say 方法才会在堆中创建,基于闭包的原理),这样就增加了内存的

消耗了,如果在对象中有大量的方法时,内存的消耗就会高,这样不行了。

解决这个问题的就是我们可以把这个方法放到全局函数,这样就所有的对象指向了一个

方法。

解决方案就是将方法全部放在外面,成为全局函数

function Person(name,age) {

this.name = name;

this.age = age;

可以将方法成为全局函数。

this.say = say;

}

function say() {

alert("我的名字是:"+this.name+",我今年"+this.age+"岁了");

}

var p1 = new Person("马天鹏",15);

p1.say();

var p2 = new Person("老万",30);

p2.say();

但是这样写的话,会带来另一个问题,就是方法一点定义为全局函数,那么 window 对

象就可以调用,这样就破坏了对象的封装性。而且如果有大量的方法,这样写导致整体代码

充斥着大量的全局函数,这样将不利于开发。所以我们急需一种可以完美的解决上述问题的

方案,javascript 给我们提供了一种解决这些问题的方案,就是基于原型的对象创建方案。

封装--Javascript 的原型(prototype)

Prototype,原型的初览

以上方式在创建对象都不太理想,所以我们可以使用 prototype 的方式来完成对象的创

建。

如何使用原型创建对象呢?首先写段代码让大家看看:

//定义了一个对象

function Person() {

}

//使用原型来给对象赋值

//这样就讲一个对象的属性和方法放在了该对象的原型中

//外界是无法访问到这样数据的

Person.prototype.name = "小妖的八戒";

Person.prototype.age = 18;

Person.prototype.say = function() {

alert("我的名字是:"+this.name+",我今年"+this.age+"岁了");

}

var p1 = new Person();

p1.say();//正常访问了

say();//报错了

这样我们发现 window 就无法访问到 say 方法了,此时 say 方法只属于 Person 对象独有

的方法。很好的解决了封装破坏的情况。

什么是原型

上面我们看了基于 prototype 创建对象的方式很好的解决了我们前面遇到的一系列问题,

那么到底什么是原型,原型又是如何解决如上的问题的呢?我们下面来研究研究。

原型是 js 中非常特殊一个对象,当一个函数创建之后,会随之就产生一个原型对象,当

通过这个函数的构造函数创建了一个具体的对象之后,在这个具体的对象中就会有一个属性

指向原型。这就是原型的概念。

鉴于原型的概念比较难以理解,我们就以上面的代码为例,画图为大家讲解:

//第一种状态

//定义了一个对象

function Person() {

}

//第二种状态,这样赋值就会赋在原型对象中

//使用原型来给对象赋值

//这样就讲一个对象的属性和方法放在了该对象的原型中

//外界是无法访问到这样数据的

Person.prototype.name = "小妖的八戒";

Person.prototype.age = 18;

Person.prototype.say = function() {

alert("我的名字是:"+this.name+",我今年"+this.age+"岁了");

}

//第三种状态

var p1 = new Person();

//此时调用的是原型中的,因为自己中没有这些属性和方法

p1.say();//正常访问了

// say();//报错了

//可以通过如下的方式检测p1是不是指向Person的原型对象

// alert(Person.prototype.isPrototypeOf(p1))

var p2 = new Person();

p2.name = "张三";

p2.age = 20;

p2.say();

常见的原型检测方式

可以通过如下的方式检测p1是不是指向Person的原型对象

alert(Person.prototype.isPrototypeOf(p1))

//检测p1的构造器是否指向Person对象

alert(p1.constructor == Person)

//检测某个属性是不是自己内存中的

alert(p1.hasOwnProperty("name"));

alert(p2.hasOwnProperty("name"))

同样我们可以使用 delete 语句来删除我们赋予对象的自己属性(注意:原型中的是无法

删除的),如

//可以使用delete语句删除对象中自己的属性,那么就会找到原型中的值

delete p2.name;

p2.say();

alert(p2.hasOwnProperty("name"));

检测在某个对象自己或者对应的原型中是否存在某个属性。

alert("name" in p1);//true

delete p2.name;//虽然删除了自己的name属性,但是原型中有

alert("name" in p2);//true

//原型和自己中都没有sex属性

alert("sex" in p1);//false

那么问题来了?如果检测只在原型中,不在自己中的属性呢?(提问)

//我们可以自己写代码来测试属性不在自己,在原型中

function hasPrototypeProperty(obj,prop) {

if (!obj.hasOwnProperty(prop)) {

if (prop in obj) {

return true;

}

}

return false;

}

alert(hasPrototypeProperty(p1,"name"));

原型重写

在上面的写法中,我们已经解决了大量的问题,使用原型。但是如果我们的对象中存在

大量的属性或者方法的时候,使用上面的方式,感觉要写大量的【对象.prototype.属性名 】

这样的代码,感觉不是很好,那么我们可以使用 json 的方式来写:

function Person() {

}

Person.prototype = {

name : "马帅哥",

age : 18,

say : function() {

alert("我的名字是:"+this.name+",我今年"+this.age+"岁了");

}

}

var p1 = new Person();

p1.say()

var p2 = new Person();

p2.name = "张三";

p2.age = 20;

p2.say();

但是这种写法,我们是将该对象的原型覆盖(注意:这两种写法不一样的,第一种是扩

充,第二种是覆盖),就会出现如下的问题:

function Person() {

}

Person.prototype = {

constructor:Person,//手动指向Person

name : "马帅哥",

age : 18,

say : function() {

alert("我的名字是:"+this.name+",我今年"+this.age+"岁了");

}

}

var p1 = new Person();

p1.say()

var p2 = new Person();

p2.name = "张三";

p2.age = 20;

p2.say();

//此时p1的构造器不在指向Person,而是指向了Object

//因为我们覆盖了Person的原型,所以如果constructor比较重要的话,

//我们可以手动指向

alert(p1.constructor == Person)

此时就没有问题了。但是原型重写会给我们带来一些非常有趣的现象。下面我们来研究

研究。

function Person() {

}

var p1 = new Person();

Person.prototype.sayHello = function() {

alert("我的名字是:"+this.name+",我今年"+this.age+"岁了");

}

// p1.sayHello();

Person.prototype = {

constructor:Person,//手动指向Person

name : "马帅哥",

age : 18,

say : function() {

alert("我的名字是:"+this.name+",我今年"+this.age+"岁了");

}

}

var p2 = new Person();

p2.name = "张三";

p2.age = 20;

p1.sayHello();//此时找不到name和age,但是代码正确

p2.say();//正确

p1.say();//错误,因为原型重写了

为了解决原型所带来的问题,需要通过组合构造函数和原型来实现对象的创建将:属性

在构造函数中定义,将方法在原型中定义。这种有效集合了两者的优点,是目前最为常用的

一种方式。

//属性在构造方法定义

function Person(name,age,friends) {

this.name = name;

this.age = age;

this.friends = friends;

}

/**

* 此时所有的属性都是保存在自己的内存中

* 方法都是定义在prototype(原型)中

*/

//方法在原型中定义

Person.prototype = {

constructor:Person,

say : function() {

alert("我的名字是:"+this.name+",我今年"+this.age+"岁了");

}

};

var p1 = new Person("马帅哥",18,["老宗","老万"]);

p1.friends.push("老马");

alert(p1.friends);

var p2 = new Person("曾小贤",20,["小丽","小美"]);

alert(p2.friends);

写了那么多的代码,目的就是为了这种定义 javascript 对象的方式,所以我们最终的定

义 javascript 对象的方案就是基于组合的方式定义,将属性的定义放在构造函数中,将方法

的定义放在原型中。

终极方案—基于动态原型的对象定义(选学)

上面的方案在我们看来已经相当的完美了,但是因为一些面向对象的程序员(如:java、

c#)等开发人员他们认为将方法放在外面不像面向对象的写法,所以提供了另一种写法,在

这里说说,经供参考:

//属性在构造方法定义

function Person(name,age,friends) {

this.name = name;

this.age = age;

this.friends = friends;

//判断不存在的时候,如果存在就不在写,内存减少消耗

if (!Person.prototype.say) {

Person.prototype.say = function() {

alert("我的名字是:"+this.name+",我今年"+this.age+"岁了");}}}

var p1 = new Person("马帅哥",18,["老宗","老万"]);

p1.friends.push("老马");

alert(p1.friends);

var p2 = new Person("曾小贤",20,["小丽","小美"]);

alert(p2.friends);

构造出的函数都有一个一个属性:Prototype:原型 对象.prototype具有constructor属性

通过json覆盖创建的对象没有constructor属性。

 

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

推荐阅读更多精彩内容