JavaScript 高级3(构造函数,原型-对象、继承)

JavaScript 高级

1、编程思路

1.1、了解-面向过程与面向对象

  • 面向过程

    分析问题中的关键步骤,并将步骤封装成函数,然后按顺序依次调用即可。

  • 面向对象

    分析问题中的关系步骤,将这些步骤划分到不同的主体(对象)上,然后按顺序依次调用主体上的方法即可。

    封装对象其实并不是为了解决问题,而是为了表示函数的所属关系。

  • 代码演示

    
    
    Document
    
    
    
    

1.2、了解-面向对象的三大特性

  • 封装

    严格的来按面向对象的思想来书写代码。

  • 多态

    多种形态。

  • 继承

    一个对象使用另一个对象上的属性或方法

👉 以上三大特性是纯正的面向对象语言所具有的,但JavaScript并不是基于对象的语言,并不是面向对象的语言,所以,这三大特性在JS中的体现并不明显。

2、构造函数

2.1、体现封装

👉 构造函数,是面向对象三大特性的封装性的体现。

  • 其目的

    将数据与操作数据的方法定义到一个主体上,方法内通过this来访问数据。

    实例化出来的每个对象上各自保留有自己的数据与操作数据的方法。

  • 示例代码

    /* 
    构造函数就是面向对象三大特性之一,封装性,在JS这种语言中的代码体现。
    */
    function Person(){this.age = 20;          // 数据保存到对象的属性上this.say = function(){// 将操作数据的代码写到对象的方法上。   console.log(this.age);}
    }let obj = new Person()
    obj.age
    obj.say()// 通过构造函数,将数据,以及操作数据的代码,封装一对象上// 封装性代码体现就是,构造函数,
    

2.2、内存浪费

  • 思考

    同一个构造函数实例化出来的对象,每个对象上会保存各自的属性与方法。

    对于属性中所保存的数据,很多时候是不同的,所以每个对象要有各自的属性。

    但对于方法,来自同一个构造函数实例化出来的对象,所有的方法都是相同的,每个对象都保存一份,这会造成内存的浪费。

  • 验证代码

    === 可以验证是不是同一个对象,同一个对象,同一个方法。

    // 本小节主要引出一个小问题,通过这个问题去引出另一个知识点。
    function Person(a,n){// 由于每个对象的属性数据不同,所以不写死,动态传递this.age = athis.name = n// 由于每个对象方法代码一般都是相同的,所以直接写死this.say = function(){// 无论当前构造函数,new出多少个对象,同一个方法内的代码是相同的,}
    }let oa = new Person(20,'zhangsan')
    let ob = new Person(22,'lisi')console.log(oa);
    console.log(ob);console.log(oa.say === ob.say);            // false

3、原型对象

3.1、简介

  • 原型对象

    在JS中只要有一个函数存在,JS就会创建一个与之对应的对象。这个对象就是原型对象。

    这个对象只有一个属性 constructor

  • 原型对象的访问

    原型对象不可以直接访问,只能通过函数或实例对象间接访问。

    • 函数.prototype
    • 实例对象.proto
  • 示例代码

    function Fn(){}
    console.log(Fn.prototype);let obj = new Fn();
    console.log(obj.__proto__);
    

3.2、构造函数与原型对象之间的关系

👉 构造函数与原型对象之间的关系 主要是体现在以下两个属性的相互指向上。

  • prototype

    构造函数.prototype 指向 原型对象

  • constructor

    原型对象.constructor 指向 构造函数

  • 示例代码

    function Person(){}// 构造函数是通过 prototype属性 "指向" 原型对象
    console.log(Person.prototype);// 构造函数是通过 contructor 属性 "指向" 构造函数
    console.log(Person.prototype.constructor);console.log(Person.prototype.constructor === Person);
    

3.3、实例对象与原型对象之间的关系

👉 实例对象与原型对象之间的关系 主要是体现在__proto__这个属性上。

  • _proto_

    实例对象._proto_ 指向 原型对象

  • 示例代码

    
    
    Document
    
    
    
    

3.4、构造函数、原型对象、实例对象的三角关系

👉 三角关系的主要体现在三个属性上

  • prototype

    构造函数.prototype 指向 原型对象

  • constructor

    原型对象.constructor 指向 构造函数

  • _proto_

    实例对象._proto_ 指向 原型对象

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kGgmVTIx-1671867193274)(./assets/image-20220623142235739.png)]

3.5、原型对象的特点

  • 原型对象上的成员会被所有的实例对象所共享

    就是当一个实例对象访问某个成员时,如果不存在会自动到原型对象上找。

  • 示例代码

    // 首先在JS中任何一个对象上一级都有它的原型对象// 原型对象的特点
    //  当某一个对象,访问一个成员(属性或方法)不存在时,会自动到它的原型对象上找这个成员进行使用。function Person(){// this.age = 20;}// 在原型对象上添加一个属性 age
    Person.prototype.age = 22let oa = new Person()console.log(oa.age);
    

👉 使用原则:所有实例共享(一份)保存到原型对象上,如果每个实例都要独有的(多份)定义在构造函数内。

4、原型继承

4.1、修改prototype指向

  • 原型对象也是可以被替换掉的

    修改之前实例化的对象,访问之前的原型对象上的方法

    修改之后实例化的对象,访问之后的原型对象上的方法

  • 示例代码

    // 默认:函数的原型对象是系统创建的,这个原型对象是可以被修改。
    function Person(){}Person.prototype.a = 20;let oa = new Person();
    console.log(oa.a);let abc = {}// ------------ 修改prototype的指向(让prototype属性指向另一个对象)
    Person.prototype = abc// 修改之后,实例化另一个对象
    let ob = new Person();console.log(ob.a);
    console.log(oa.a);
    

4.2、原型继承

👉 在JS中继承的方法有很多,这里我们只了解一种常用的 原型继承

  • 原型继承

    原型继承也称之为替换原型继承。

    主要思想是 使用父的实例对象来替换子的原型对象。

  • 代码实现:

    // 继承:
    //      一个对象使用另一个对象上的成员//      使用父的实例(实例对象),来替换子的原型function A(){           // 父this.x = 100;this.y = 200;}let oA = new A()        // 这是父的一个实例对象function B(){           // 子}          // 使用父的实例(实例对象),来替换子的原型
    B.prototype = oAlet oB = new B()
    console.log(oB.x);
    console.log(oB.y);/* 
    小结:JS中继承实现有很多种,使用父的实例,来修改子的原型。*/
    

5、原型链

5.1、什么是原型链

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OUMPT6sI-1671867193278)(assets/image-20220717153152960.png)]

  • 什么是原型链

    原型链是由原型对象形成的一个链条。

    👉 原型链是通过__proto__形成的链接

  • 示例代码

    // 原型链:由原型对象形成的一个"链条"function Person(){}let oa = new Person()// 当一个对象访问某个成员,成员不存在时,会自动到原型对象上找。
    // console.log(oa.age);// 原型对象也是对象,原型对象上在查找age属性时,
    // 如果没有,会到原型对象的原型对象上找这个属性
    console.log(Person.prototype);    // 一级原型
    console.log(Person.prototype.__proto__);  // 二级原型
    console.log(Person.prototype.__proto__.__proto__);  // null// 默认每一个原型对象都有一个函数与之对应
    // 一级原型对应的构造函数 Person这个函数   二级原型对象的构造函数是 Object()
    // 验证 === 
    console.log(Person.prototype.__proto__.constructor === Object);
    

5.2、原型链的查找规则

  • 原型链的查找规则

    原型的作用是当实例对象上访问某个成员不存在时,则会到原型对象上找。

    而原型链是当原型对象上访问某个成员不存在时,会到原型对象的原型对象上找。

    这样就形成了一级一级向上找的过程。

    就近原则,

  • 示例代码

    function Person(){}// Person.prototype.n = 10;
    Person.prototype.__proto__.n = 20;let oa = new Person();
    console.log(oa.n);
    

5.3、instanceof 运算符

  • 语法:

    对象 instanceof 函数

  • 说明:

    作用于判断构造函数与实例对象的关系,

    依据是构造函数对应的原型对象是否在以实例为起点的原型链上。

  • 示例代码

    function Human(){}function Person(){}let oa = new Person();
    console.log(oa instanceof Person);      // true
    console.log(oa instanceof Human);       // false
    console.log(oa instanceof Object);       // true
    

6、面向对象-模态弹窗

6.1、封装构造函数

  • 面向对象思想分析

    采用面向对象思想,先找对象,我们将弹窗当成一个对象。

    构造函数体现面向对象代码体现,通过构造函数可以得到对象。

  • 对象设计

    弹窗标题 与 弹窗内容,可以看作是弹窗相关的数据,使用属性保存

    弹窗需要一个html标签来显示页面效果,这个标签也看作弹窗的属性

    弹窗要显示,所以弹窗需要具有显示的方法。

  • 实现

    function Modal(t, c) {this.title = tthis.content = cthis.el = document.createElement('div')this.el.className = 'modal'this.el.innerHTML = `
    ${this.title} x
    ${this.content}
    `this.show = function () {document.body.appendChild(this.el)} }// 测试:删除按钮事件 document.querySelector('#delete').addEventListener('click', function(){let oM = new Modal('温馨提示','您无权删除')oM.show() })// 测试:登陆按钮事件 document.querySelector('#login').addEventListener('click', function(){let oM = new Modal('登陆成功', '正在为您跳转')oM.show() })

6.2、实现模态弹窗

  • 分析

    同一时刻只能出现一个弹窗,这种就是模态弹窗

    弹窗显示时,先获取页面获取弹窗用到的标签,如果有则不显示第2个,如果没有才显示

  • 实现

    function Modal(t, c) {this.title = tthis.content = cthis.el = document.createElement('div')this.el.className = 'modal'this.el.innerHTML = `
    ${this.title} x
    ${this.content}
    `this.show = function () {// -------- 判断页面上是否有 弹窗存在if (!document.querySelector('.modal')) {document.body.appendChild(this.el)}} }// 测试:删除按钮事件 document.querySelector('#delete').addEventListener('click', function(){let oM = new Modal('温馨提示','您无权删除')oM.show() })// 测试:登陆按钮事件 document.querySelector('#login').addEventListener('click', function(){let oM = new Modal('登陆成功', '正在为您跳转')oM.show() })

6.3、实现关闭

  • 分析

    为关闭按钮注册事件,

    事件内从body标签中将弹窗删除

  • 实现

    function Modal(t, c) {this.title = tthis.content = cthis.el = document.createElement('div')this.el.className = 'modal'this.el.innerHTML = `
    ${this.title} x
    ${this.content}
    `this.show = function () {// 判断页面上是否有 弹窗存在if (!document.querySelector('.modal')) {document.body.appendChild(this.el)// -------- 为 关闭按钮 注册事件,document.querySelector('.modal .header i').addEventListener('click', function(){// -------- 在事件内将 .modal 元素从页面里移除即可document.body.removeChild(document.querySelector('.modal'))})}} }// 测试:删除按钮事件 document.querySelector('#delete').addEventListener('click', function(){let oM = new Modal('温馨提示','您无权删除')oM.show() })// 测试:登陆按钮事件 document.querySelector('#login').addEventListener('click', function(){let oM = new Modal('登陆成功', '正在为您跳转')oM.show() })