JavaScript Web前端之路JavaScript

深入了解javascript(三)——面向对象与原型

2017-05-28  本文已影响77人  南蓝NL

面向对象是js最难弄懂的一部分,我在看了《Javascript高级程序设计》之后还看了李炎恢老师关于面向对象与原型的讲解,算是理解了,在此做下笔记,仅为以后的个人学习作参考。先从创建对象逐步剖析

Object构造函数创建多个实例

这种Object构造函数创建多个对象,容易造成代码冗余,所以有工厂模式的出现

工厂模式创建对象,就是普通函数的创建

但是工厂模式没有解决多个对象识别的问题,即怎样知道一个对象的类型,怎样体现呢

工厂模式无法解决对象识别的问题

很明显,box2是createObject的对象,但三个提示框的结果都是true,只知道它们都是object,但不知道是谁的对象,所以就有了构造函数创建对象模式的出现

构造函数很好地接解决了工厂模式无法识别对象的类型的问题

注意几点: 构造函数没有显式地创建对象;直接将属性和方法赋给了this对象;要创建实例,必须使用new 关键字;将构造函数的作用域直接付给这个对象(this也就指向了这个新对象);构造函数的首字母必须大写,比如例子中的Box,Box1;

构造函数的调用

构造函数的问题(不同实例的同名函数不相等)

创建两个完成同样任务的函数没有必要,但我们可以这样来解决

我们把sayName()函数的定义转移到构造函数外部,在构造函数内容,我们把sayName属性设置成等于全局的sayName函数,box1、box2对象就共享了在全局作用中定义的同一个sayName函数,这着实解决了两个函数做同一个事情。但是新的问题又来了,sayName是全局函数,如果对象需要用到多个函数,那么就要定义多个全局函数,这样这个自定义的引用类型没有封装性可言。我们可以通过原型模式来解决

使用原型的好处:让所有对象实例共享它的属性和方法

理解原型对象:只要创建了一个新的函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向原型对象。在所有默认的请款下,所有的原型对象都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针。就拿上面的例子来说,Box.prototype.constructor指向Box

这张图是《Javascript高级程序设计》上面,实例对象和属性方法的名字不一样而已,、 红线标注的返回true

用原型对象isPrototype()方法测试了Box,因为它们的内容都有一个Box.prototype的指针,因此都返回了ture

每当代码读取某个对象的某个属性时,都会从实例开始,如果找到了话就返回,没有找到则继续搜索指针指向的原型对象,在原型对象中查找具有给定名字的属性。

可以通过对象实例访问原型的值,但是不能通过对象实例重写原型中的值,但是可以通过delete完全删除实例属性的值,从而使对象实例访问原型的值

通过delete删除实例属性

hasOwnProperty()可以检测一个属性是存在实例中还是存在原型中,存在实例中返回true

原型与in操作符(两种使用情况:单独使用和在for-in循环当中)

还有个值得注意的是hasprototypeProoerty()是与hasOwnprotery刚好相反

使用字面量来创建原型

原型的动态性

关于这里我是有个问题的,难道说原型要写在创建实例对象之前,才不会说是切断实例与原型的关系

原型对象的问题:也是原型对象的优点,即最大的问题是由共享属性所导致的。然而,如果原型当中包含引用类型的属性来说,问题就比较突出了

这里还是有个问题

两张图的区别是一个通过box1.family.push(),另一张是通过box1.family = [],结果不一样,引用类型.属性.方法可以重写原型当中的值,但是引用类型.属性只能操作但是不能修改原型中的值,针对这些问题,所以有组合使用构造函数和原型模式的出现

组合使用构造函数和原型模式

构造函数+原型实在ECMAScript当中最广泛、认同度最高的一种创建定义类型的方法

动态原型模式

红线标注的部分说明在run()方法不存在的情况下执行

注意:使用动态原型模式,不能使用对象字面量重写原型;如果在创建实例的情况下,那么就会切断现有实例与原型之间的关系

寄生构造函数模式

 

改函数的作用是仅仅是封装创建对象的代码,然后在返回新创建的对象。关于寄生构造函数模式:返回的对象与构造函数或者构造函数的原型属性之间没有关系;也就是说,构造函数返回的对象与在构造函数外部创建的蚃没有什么不同。为此,不能依赖instanceof操作符来确定对象类型。不建议使用这种模式

稳妥构造函数模式

咋一看,好像跟寄生构造函数没有什么不同。但还是有两点不同的,一是新建对象的实例方法不引用this;二是不使用new操作符调用构造函数,Box里面的属性是似有,除了run方法之外,没有其他的办法访问name和age属性,因此稳妥构造函数是比较安全的


继承

继承是OO(面向对象)语言一个重要的概念。许多OO语言都支持两种继承方式:接口继承和实现继承。接口继承则继承方法签名,函数没有签名,因为ECMAScript只支持实现继承,而且实现继承主要是依靠原型链来实现的

原型链:利用一个引用类型继承另一个引用类型的属性和方法。构造函数、原型和实例的关系:每个够走啊函数都有一个原型独享,原型对象都包含一个指向构造函数的指针,而实例都柏寒一个指向原型对象的内部指针。

原型链继承

注意:有时我们需要在子类型中重写超类型的某个方法,或者需要添加超类型中不存在的某个方法,但不管怎样,给原型添加方法的代码一定要在替换原型之后

还有一点,通过原型实现继承时,不能使用对象字面量创建原型方法,因为这样会重写原型链

原型链的问题:1、包含引用类型值得原型属性会被实例共享(之前的解决办法是使用构造函数+原型,即引用类型值放在构造函数当中而不是原型当中),通过原型链继承,被继承的原型会成为继承的原型的一个实例,进而原先的实例属性会成为另外一个原型的实例属性了)

2、在创建子类型的实例时,不能像超类型的构造函数中传递参数

借用构造函数实现继承

解决了单独使用原型链的两个问题:借用构造函数引起引用类型没有被共享同时还解决了子类型向超类型传参的问题

借用构造函数的问题:方法都在构造函数当中定义,函数复用就无从谈起了。而且,在超类型的原型中定义的方法,对子类型而言是不可见的

推荐:组合继承(原型链+构造函数)

组合继承避免了原型链和构造函数的缺陷,融合了它们的优点

原型式继承,有点难以理解,不想作过多的解释

寄生式继承(构造函数+工厂模式,下面写错了)

使用集成式继承为对象添加函数,会由于不能做到函数复用而降低效率;这一点与构造函数模式类似

(常用)寄生组合式继承

所谓寄生式继承,通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。其基本的思路是:不必为了制定子类型的原型而调用超类型的构造函数,我们所需要的无非就是hi超类型原型的一个副本而已。本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果制定给子类型的原型。

寄生组合式继承的基本模式

结语,在面向对象与原型通过结合书本和视频的方式去学习,本身原型和原型链是js的重点难点。希望在接下来的组件化开发当中能够有效地运用起来,不然一切都是纸上谈兵

上一篇下一篇

猜你喜欢

热点阅读