美文网首页
JS 继承详解

JS 继承详解

作者: wyc0859 | 来源:发表于2022-03-10 00:15 被阅读0次

原型链继承

原型链继承实现的本质: 是改变 构造函数的.prototype的指向
原型链继承容易被遗忘的重要一步: 构造函数.prototype.constructor = 构造函数
原型链继承的不足: 不能通过子类构造函数向父类构造函数传递参数

function banji(className, teacherName) {
  this.className = className;
  this.teacherName = teacherName;
}

function student(name, age) {
  this.name = name;
  this.age = age;
}

const twoClass = new banji("二班", "小雪老师");
const lilei = new student("李雷", 18);
console.log("二班:", twoClass); // {className: '二班', teacherName: '小雪老师'}
console.log("李雷:", lilei); // {name: '李雷', age: 18}
console.log("李雷的原型空间:", lilei.__proto__); // constructor: ƒ student(name, age)

console.log("student构造函数:", student()); //空,因为没return
console.log("student的原型空间:", student.prototype); // {constructor: student函数}

student.prototype.job = "学生"; //此时20行打印会立即变化,因为对原型对象的修改是实时的
banji.prototype.course = ["数学", "物理", "化学"];

student.prototype = twoClass; //指向twoClass,来作为原型空间。即原型链继承
// banji {className: '二班', teacherName: '小雪老师'}
console.log("原型链继承后的 student的原型空间1:", student.prototype); //但作为原型空间怎能没有construct属性
student.prototype.construct = student;
//对原型对象的属性或方法做修改或新增,所有的实例对象立即可以访问
//如果刷新浏览器,会发现原来第19行的打印都会跟着改变。

//这行打印多余,因为和31行一样。是实时改变,而不论先后顺序
console.log("原型链继承后的 student的原型空间2:", student.prototype);

以上就是原型链继承,结构图如下

JS原型链继承.png

原型链继承+借用构造函数

2者结合后,2缺点就不存在了。但新缺点就是父类会被调用2次

function banji(className, teacherName) {
  console.log("banji构造函数执行了");

  this.className = className;
  this.teacherName = teacherName;
}

banji.prototype.str = function () {
  console.log(`${this.name}在${this.className},老师是:${this.teacherName}`);
};

function student(name, age) {
  this.name = name;
  this.age = age;
  banji.apply(this, ["二班", "小雪老师"]); //第二次触发banji构造函数
}

const twoClass = new banji("二班", "小雪老师"); //第一次触发banji构造函数
student.prototype = twoClass; //原型链继承
student.prototype.construct = student; // 添加原型空间缺少的construct属性
const lilei = new student("李雷", 18);

console.log("班级名称:", lilei.className);
console.log("lilei.__proto__:", lilei.__proto__); //结果为:二班实例对象

lilei.str(); //李雷在二班,老师是:小雪老师 //自己会一层层的去找,无需具体位置
lilei.__proto__.__proto__.str(); //undefined在undefined,老师是:undefined
//直接从具体位置调用,反倒拿不到前面的数据

继承到这里,即实现了调用父类对象,也可以传值给父类构造函数。
但缺点就是父类会被调用2次

寄生组合继承模式=借用构造函数+原型链继承+寄生继承

利用中间函数,完美的解决了调用2次构造函数的问题

function banji(className, teacherName) {
  console.log("banji构造函数执行了");
  this.className = className;
  this.teacherName = teacherName;
}

banji.prototype.str = function () {
  return `${this.name}在${this.className},老师是:${this.teacherName}`;
};

function student(name, age, className, teacherName) {
  this.name = name;
  this.age = age;
  banji.apply(this, [className, teacherName]);
}

//寄生组合继承实现步骤
// 第一步: 创建一个寄生构造函数
function _extend() {
  this.title = "某属性";
}
const one_extend = new _extend();
console.log("one_extend:", one_extend);
console.log("one_extend的原型对象:", one_extend.__proto__); //对的,是寄生构造函数原型对象

//寄生构造函数的原型对象 => 班级构造函数的原型对象
_extend.prototype = banji.prototype;

// 第二步:创建一个 寄生构造函数的实例对象
const two_extend = new _extend();
console.log("two_extend:", two_extend);
console.log("two_extend的原型对象:", two_extend.__proto__); //对的,是班级原型对象

// 第三步:student子类的原型对象属性指向第二步的 寄生实例对象
student.prototype = two_extend;
student.prototype.constructor = student;
//往two_extend寄生实例对象上加了一个constructor属性,让他指向自己

let one = new student("李雷", 18, "二班", "小雪老师");
console.log("学生A的原型对象:", one.__proto__); //banji {title: '某属性', constructor: ƒ}
console.log("学生A的原型对象下C属性:", one.__proto__.constructor); //student(name, age) {}
/**
 * 学生A实例对象的原型(student.prototype) -> 寄生实例对象two 【它的原型】-> 班级构造函数的原型对象
 * 得出:学生A实例对象的原型 == 班级构造函数的原型对象
 *
 * 学生A实例对象的原型下constructor属性(student.prototype.constructor)-> 学生构造函数空间
 * 得出:学生A实例对象的原型下constructor属性 == 学生构造函数空间
 */

console.log("学生A属性:", one);
console.log("学生A信息:", one.str());
//所以学生的实例对象:有班级的原型对象属性方法。它的原型下construct属性==自己的构造函数空间。完美

let two = new student("梅梅", 17, "三班", "大壮老师");
console.log("学生B属性:", two);
console.log("学生B信息:", two.str());

上面已经OK了,但封装下,复用性更好


封装:寄生组合继承模式

封装后2行代码就能实现继承。Object.create方法和自行封装的寄生组合继承,逻辑代码上是一样的,但调用需3行代码。且内部不能做任何扩展,自行封装的自由度高,所以用自行封装的更好用

student.prototype = _extend(banji, student);
const a = new student("a", 18, "二班", "小雪");
function banji(className, teacherName) {
  console.log("banji构造函数执行了");

  this.className = className;
  this.teacherName = teacherName;
}

banji.prototype.str = function () {
  return `${this.name}在${this.className},老师是:${this.teacherName}`;
};

function student(name, age, className, teacherName) {
  this.name = name;
  this.age = age;
  banji.apply(this, [className, teacherName]);
}

//寄生组合继承实现步骤
function _extend(parent, son) {
  function Middle() {
    this.title = "某属性";
    this.constructor = son; //往寄生实例对象上加了constructor属性,让他指向自己(子类)
  }
  Middle.prototype = parent.prototype; //寄生构造函数的原型对象 => 父类构造函数的原型对象

  let middle = new Middle();
  return middle;
}

student.prototype = _extend(banji, student);
const a = new student("a", 18, "二班", "小雪");
console.log("学生A属性:", a);
console.log("学生A信息:", a.str());

Object.setPrototypeOf 与 Object.crate 区别

setPrototypeOf(现有对象,原型对象),为现有对象设置原型,返回一个新对象
接收两个参数,并且可继承父类的静态方法,是es6方法
Object.crate(原型对象,{参数}) 会使用指定的原型对象以及属性去创建一个新的对象,是es5方法

2者的区别:Object.crate继承了原型,但不能再访问原型中的前属性。Object.setPrototypeOf则可以访问

function banji(className, teacherName) {
  this.className = className;
  this.teacherName = teacherName;
}
banji.static_grade = "五年级";  //静态属性
function student(name, age) {
  this.name = name;
  this.age = age;
}

const s_a = new student("小A", 18);
console.log("a的原型空间-1:", s_a.__proto__); //constructor: ƒ student(name, age)
Object.setPrototypeOf(s_a, banji.prototype); //第二个参数也可以是 banji;和banji.prototype的区别在静态属性上
console.log("a的原型空间-2:", s_a.__proto__); //constructor: ƒ banji(className, teacherName)
//浏览器控制台能看见打印出的结果,constructor下有grade静态属性

相关文章

  • JS继承详解

    1.原型链继承  原型链继承所带来的问题:   ① 引用类型的属性被所有实例共享。  ② 在创建 Child 的实...

  • JS 继承详解

    原型链继承 原型链继承实现的本质: 是改变 构造函数的.prototype的指向原型链继承容易被遗忘的重要一步: ...

  • 详解js继承

    详解js继承 来源视频讲解:https://www.bilibili.com/video/BV1J54y1y7u9...

  • Javascript(三)之原型继承理解

    进阶路线 3 原型继承 3.1 优秀文章 最详尽的 JS 原型与原型链终极详解 一 最详尽的 JS 原型与原型链终...

  • 扣丁学堂JavaScript实现面向对象继承的五种方式

    今天扣丁学堂老师给大家主要介绍了JS实现面向对象继承方式的详解,首先js是门灵活的语言,实现一种功能往往有多种做法...

  • javascript - 如何优雅的实现继承

    这篇文章不好开篇 , 在学习继承的路上挖的一个坑。在此记录。 参考 三生石的js继承详解还有这里作者不清楚名字但是...

  • Js的继承

    js的继承 @(js)[继承, js, 前端] 组合继承是原性链继承和构造函数继承的合体,它汲取了二者各自的有点,...

  • js的完美继承方式详解

    分为六步,能看完的话,不会你打我 类式继承、构造函数继承、组合继承、原型继承、寄生式继承、寄生组合式继承 困了 安

  • js 继承的几种方式详解

    不同于其他面向对象语言,js是一种弱类型语言,它没有interface、implements、constructo...

  • ListView详解,RecyclerView和ListView

    ListView详解 直接继承自AbsListView,AbsListView继承自AdapterView,Ada...

网友评论

      本文标题:JS 继承详解

      本文链接:https://www.haomeiwen.com/subject/fgaxdrtx.html