十二、对象继承深入、call_apply、※圣杯模式、※构造函数和闭包、※企业模块化

本文探讨JavaScript中的对象继承深入,包括原型链继承、call和apply的使用。重点讲解圣杯模式,如何通过中间变量实现对象继承而不影响原型。同时讨论构造函数和闭包的关系,以及如何在实际的企业模块化开发中应用圣杯模式和闭包,以防止全局变量污染并提高代码可维护性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

十二、对象继承深入、call_apply、※圣杯模式、※构造函数和闭包、※企业模块化

对象继承深入

原型链继承

原型链定义

​ 对象沿着__proto__在原型上寻找属性形成一种链条式的继承关系,这种继承关系就叫做原型链。

例如:

Professor.prototype = {
    name: 'Mr.Zhang',
    tSkill: 'Java'
}
function Professor(){}
var professor = new Professor();

Teacher.prototype = professor;
function Teacher(){
    this.mkill = 'JS/JQ';
    this.name = 'Mr.Wang'
}
var teacher = new Teacher();

Student.prototype = teacher;
function Student(){
    this.name = 'Mr.li'
    this.pkill = 'HTML/CSS';
}
var student = new Student();
console.log(student);

​ 通过指定原型的指向,使学生实例继承老师实例,老师实例继承教授实例,而教授实例继承它的原型,这样学生就可以继承原型链上的所有属性。

在这里插入图片描述

处在原型链底端的实例继承了原型链上所有的属性,可这样合适吗?有些属性并不需要,有些属性又重复了。

call_apply是怎么解决这个问题的?

call_apply

Teacher.prototype.TSkill = 'Vue';

function Teacher(name, mSkill){
    this.name = name;
    this.mSkill = mSkill;
}

function Student(name, mSkill, age, major){
    Teacher.apply(this,[name, mSkill])
    this.age = age;
    this.major = major;
}

var student = new Student(
    'white', 'JS', 18, 'computer' 
);
console.log(student)

在这里插入图片描述

​ 通过call和apply更改this指向,借用构造函数的属性和方法,而不能继承构造函数的原型。

一般来说我们更希望去继承这个原型对象,因为原型对象中一般写一些写死的方法,或者是实例化对象的公共方法,所以apply这个借用构造函数的属性也不太合理。


那我们直接把构造函数的原型对象指向另一个构造函数的原型对象,这样不就达到我们继承原型对象的目的了吗?

function Teacher(){
    this.name = 'Mr.Liu'
}

Teacher.prototype = {
    pSkill: 'JS/Vue'
}
var t1 = new Teacher();

function Student(){
    this.name = 'white'
}

Student.prototype = Teacher.prototype;
Student.prototype.sport = 'baskerball';

var student = new Student();
console.log(student);
console.log(t1);

学生实例继承了老师构造函数的原型对象了吗? √
在这里插入图片描述

那更改了学生的原型对象,老师的原型的原型对象会更改吗? √

给学生的原型对象增加了一个属性,发现老师的原型对象中也增加了一模一样的一个属性,显然不合理!

在这里插入图片描述

能不能借用一个中间变量,通过中间变量,实例化对象既可以继承顶端构造函数的的原型对象,而修改自身的原型对象时,也不会更改所继承的原型对象

这就是圣杯模式。

※圣杯模式

借用中间变量(圣杯),到达对象继承原型的效果。

实例化对象继承其他构造函数的原型对象属性/方法,并且更改实例化对象的构造函数的原型时,也不会影响其他构造函数的原型。

function Teacher(){
    this.name = 'Mr.Liu';
    this.tSkill = 'Java'
}

Teacher.prototype = {
    pSkill: 'Js/jq'
}

function Student(){
    this.name = 'white'
}
//中间构造函数的原型对象指向被继承的构造函数的原型对象
Buffer.prototype = Teacher.prototype;
function Buffer(){}
var buffer = new Buffer();

//底部的构造函数指向中间构造函数的实例
Student.prototype = buffer;
Student.prototype.age = 22;
var stu1 = new Student();

console.log(stu1);
console.log(Student.prototype.constructor);
console.log(buffer);
console.log(Teacher.prototype);
console.log(Teacher.prototype.constructor);

在这里插入图片描述

圣杯布局过程:

  1. 声明一个构造函数作为中间变量
  2. 中间构造函数的原型指向被继承的构造函数的原型对象
  3. 底部的构造函数的原型指向中间构造函数的实例

这样底部的实例化对象即可以继承顶部的原型对象,修改底部构造函数的原型对象时,也不会影响其他构造函数的原型对象。

而修改底部构造函数的原型对象时,是中间构造函数的实例化对象被更改了。


上图有一个问题:底部构造函数的原型对象的constructor不见了!!

原型对象的来源不见了,需要我们手动指定,而且我们可以对圣杯模式的这种继承方式进行一种封装:

封装圣杯模式
function inherit(Target, Origin){
    function Buffer(){}
    Buffer.prototype = Origin.prototype;
    // 先写prototype再写new
    Target.prototype = new Buffer();
    // 还原构造器
    // 需要手动写构造器指向本身
    Target.prototype.constructor = Target;
    // 设置继承源
    // 还要很容易看到它所继承的构造函数
    // 被继承的那一方(源)我们叫超类
    // super是保留字,不能使用,源这个使用什么名字都可以
    Target.prototype.super_class = Origin;
    // Target.prototype.uber = Origin;

}

封装圣杯布局的过程:

  1. 声明中间构造函数
  2. 指定中间构造函数的原型对象的指向->顶端构造函数的原型(继承源)
  3. 指定底部构造函数的原型对象的指向->中间构造函数的实例
  4. 手动书写底部构造函数的原型构造器指向->底部构造器
  5. 在底部构造函数的原型中显示声明ta所继承的构造函数(超类)

※构造函数和闭包

闭包的写法:
  • 普通函数形式写闭包:return一个内部函数,或者函数数组;
  • 对象形式写闭包:在对象内部书写方法,return这个对象;
  • 构造函数形式写闭包,this.xx=function(){},return this,实例化对象使用

自己的方法。


怎么把圣杯模式放到闭包中?
function test(){
    var Buffer = function(){}
    function inherit(Target, Origin){
        Buffer.prototype = Origin.prototype;
        Target.prototype = new Buffer();
        Target.prototype.constructor = Target;
        Target.prototype.super_class = Origin;
    }
    return inherit;
}
var inherit = test();

既然是执行返回函数,之后再执行,那么可以被IIFE包裹。

var inherit = (function(){
    var Buffer = function(){}
    return function inherit(Target, Origin){
        Buffer.prototype = Origin.prototype;
        Target.prototype = new Buffer();
        Target.prototype.constructor = Target;
        Target.prototype.super_class = Origin;
    }
})();

这就是企业级写圣杯模式继承的方式。

为什么要放到闭包+IIFE中?

闭包使Buffer成为了私有变量,防止全局变量污染。

IIFE:页面加载就执行,拥有了自己的命名空间,使函数整体隔离了全局作用域,便于后期维护,二次开发。

※企业协同开发

※企业模块化IIFE+全局变量接收返回值

企业模块化目的:

  • 防止全局变量被污染
  • 页面一加载就拿到函数的返回值,等待执行
var initProgrammer = (function(){
    return;
})();

多个功能怎么实现模块化呢:(原生开发)

  1. 把自启动函数的执行放到初始化函数中。
  2. 页面加载时调用初始化函数。
window.onload = function(){
    init();
}

function init(){
    initProgrammer();
    initFunction();
}

var initProgrammer = (function(){
    return;
})();

var initFunction = (function(){
    return;
})();

企业插件:IIFE+构造函数相关+window暴露到全局

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值