继承小结

实例和对象的区别

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('运作良好!'); // 接收的数据:"运作良好!"
原文地址:https://www.cnblogs.com/liuyinlei/p/5846289.html