js--面向对象
面向对象主要分为两大块:类与实例和类与继承
类与实例
类的声明–两种(ES5和ES6)
实例–直接new
console.log(new Animal(), new Animal1());
类与继承 对原型链的掌握
如何实现继承
每种方法都思考: 实现继承的那种方式 1.原理是什么,2. 有什么特点,3. 有没有缺点需要继续改进
第一种:通过构造函数 – call()
实现原理:call可以改变函数运行的上下文this,在这里就修改了父类构造函数this的指向,从而导致了父类执行的时候属性都会挂在Child类实例上。弊端是这种方式不能继承原型链
缺点
只实现了部分继承,如果父类的属性都在构造函数上,没有问题;如果父类的原型对象上还有方法,子类是拿不到这些方法的
第二种:借助原型链实现继承 弥补 构造函数实现继承的不足
实现原理:使子类原型对象指向父类的实例以实现继承,即重写类的原型,弊端是不能直接实现多继承。因为实例引用的是同一个对象
缺点若有多个实例,更改第一个对象上的属性,第二个对象上的属性也跟着一起改变了 对象本身应该是隔离的,否则完全没有必要用面向对象
造成的原因原型链上的原型对象是共用的。实例引用的是同一个对象--即父类的实例对象。而父类的方法在实例对象上且是引用类型
详解
Step 1:
Parent构造函数有很多实例,其中一个实例赋值给了某个子类的 原型
当这个子类的实例向里面添加东西时
Step 2:
但C1这个实例里面是没有这个属性或方法的
所以只能通过原型链向上找,给Child3.prototype里的属性或方法添加
结果就变成了
即c1,c2,c3均间接获得了添加的内容
第三种:组合方式 --结合构造函数和原型链的优点–比较通用
实现继承的原理:
通过Parent.call(this,name); 可以继承父类的基本属性和引用属性 ,并且可以传参数(后面的name就是当子类有形参时传入的参数name);
通过 Child.prototype = new Parent(); 将子类的原型指向父类的实例,这样子类的实例(即new child())就继承了父类的所有属性和方法(包括实例属性/方法和原型属性/方法)。
优点:
1. 修改子类实例的属性时,不会引起父类的属性的变化
2. 可以向父类传参数了
缺点:
- 创建子类的实例的时候,父类构造函数执行了两次
父类的play数组里面的值没有改变
缺点父类的构造函数执行了2次,但没有这个必要
上面代码中,在构造函数继承(即Parent.call(this))的时候已经执行了一次Parent,父类Parent的属性已经在子类Child里运行了(Child已经有了Parent的属性和方法,不包括原型上的属性和方法)。
外面原型链继承的时候(即Child.prototpe= new Parent())就没有必要再执行一次Parent了,只需要把原型上的属性和方法继承了就可以了。.
图解
Child 类里先通过call()获得了parent的属性和方法
再通过child.prototype使child构造函数的原型为parent实例
- 这样就解决了构造函数在继承过程中无法继承父类原型对象里面的属性和方法
因为子类找不到就会通过构造函数向子类的原型找子类的原型是parent的实例,找不到再往上就可以找到parent的原型对象,也就让子类能够获得父类原型对象里面的函数和方法了 - 同时由于父类构造函数里面的属性和方法通过call(this)已经挂在了子类上,所以生成的实例不是引用同一个对象,都是子类自己生成的,互不干扰,因此实例再添加属性的时候就不会引起其他对象的变化了
== 》解决父类构造函数执行2次的缺点
第四种 组合方式优化1
看上图,child.prototype=new parent()的目的原本就是想要拿到父类构造函数的原型对象,即解决第一种call()的缺点。【因为call()已经可以拿到parent内的属性和方法了】
所以直接将父类构造函数的原型赋给子类构造函数的原型即可
这个时候父类构造函数就只执行了一次
parent.prototye是一个简单的引用,因为原型对象是对象,所以肯定是引用类型,不是zhi类型。因此没有执行构造函数,只是引用了
缺点由于一个构造函数的原型等于另一个构造函数的实例,则有点相当于平级了。但不管怎样,由原型链的知识可知,同一条原型链上的构造函数,instanceof都为true
则怎么区分一个对象是直接由子类实例化的,还是由父类实例化的。也就是由谁直接实例化
s1.constructor竟然是Parent
因为 Child.prototype = Parent.prototype;是同一个对象,里面的constructor就是父类的constructor。父类的prototype的constructor自然指的就是自己了
其实第三种方式的constructor也是Parent3
如图:
因为Child3.prototype直接拿的是父类的实例,没有自己的constructor,它的constructor是从父类继承的,所以拿过来的肯定是Parent3的constructor。Parent3.prototype.constructor肯定是Parent3呀
==》优化二 解决无法判断父类的实例(共用了一个原型对象)–用Object.Create()
第五种 组合方式优化2
通过Object.Create()创建一个中间对象【Child.prototype = Parent.prototype;是引用同一个对象】即把两个原型对象区分开了。但这个中间对象还具备一个特性,就是其原型对象是父类的原型对象。所以又相联系起来了
原型-原型链-(相关联的prototype、constructor)-面向对象的几种继承方式
考察的都是原型链的知识,终于算是把这块缕清楚了!✿✿ヽ(°▽°)ノ✿