zoukankan      html  css  js  c++  java
  • 继承小结

    实例和对象的区别

    JS语法没有类这个概念(当然ES6引用了类这个概念)。只能通过构造函数来创建类,new一个类之后就是对象。

    一切都是对象。那么函数也是对象、实例也是对象、对象字面量也是对象,而且JS中所有对象的成员都是公用的。

    即:对象是一个具有多种属性的内容结构!所以:实例都是对象,而对象不全是实例!

    对象和函数

    函数就是对象;对象不一定是函数,而且在JS中,函数还是第一类对象,而且还是也是实例。

    var add = new Function('a','b','return a+b'); 
    //add为一个函数,它也是构造函数Function的一个实例,即对象
    add(1,2) //3

    结论:一切皆为对象,函数为第一类对象。而对象不是实例!

    什么是继承

      继承就是子类化,从一个基础或者超类对象中继承相关的属性

    JS继承的方式

      JS继承主要有两种、apply(call)和 prototype基础,很多继承的设计模式都是按照这两个继承来设计的。

    1.先看apply(call)的一个例子

    function Stream(x, y){
         this.x = x;
         this.y = y;
         //私有变量
         var _x = 9;
         //暴露子类对私有变量的访问
         this.getX = function(){
             //_x:9
             console.log("_x:"+_x);
             return _x;
         }
     }
    var stream = function(){
        var array = Array.prototype.slice.call(arguments);
        //继承Stream中的公有属性。array是对公有属性赋值。
        Stream.apply(this,array);
    }
    var str = new stream(7, 8);
    //x:7
    console.log("x:" +  str.x);
    //str:9
    console.log("str:" + str.getX());

    2.另外一个是原型继承,通过它可以实现类和实例直接的继承关系。

    2.1 实现数据备份

    // 通过原型来来实现数据备份
    function p(x){
        this.x = x;
    }
    p.prototype.backup = function(){   //备份函数、初始化第一个对象时进行备份,还原也只能还原第一个对象的数据
        for(i in this){
            p.prototype[i] = this[i];
        }
    }
    var p1 = new p(1);
    console.log(p1.x);     //1;
    p1.backup();           //p1的原型对象(构造函数、构造类)的原型属性应用了p1对象。即p.prototype.x = p1.x;
    p1.x = 10;
    p1 = p.prototype;      //p1对象赋值给p.prototype,p1.x = p.prototype.x  实现对象的备份还原。
    console.log(p1.x);

    2.2  继承封装

       这也是原型最重要的特征,下面是套路,这是一种原型设计模式,需要牢记!

      extend 方法

    function extend(Child, Parent) {
        var F = function(){};
        F.prototype = Parent.prototype;
        Child.prototype = new F();
        Child.prototype.constructor = Child;
        Child.uber = Parent.prototype;    //意思是为子对象设一个uber属性,这个属性直接指向父对象的prototype属性。(uber是一个德语词,意思是"向上"、"上一层"。)这等于在子对象上打开一条通道,可以直接调用父对象的方法。这一行放在这里,只是为了实现继承的完备性,纯属备用性质。
    }
    extend(Cat,Animal);
    var cat1 = new Cat("大毛","黄色");
    alert(cat1.species); // 动物

    method方法

    这个例子是来源于《JS模式》这本书

    if(!Function.prototype.method){
        Function.prototype.method = function(name,fn){
            //this是调用method的构造函数
            this.prototype[name] = fn;
            return this;
        }
    }
    var Person = function(name){
        this.name = name;  
    }.method("setName",function(name){
        //this是构造函数的实例(new方法)
        this.name = name;
        return this;
    }).method("getName",function(){
        console.log(this.name);
        return this;
    });
    var p = new Person("anthonyliu");
    //anthonyliu
    p.getName();
    p.setName("liuyinlei").getName();
    //liuyinlei

     __proto__和prototype

     对象不一定有prototype属性,例如{};构造函数肯定有prototype属性,而且构造函数定义的实例会有__proto__属性,

     该属性值是构造构造函数的prototype指向的对象,如下:

    function A(x,y){
        this.x = x;
        this.y = y;
    }
    A.prototype.getX = function(){
        return this.x;
    }
    var a = new A(3,4);
    console.log(a.__proto__.constructor);    //A
    //即:a.__proto__.constructor.prototype = A.prototype;A.prototype.construct = a.__proto__.construct = A 

     还有更诡异的事情:一个函数的静态成员对象的__proto__赋值一个对象b,那个可以直接通过静态成员访问b对象中的成员:例如:

    var request = {
        x:"100"
    };
    function  A() {
        // body...
    }
    A.request = {
        __proto__:request
    };
    console.log(A.request.x);           //100 

    express源码中也能看到其中的身影:

    function createApplication() {
      var app = function(req, res, next) {
        app.handle(req, res, next);
      };
      //继承事件的方法和proto的方法。
      mixin(app, EventEmitter.prototype, false);
      mixin(app, proto, false);
      //app.request为req(node的http.IncomingMessage 类的一个实例)
      app.request = { __proto__: req, app: app };
      app.response = { __proto__: res, app: app };
      app.init();
      return app;
    }

    其实__proto__属性可以看做是一个mixin方法:

    var proto ={};
    var mixin = {"x":"y"};
    proto.__proto__ = mixin;
    console.log(proto.x);    //y

    console打印原型

    var A = function (argument) {
        // body...
    };
    A.prototype.pipe = function(){
    
    };
    //A { pipe: [Function] }
    console.log(A.prototype);
    //打印nodejs中的stream.prototype
    var stream = require("stream");
    //Stream { pipe: [Function] }
    console.log(stream.prototype);

    打印函数

    function B(x,y){
        this.x = x;
        this.y = y;
    };
    
    var A = function (x,y) {
        B.apply(this,arguments);
        // body..
    };
    A.prototype.pipe = function(){
    
    };
    A.prototype.pipe2 = function(){
    
    }
    A.getA =function(){
    
    }
    A.getB = function(){
    
    }
    //{ [Function: A] 
    //   getA: [Function], 
    //   getB: [Function] 
    //   }
    console.log(A); 

    mixin设计模式

    可以将继承MiXin看作为一种通过扩展收集功能的方式

    e.mixin = function(t) {
       for (var i in e.prototype)
          t[i] = e.prototype[i];   //调用该模块,t继承了e.prototype的方法,
       return t
    }
    //e.prototype写法是:
    function e() {}
    var j = e.prototype;
    return j.on = function(e, t) {},
    j.emit =function(e,t){},
    j //引用,e.prototype中的方法都是j的方法,即实现了继承。

    下面是一个简单的mixin模式写法

    function A(x){
        this.x = x;
    }
    A.prototype.setA = function(){console.log("coming in a");}
    A.prototype.setB = function(){console.log("coming in b");}
    A.mixin = function(t){
        for(key in A.prototype){
            t[key] = A.prototype[key];
        }
    }
    var b= {};
    A.mixin(b);
    b.setA();        //coming in a

    小结:mixin设计模式非常像拷贝继承,简单说,把父对象的所有属性和方法,拷贝进子对象,扩展方法,都是从父对象来扩展方法。

    在nodejs中util包有个函数inherits,也具有mixin的功能。建议用ES6 的 class 和 extends 关键词获得语言层面的继承支持,记住下面的套路:

    const util = require('util');
    const EventEmitter = require('events');
    
    function MyStream() {
      EventEmitter.call(this);
    }
    
    util.inherits(MyStream, EventEmitter);
    
    MyStream.prototype.write = function(data) {
      this.emit('data', data);
    };
    
    const stream = new MyStream();
    
    console.log(stream instanceof EventEmitter); // true
    console.log(MyStream.super_ === EventEmitter); // true
    
    stream.on('data', (data) => {
      console.log(`接收的数据:"${data}"`);
    });
    stream.write('运作良好!'); // 接收的数据:"运作良好!"
  • 相关阅读:
    vb.net 与 c# 运算符区别
    获取任务栏坐标
    获取系统任务栏高度
    【HDOJ5555】Immortality of Frog(状压DP)
    【HDOJ5559】Frog and String(构造)
    【HDOJ5558】Alice's Classified Message(后缀数组)
    【Hihocoder1634】Puzzle Game(DP)
    【HDOJ5981】Guess the number(DP)
    【HDOJ5975】Aninteresting game(BIT原理)
    【HDOJ5973】Game of Taking Stones(Java,威佐夫博弈)
  • 原文地址:https://www.cnblogs.com/liuyinlei/p/5846289.html
Copyright © 2011-2022 走看看