zoukankan      html  css  js  c++  java
  • js中实现继承的几种方式

      首先我们了解,js中的继承是主要是由原型链实现的。那么什么是原型链呢?

      由于每个实例中都有一个指向原型对象的指针,如果一个对象的原型对象,是另一个构造函数的实例,这个对象的原型对象就会指向另一个对象的原型对象,如此循环,就行成了原型链。

      在了解原型链之后,我们还需要了解属性搜索机制,所谓的属性搜索机制,就是当我们访问对象上的一个属性时,我们如何找到这个属性值。首先,我们现在当前实例中查找该属性,如果找到了,返回该值,否则,通过__proto__找到原型对象,在原型对象中进行搜索,如果找到,返回该值,否则,继续向上进行搜索,直到找到该属性,或者在原型链中没有找到,返回undefined。

      根据《javascript高级程序设计》中,可以有六种继承方式,下面我们一一来介绍:

      1. 原型链

     1     // 父亲类
     2     function Parent() {
     3         this.value = 'value';
     4     }
     5     Parent.prototype.sayHi = function() {
     6             console.log('Hi');
     7         }
     8         // 儿子类
     9     function Child() {
    10 
    11     }
    12     // 改变儿子的prototype属性为父亲的实例
    13     Child.prototype = new Parent();
    14 
    15     var child = new Child();
    16     // 首先现在child实例上进行查找,未找到,
    17     // 然后找到原型对象(Parent类的一个实例),在进行查找,未找到,
    18     // 在根据__proto__进行找到原型,发现sayHi方法。
    19 
    20     // 实现了Child继承
    21     child.sayHi();

    但是这种继承方式存在一个问题,那就是引用类型属性共享。

     1 // 父亲类
     2     function Parent() {
     3         this.color = ['pink', 'red'];
     4     }
     5 
     6     // 儿子类
     7     function Child() {
     8 
     9     }
    10     Child.prototype = new Parent();
    11 
    12     var child1 = new Child();
    13     var child2 = new Child();
    14     // 先输出child1和child2种color的值
    15     console.log(child1.color); // ["pink", "red"]
    16     console.log(child2.color); // ["pink", "red"]
    17 
    18     // 在child1的color数组添加white
    19     child1.color.push('white');
    20     console.log(child1.color); // ["pink", "red", "white"]
    21     // child1上的改动,child2也会受到影响
    22     console.log(child2.color); // ["pink", "red", "white"]

      它存在第二个问题,就是无法向父类种传参。

      2. 借用构造函数

      在这里,我们借用call函数可以改变函数作用域的特性,在子类中调用父类构造函数,复制父类的属性。此时没调用一次子类,复制一次。此时,每个实例都有自己的属性,不共享。同时我们可以通过call函数给父类传递参数。

       2.1 解决引用类型共享问题

     1     // 父亲类
     2     function Parent(name) {
     3         this.name = name;
     4         this.color = ['pink', 'red'];
     5     }
     6 
     7     // 儿子类
     8     function Child() {
     9         Parent.call(this);
    10 
    11         // 定义自己的属性
    12         this.value = 'test';
    13     }
    14 
    15 
    16     var child1 = new Child();
    17     var child2 = new Child();
    18 
    19     // 先输出child1和child2种color的值
    20     console.log(child1.color); // ["pink", "red"]
    21     console.log(child2.color); // ["pink", "red"]
    22 
    23     // 在child1的color数组添加white
    24     child1.color.push('white');
    25     console.log(child1.color); // ["pink", "red", "white"]
    26     // child1上的改动,child2并没有受到影响
    27     console.log(child2.color); // ["pink", "red"]

      2.2 解决传参数问题

     1     // 父亲类
     2     function Parent(name) {
     3         this.name = name;
     4         this.color = ['pink', 'red'];
     5     }
     6 
     7     // 儿子类
     8     function Child(name) {
     9         Parent.call(this, name);
    10 
    11         // 定义自己的属性
    12         this.value = 'test';
    13     }
    14 
    15     var child = new Child('qq');
    16     // 将qq传递给Parent
    17     console.log(child.name); // qq

      当时,上述方法也存在一个问题,共享的方法都在构造函数中定义,无法达到函数复用的效果。

      3. 组合继承

      根据上述两种方式,我们可以扬长避短,将需要共享的属性使用原型链继承的方法继承,将实例特有的属性,用借用构造函数的方式继承。

     1     // 父亲类
     2     function Parent() {
     3         this.color = ['pink', 'red'];
     4     }
     5     Parent.prototype.sayHi = function() {
     6         console.log('Hi');
     7     }
     8 
     9     // 儿子类
    10     function Child() {
    11         // 借用构造函数继承
    12         Parent.call(this);
    13 
    14         // 下面可以自己定义需要的属性
    15     }
    16     // 原型链继承
    17     Child.prototype = new Parent();
    18 
    19     var child1 = new Child();
    20     var child2 = new Child();
    21 
    22     // 每个实例特有的属性
    23     // 先输出child1和child2种color的值
    24     console.log(child1.color); // ["pink", "red"]
    25     console.log(child2.color); // ["pink", "red"]
    26 
    27     // 在child1的color数组添加white
    28     child1.color.push('white');
    29     console.log(child1.color); // ["pink", "red", "white"]
    30     // child1上的改动,child2并没有受到影响
    31     console.log(child2.color); // ["pink", "red"]
    32 
    33     // 每个实例共享的属性
    34     child1.sayHi(); // Hi
    35     child2.sayHi(); // Hi

      上述方法,虽然综合了原型链和借用构造函数的优点,达到了我们想要的结果,但是它存在一个问题。就是创建一次实例时,两次调用了父类构造函数。

     1 // 父亲类
     2     function Parent() {
     3         this.color = ['pink', 'red'];
     4     }
     5     Parent.prototype.sayHi = function() {
     6         console.log('Hi');
     7     }
     8 
     9     // 儿子类
    10     function Child() {
    11         Parent.call(this); // 第二次调用构造函数:在新对象上创建一个color属性
    12     }
    13    
    14     Child.prototype = new Parent(); // 第一次调用构造函数Child.prototype将会得到一个color属性,屏蔽了原型中的color属性。

      因此,出现了寄生组合式继承。在了解之前,我们先了解一下什么是寄生式继承。

      4. 寄生式继承

      同工厂模式类似,将我们需要继承的函数进行封装,然后进行某种增强,在返回对象。

     1     function Parent() {
     2         this.color = ['pink', 'red'];
     3     }
     4 
     5 
     6     function createAnother(o) {
     7         // 获得当前对象的一个克隆
     8         var another = new Object(o);
     9         // 增强对象
    10         o.sayHi = function() {
    11                 console.log('Hi');
    12             }
    13         // 返回对象
    14         return another;
    15     }

      5. 寄生组合式继承

     1     // 创建只继承原型对象的函数
     2     function inheritPrototype(parent, child) {
     3         // 创建一个原型对象副本
     4         var prototype = new Object(parent.prototype);
     5         // 设置constructor属性
     6         prototype.constructor = child;
     7         child.prototype = prototype;
     8     }
     9 
    10     // 父亲类
    11     function Parent() {
    12         this.color = ['pink', 'red'];
    13     }
    14     Parent.prototype.sayHi = function() {
    15         console.log('Hi');
    16     }
    17 
    18     // 儿子类
    19     function Child() {
    20         Parent.call(this);
    21     }
    22 
    23     inheritPrototype(Parent, Child);

      6. 原型式继承  

      思想:基于已有的对象创建对象。

     1     function createAnother(o) {
     2         // 创建一个临时构造函数
     3         function F() {
     4 
     5         }
     6         // 将传入的对象作为它的原型
     7         F.prototype = o;
     8         // 返回一个实例
     9         return new F();
    10     }
  • 相关阅读:
    Flask-SQLAlchemy
    with 与 上下文管理器
    使用@property
    C++:如何把一个int转成4个字节?
    尝试理解Flask源码 之 搞懂WSGI协议
    qt setData()和data()
    我使用过的Linux命令之sftp
    linux下如何使用sftp命令
    Linux环境下安装JDK
    CentOS 6.5 配置IP地址的三种方法
  • 原文地址:https://www.cnblogs.com/diligentYe/p/6413450.html
Copyright © 2011-2022 走看看