继承原理详解

一,对象的方法

1, setPrototypeOf 和 getPrototypeOf

//__proto__链
//在es6中可以在对象内直接操作__proto__

let obj1={name:'wdwd'};
let obj2 = {age :8};
// obj1.__proto__ = obj2;
Object.setPrototypeOf(obj1,obj2);
console.log(Object.getPrototypeOf(obj1));//{ age: 8 }

2, super

let obj2 = {
   age:10,
   name:'erer'
};
let obj = {
   name:'adawd',
   getPName(){//可以通过super关键字获取到父类的属性
      return super.name
   },
   __proto__:obj2
}
console.log(obj.getPName());//erer

二,继承

在es5中没有类的概念, 只有构造函数
在es6中 有class关键字
类的属性 三种属性:公有属性(__proto__),私有属性,静态方法(静态属性)
function Parent(){
   // 构造函数中的this,通过new调用的那么this指代的是实例
   this.name = 'parent';
}
Parent.prototype.eat=function () {
   console.log('eat');
}
let parent = new Parent();
parent.__proto__.eat();//会先找私有属性,找不到再去找公有属性
console.log(Parent.prototype.constructor === Parent);//true

在这里插入图片描述

1. 继承私有属性:

只有call方法改变this指向才可以

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

2. 继承公有属性:

  • Child.prototype=Parent.prototype这种方法会使他们变成兄弟关系,而不是继承关系。(大多数人使用这种方法其实是错误的,虽然它还是能拿到Parent原型上的属性,但是它破坏了这种继承关系)

  • 第一种方式: Child.prototype.__proto__= Parent.prototype等价于Object.setPrototypeOf(Child.prototype,Parent.prototype)

Parent.prototype.say= "saying";
function Parent(){
}
function Child(){
}
Object.setPrototypeOf(Child.prototype,Parent.prototype);//==Child.prototype.__proto__= Parent.prototype
let child = new Child();
console.log(child.say);//saying

  • 第二种方式:Child.prototype = Object.create(Parent.prototype). ,只继承公有属性
Parent.prototype.say= "saying";
function Parent(){
}
function Child(){
}
Child.prototype = Object.create(Parent.prototype);
let child = new Child();
console.log(child.say);//saying
console.log(child.constructor)//[Function: Parent]有一个小毛病,就是,它找到的构造出它的函数是Parent而不是Child

为什么会出现这种毛病,来分析一下Object.create(Parent.prototype)的原理

//圣杯模式
function create(parentPrototype){
   function Fn(){};
   Fn.prototype = parentPrototype;
   return new Fn();
}
Child.prototype = create(Parent.prototype);
let child = new Child();
//就是下图中的紫色线条的路线,那么child找constructor,实例上没有constructor,
//去原型上找constructor属性,原型就是实例fn,一个实例它也没有constructor属性,只能再去它的原型parentPrototype上找,
//parentPrototype原型上的constructor当然指向Parent,所以导致一个错误就是child找constructor居然是Parent。

在这里插入图片描述
第一种处理构造函数指向错误的方法:

Parent.prototype.say= "saying";
function Parent(){
}
function Child(){
}
Child.prototype = Object.create(Parent.prototype,{constructor:{value:Child}});
let child = new Child();
console.log(child.constructor)//[Function: Child]

第二种处理构造函数指向错误的方法:

Parent.prototype.say= "saying";
function Parent(){
}
function Child(){
}
function create(parentPrototype,props){
   function Fn(){};
   Fn.prototype = parentPrototype;
   let fn = new Fn();
   for(let key in props){
      Object.defineProperty(fn, key, {
         ...props[key],
         enumerable:true
      });
   }
   return fn;
}
Child.prototype = create(Parent.prototype,{constructor:{value:Child}});
let child = new Child();
console.log(child.say);//saying
console.log(child.constructor);//[Function: Child]
-----------------------------------------------------------------------------------
//以下例子解释一下 Object.defineProperty()方法的用法
let a = {}
// a.name =1;
// ES5的一个方法来添加属性:
Object.defineProperty(a,'name',{
   //前三个属性是可选的
   enumerable:true,//表示这个属性是否可以被枚举出来
   configurable:true,//表示这个属性是否可以被删除
   writable:true,//表示这个属性是否可以被写入(更改)
   value : 1
});
console.log(a.name);//1
------------

let b = {};
Object.defineProperty(b,'name',{
   //前三个属性是可选的
   enumerable:true,//表示这个属性是否可以被枚举出来
   configurable:true,//表示这个属性是否可以被删除
   // writable:true,//表示这个属性是否可以被写入(更改)
   //当有set和get的时候,writable属性就不能写。
   get(){
      console.log('get');
      return 1;
   },
   set(val){
      console.log('设置值')
   }
});
b.name = 'asd';//当给该对象设置属性的值时候,会自动调用set,打印“设置值”
console.log(b.name);//当取出属性值得时候,会自动调用get,打印“get”,还有返回值

猜你喜欢

转载自blog.csdn.net/weixin_43623871/article/details/89320585
今日推荐