From 19ab6cdea069971ea5736993a15857d693446471 Mon Sep 17 00:00:00 2001 From: qianguyihao Date: Wed, 7 Mar 2018 12:17:11 +0800 Subject: [PATCH] =?UTF-8?q?add=20file=EF=BC=9A=E9=9D=A2=E5=90=91=E5=AF=B9?= =?UTF-8?q?=E8=B1=A1=EF=BC=9A=E7=B1=BB=E7=9A=84=E7=BB=A7=E6=89=BF=E6=96=B9?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 03-JavaScript/{10-原型链.md => 11-原型链.md} | 0 06-前端基础/05-原型链总结.md | 4 +- .../06-面向对象:类的定义和继承的几种方式.md | 246 ++++++++++++++++++ 3 files changed, 248 insertions(+), 2 deletions(-) rename 03-JavaScript/{10-原型链.md => 11-原型链.md} (100%) create mode 100644 06-前端基础/06-面向对象:类的定义和继承的几种方式.md diff --git a/03-JavaScript/10-原型链.md b/03-JavaScript/11-原型链.md similarity index 100% rename from 03-JavaScript/10-原型链.md rename to 03-JavaScript/11-原型链.md diff --git a/06-前端基础/05-原型链总结.md b/06-前端基础/05-原型链总结.md index cadeb6b..fc93f3b 100644 --- a/06-前端基础/05-原型链总结.md +++ b/06-前端基础/05-原型链总结.md @@ -134,7 +134,7 @@ Object是原型链的顶端。 20180306_2209.png -`instanceof`的**作用**:用于判断**引用类型**属于哪个**构造函数**。 +`instanceof`的**作用**:用于判断**实例**属于哪个**构造函数**。 `instanceof`的**原理**:判断实例对象的`__proto__`属性,和构造函数的`prototype`属性,是否为同一个引用(是否指向同一个地址)。 @@ -153,7 +153,7 @@ Object是原型链的顶端。 ### 分析一个问题 -**问题:**已知A继承了B,B继承了C。怎么判断 a 是A直接生成的实例,还是B直接生成的实例呢?还是C直接生成的实例呢? +**问题:**已知A继承了B,B继承了C。怎么判断 a 是由A**直接生成**的实例,还是B直接生成的实例呢?还是C直接生成的实例呢? 分析:这就要用到原型的`constructor`属性了。 diff --git a/06-前端基础/06-面向对象:类的定义和继承的几种方式.md b/06-前端基础/06-面向对象:类的定义和继承的几种方式.md new file mode 100644 index 0000000..b8fdb7e --- /dev/null +++ b/06-前端基础/06-面向对象:类的定义和继承的几种方式.md @@ -0,0 +1,246 @@ + + + +## 前言 + + + +类与实例: + +- 类的声明 + +- 生成实例 + +类与继承: + +- 如何实现继承:继承的本质就是原型链 + +- 继承的几种方式 + + + + +## 类的定义、实例化 + +### 类的定义/类的声明 + +**方式一**:用构造函数模拟类(传统写法) + +```javascript + function Animal1() { + this.name = 'smyhvae'; //通过this,表明这是一个构造函数 + } +``` + + +**方式二**:用 class 声明(ES6的写法) + + +```javascript + class Animal2 { + constructor() { //可以在构造函数里写属性 + this.name = name; + } + } +``` + +控制台的效果: + +20180307_0957.png + +### 实例化 + +类的实例化很简单,直接 new 出来即可。 + +```javascript + console.log(new Animal1(),new Animal2()); //实例化。如果括号里没有参数,则括号可以省略 +``` + +20180307_1000.png + +## 继承的几种方式 + +继承的本质就是原型链。 + +**继承的方式有几种?每种形式的优缺点是?**这些问题必问的。其实就是考察你对原型链的掌握程度。 + + +### 方式一:借助构造函数 + + +```javascript + function Parent1() { + this.name = 'parent1 的属性'; + } + + function Child1() { + Parent1.call(this); //【重要】此处用 call 或 apply 都行:改变 this 的指向 + this.type = 'child1 的属性'; + } + + console.log(new Child1); +``` + + +【重要】上方代码中,最重要的那行代码:在子类的构造函数里写了`Parent1.call(this);`,意思是:**让Parent的构造函数在child的构造函数中执行**。发生的变化是:**改变this的指向**,parent的实例 --> 改为指向child的实例。导致 parent的实例的属性挂在到了child的实例上,这就实现了继承。 + +打印结果: + +20180307_1015.png + +上方结果表明:child先有了 parent 实例的属性(继承得以实现),再有了child 实例的属性。 + +**分析**: + +这种方式,虽然改变了 this 的指向,但是,**Child1 无法继承 Parent1 的原型**。也就是说,如果我给 Parent1 的原型增加一个方法: + +```javascript + Parent1.prototype.say = function () { + }; +``` + +上面这个方法是无法被 Child1 继承的。如下: + +20180307_1030.png + + +### 方法二;通过原型链实现继承 + +```javascript + /* + 通过原型链实现继承 + */ + function Parent() { + this.name = 'Parent 的属性'; + } + + function Child() { + this.type = 'Child 的属性'; + } + + Child.prototype = new Parent(); //【重要】 + + console.log(new Child()); +``` + +打印结果: + +20180307_1109.png + + +【重要】上方代码中,最重要的那行:每个函数都有`prototype`属性,于是,构造函数也有这个属性,这个属性是一个对象。现在,**我们把`Parent`的实例赋值给了`Child`的`prototye`**,从而实现**继承**。此时,`Child`构造函数、`Parent`的实例、`Child`的实例构成一个三角关系。于是: + +- `new Child.__proto__ === new Parent()`的结果为true + + +**分析:** + +这种继承方式,**Child 可以继承 Parent 的原型**,但有个缺点: + +缺点是:**如果修改 child1实例的name属性,Child实例中的name属性也会跟着改变。 + +如下: + +20180307_1123.png + +上面的代码中, child1修改了arr属性,却发现,child2的arr属性也跟着改变了。这显然不太好,在业务中,两个子模块应该隔离才对。如果改了一个对象,另一个对象却发生了改变,就不太好。 + +造成这种缺点的原因是:child1和child2共用原型。即:`chi1d1.__proto__ === child2__proto__`是严格相同。而 arr方法是在 Parent 的实例上(即 Child实例的原型)的。 + + + +## 方式三;组合的方式:构造函数 + 原型链 + +就是把上面的两种方式组合起来: + + +```javascript + /* + 组合方式实现继承:构造函数、原型链 + */ + function Parent3() { + this.name = 'Parent 的属性'; + this.arr = [1, 2, 3]; + } + + function Child3() { + Parent3.call(this); //【重要1】执行 parent方法 + this.type = 'Child 的属性'; + } + Child3.prototype = new Parent3(); //【重要2】第二次执行parent方法 + + var child = new Child3(); +``` + + +这种方式,能解决之前两种方式的问题:既可以继承父类原型的内容,也不会造成原型里属性的修改。 + +这种方式的缺点是:让父亲Parent的构造方法执行了两次。 + + + + + + + + + + + +```javascript + +``` + + + + + + + + +```javascript + +``` + + + + + + + +ES6中的继承方式,一带而过即可,重点是要掌握ES5中的继承。 + + + + + +```javascript + +``` + + + + + + + + + + + + +```javascript + +``` + + + + + + + + + +```javascript + +```