JavaScript的“继承”

原型链:每个对象内部存在一个内部链接,它会引用其他对象(称为原型对象),如果对象上没有找到需要的属性或者方法引用,引擎会去它链接的原型对象上查找,如果找不到再去链接的对象自身的原型对象上查找,以此类推,直到查找到Object的prototype原型对象,这一系列对象的链接就称作原型链。

在构造函数、实例和原型对象之间的原型链可以具体表现为:每个构造函数都有一个原型对象、原型对象上有一个属性指回构造函数、实例上有一个内部指针指向原型对象。当原型对象是另一个类型的实例时,就会和另一个组实例、原型对象、构造函数之间相关联,从而形成这些对象之间的链接。代码表现为下:

function Bar(){
  console.log('Bar构造函数')
}
//new 关键字实际上是创建了一个新对象并原型对象关联。
let bar1 = new Bar();  // bar1 实例

Bar.prototype // 指向原型对象
Bar.prototype.constructor // 指向构造函数
bar1.__proto__ // 指向原型对象
console.log(bar1.__proto__ === Bar.prototype) //true


//扩展
//new方法的简单实现
let newMethod = function (Parent, ...rest) {
  // 1.以构造器的prototype属性为原型,创建新对象;
  let child = Object.create(Parent.prototype);
  // 2.将this和调用参数传给构造器执行
  let result = Parent.apply(child, rest);
  // 3.如果构造器没有手动返回对象,则返回第一步的对象
  return typeof result  === 'object' ? result : child;
};

继承的实现方式:

1、原型链继承

通过改变默认原型对象为另一对象的实例实现

function Parent(){
  console.log("父类")
}
function Child(){
  console.log("子类")
}
Child.prototype = new Parent();

2、盗用构造函数继承

在子类构造函数中调用父类构造函数,利用apply,call实现。

function Parent(){
  console.log("父类")
}
function Child(){
  Parent.call(this);
  console.log("子类")
}
let child = new Child();

3、组合继承
利用原型链继承原型上的属性与方法,再盗用构造函数继承实例属性。

function Parent(name){
  this.name = name;
  console.log("父类")
}
function Child(name, age){
  //继承属性
  Parent.call(this,name); 
  this.age = age;
  console.log("子类")
}
//继承方法
Child.prototype = new Parent();

4、原型式继承

通过一个临时构造函数实现原型链继承。

function object(o){
  function F(){};
  F.prototype = o;
  return new F();
}
//ES5增加了Object.create()方法以实现原型式继承
//ES6之后,新增setPrototypeOf、getPrototypeOf
Object.setPrototypeOf(Bar.prototype, Foo.prototype);

5、寄生式继承

创建一个实现继承的函数、以某种方式增强对象,然后返回这个对象。

function createAnother(origin){
  let clone = Object.create(origin)
  //以某种方式增强对象
  clone.sayHi = function(){
    console.log('hi');
  }
   //返回这个对象
  return clone;
}

6、寄生组合继承

使用寄生式继承来继承父类原型,然后将返回的新对象赋值给子类原型。

function extendPrototype(Child, Parent){
  //创建指向原型指向Parent的对象prototype
  let prototype = Object.create(Parent); 
   //修复丢失的构造函数
  prototype.constructor = Child; 
  Child.prototype = prototype;  
}

以上是javaScrpit中的几种继承方式,然而除了构造函数继承外,涉及[[prototype]]的继承实际上都是对象之间的委托关联。javaScrpit中的继承不是像传统类那样执行复制操作。

委托是不同于类的设计模式,对象并不是按照父类到子类的关系垂直组织的,而是通过任意方向的委托关联并排组织的。

let Foo = {
  setName: function(Name) { this.name = Name; },
  getName: function() { console.log( this.name ); }
};
  // 让Bar委托Foo
let Bar = Object.create( Foo );
// Bar是存在一个原型对象指向Foo的对象。
Bar.SetNameAndAge = function(Name,Age) { 
  this.setName( Name );
  this.age = Age;
};
Bar.getNameAndAge = function() { 
  this.getName();
  console.log( this.age ); 
};
// 让b1委托Bar
let b1 = Object.create( Bar );
b1.SetNameAndAge('tom',3)
// 让b2委托Bar
let b2 = Object.create( Bar );
b2.SetNameAndAge('jerry',4)

b1.getNameAndAge()
b2.getNameAndAge()

 

上一篇:纯C/C++封装的INI文件读写类


下一篇:IIS 布署net项目报 HTTP错误 500.19-Internal Server Error