面向对象是js最难弄懂的一部分,我在看了《Javascript高级程序设计》之后还看了李炎恢老师关于面向对象与原型的讲解,算是理解了,在此做下笔记,仅为以后的个人学习作参考。先从创建对象逐步剖析
这种Object构造函数创建多个对象,容易造成代码冗余,所以有工厂模式的出现
但是工厂模式没有解决多个对象识别的问题,即怎样知道一个对象的类型,怎样体现呢
很明显,box2是createObject的对象,但三个提示框的结果都是true,只知道它们都是object,但不知道是谁的对象,所以就有了构造函数创建对象模式的出现
注意几点: 构造函数没有显式地创建对象;直接将属性和方法赋给了this对象;要创建实例,必须使用new 关键字;将构造函数的作用域直接付给这个对象(this也就指向了这个新对象);构造函数的首字母必须大写,比如例子中的Box,Box1;
构造函数的调用
构造函数的问题(不同实例的同名函数不相等)
创建两个完成同样任务的函数没有必要,但我们可以这样来解决
我们把sayName()函数的定义转移到构造函数外部,在构造函数内容,我们把sayName属性设置成等于全局的sayName函数,box1、box2对象就共享了在全局作用中定义的同一个sayName函数,这着实解决了两个函数做同一个事情。但是新的问题又来了,sayName是全局函数,如果对象需要用到多个函数,那么就要定义多个全局函数,这样这个自定义的引用类型没有封装性可言。我们可以通过原型模式来解决
使用原型的好处:让所有对象实例共享它的属性和方法
理解原型对象:只要创建了一个新的函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向原型对象。在所有默认的请款下,所有的原型对象都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针。就拿上面的例子来说,Box.prototype.constructor指向Box
用原型对象isPrototype()方法测试了Box,因为它们的内容都有一个Box.prototype的指针,因此都返回了ture
每当代码读取某个对象的某个属性时,都会从实例开始,如果找到了话就返回,没有找到则继续搜索指针指向的原型对象,在原型对象中查找具有给定名字的属性。
可以通过对象实例访问原型的值,但是不能通过对象实例重写原型中的值,但是可以通过delete完全删除实例属性的值,从而使对象实例访问原型的值
hasOwnProperty()可以检测一个属性是存在实例中还是存在原型中,存在实例中返回true
原型与in操作符(两种使用情况:单独使用和在for-in循环当中)
还有个值得注意的是hasprototypeProoerty()是与hasOwnprotery刚好相反
使用字面量来创建原型
原型的动态性
原型对象的问题:也是原型对象的优点,即最大的问题是由共享属性所导致的。然而,如果原型当中包含引用类型的属性来说,问题就比较突出了
这里还是有个问题
两张图的区别是一个通过box1.family.push(),另一张是通过box1.family = [],结果不一样,引用类型.属性.方法可以重写原型当中的值,但是引用类型.属性只能操作但是不能修改原型中的值,针对这些问题,所以有组合使用构造函数和原型模式的出现
组合使用构造函数和原型模式
动态原型模式
注意:使用动态原型模式,不能使用对象字面量重写原型;如果在创建实例的情况下,那么就会切断现有实例与原型之间的关系
寄生构造函数模式
改函数的作用是仅仅是封装创建对象的代码,然后在返回新创建的对象。关于寄生构造函数模式:返回的对象与构造函数或者构造函数的原型属性之间没有关系;也就是说,构造函数返回的对象与在构造函数外部创建的蚃没有什么不同。为此,不能依赖instanceof操作符来确定对象类型。不建议使用这种模式
稳妥构造函数模式
咋一看,好像跟寄生构造函数没有什么不同。但还是有两点不同的,一是新建对象的实例方法不引用this;二是不使用new操作符调用构造函数,Box里面的属性是似有,除了run方法之外,没有其他的办法访问name和age属性,因此稳妥构造函数是比较安全的
继承
继承是OO(面向对象)语言一个重要的概念。许多OO语言都支持两种继承方式:接口继承和实现继承。接口继承则继承方法签名,函数没有签名,因为ECMAScript只支持实现继承,而且实现继承主要是依靠原型链来实现的
原型链:利用一个引用类型继承另一个引用类型的属性和方法。构造函数、原型和实例的关系:每个够走啊函数都有一个原型独享,原型对象都包含一个指向构造函数的指针,而实例都柏寒一个指向原型对象的内部指针。
注意:有时我们需要在子类型中重写超类型的某个方法,或者需要添加超类型中不存在的某个方法,但不管怎样,给原型添加方法的代码一定要在替换原型之后
还有一点,通过原型实现继承时,不能使用对象字面量创建原型方法,因为这样会重写原型链
原型链的问题:1、包含引用类型值得原型属性会被实例共享(之前的解决办法是使用构造函数+原型,即引用类型值放在构造函数当中而不是原型当中),通过原型链继承,被继承的原型会成为继承的原型的一个实例,进而原先的实例属性会成为另外一个原型的实例属性了)
2、在创建子类型的实例时,不能像超类型的构造函数中传递参数
借用构造函数实现继承
借用构造函数的问题:方法都在构造函数当中定义,函数复用就无从谈起了。而且,在超类型的原型中定义的方法,对子类型而言是不可见的
推荐:组合继承(原型链+构造函数)
原型式继承,有点难以理解,不想作过多的解释
寄生式继承(构造函数+工厂模式,下面写错了)
使用集成式继承为对象添加函数,会由于不能做到函数复用而降低效率;这一点与构造函数模式类似
(常用)寄生组合式继承
所谓寄生式继承,通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。其基本的思路是:不必为了制定子类型的原型而调用超类型的构造函数,我们所需要的无非就是hi超类型原型的一个副本而已。本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果制定给子类型的原型。
结语,在面向对象与原型通过结合书本和视频的方式去学习,本身原型和原型链是js的重点难点。希望在接下来的组件化开发当中能够有效地运用起来,不然一切都是纸上谈兵