彻底弄懂对象的继承关系

559 阅读32分钟

1 前言

说起对象,应该想到的是对象的三个基本特性:封装、继承、 多态。

1.1 封装:通俗的来说,就是封装函数,通过私有化的变量和私有化的方法,不让外部访问到;

1.2 继承:简约来说可分为两种:1).通过原型prototype对象实例化实现此类的继承。2).直接实例化对象复制,完全复制父类对象;

1.3 多态: 多态就是通过对传递的参数判断来执行逻辑,即可实现一种多态处理机制。

2 继承  

说起继承肯定是想起prototype , __proto__ , constructor,  那么这三者之间到底是什么一样关系呢?

下面先用一张图来展示下:

function Parent() {       
    // 构造函数中的this 通过new调用的,那么this指向的就是那个实例        
    this.name = '我是一个构造函数';        
    this.age = '18';  
    //这里定义的属性和方法都是该构造函数内的私有属性和方法    
}   
Parent.prototype.eat =function() {     // 在原型对象定义的方法是公有的 
     console.log('eat');     
 }      
console.log( Parent.prototype.constructor === Parent) // true
let parent = new Parent();parent.eat() 等价于 parent.__proto__.eat()  //  先去找私有属性,找不到会去找公有属性

console.log(parent.__proto__ === Parent.prototype )  // true

实例的__proto__属性指向原型对象,调用当前实例对象的一个属性时 如果不存在,那么会沿着原型链一直寻找,直到链的末端。

链的末端: Object.prototype.__proto__ 

console.log( Parent.prototype.__proto__ === Object.prototype )  // true

console.log( Object.prototype.__proto__ === null )                       // true

当通过new调用函数创建实例后,该实例内部包含一个指针 [[Prototype]] 指向构造函数的原型对象,该属性为内部属性,无法直接访问,但在Firfox、Chrome和Safari中有一个 __proto__ 属性可以访问到构造函数的原型。

2.1 只继承私有属性

function Parent(){   
    this.name = 'parent';    
}     
Parent.prototype.eat =function() {     
    console.log('eat');     
}      
function Child(){   
   this.age = 9;       
   Parent.call(this);     
}     
let child = new Child();      
console.log(child.name)    // parent

以上定义了两个类, 一个Parent、一个Child,   在Child的内通过调用**Parent.call(this),**可以将父类的私有方法挂载在子类Child内部,这样在Child的实例上就可以拿到Parent上的方法了。

2.2  只继承公有属性

function Parent() {
     this.name = 'parent';     
}      
Parent.prototype.eat =function() {
      console.log('eat');      
}      
function Child() {
      this.age = 9;      
}      
Child.prototype.smoking = function() {
      console.log('吸烟');      
} 
     
Object.setPrototypeOf(Child.prototype, Parent.prototype) // 只继承公有属性    
  
//Child.prototype = Object.create(Parent.prototype)  // 只继承公有属性      

let child = new Child(); 
           
child.eat()    // 打印出了 eat , 拿到了父类原型对象上公有的方法了      
console.log(child.name)  // undefined  没有拿到父类私有属性

通过 Object.setPrototypeOf(Child.prototype, Parent.prototype) ||  Child.prototype = Object.create(Parent.prototype),

这两种方式, 可以实现只继承父类的私有属性。

其中Child.prototype.__proto__ = Parent.prototype;   等价于 Object.setPrototypeOf(Child.prototype, Parent.prototype), 
一个是ES5中的方法, 另外一个是ES6中的方法。
Child.prototype = Object.create(Parent.prototype)  这种方法 最优

2.3 继承私有和公有属性

 Child.prototype = new Parent();

//   这种方式不会使用 , 一般都是通过es6 里面的类的继承 extends 实现。

extends  可以实现私有属性、公有属性、静态方法的继承。