JavaScript 进阶 深入面向对象

发布于:2023-01-10 ⋅ 阅读:(453) ⋅ 点赞:(0)

编程思想

面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个的依次 调用就可以了。

面向过程,就是按照我们分析好了的步骤,按照步骤解决问题。

面向对象是以对象功能来划分问题,而不是步骤。

面向对象是把事务分解成为一个个对象,然后由对象之间分工与合作。

 编程思想-面向过程和面向对象的对比

 

面向对象

面向对象编程是一种程序设计思想,它具有 3 个显著的特征:封装、继承、多态。

封装

封装的本质是将具有关联的代码组合在一起,其优势是能够保证代码复用且易于维护,函数是最典型也是最基础的代码封装形式,面向对象思想中的封装仍以函数为基础,但提供了更高级的封装形式。

<script>
  // 普通对象(命名空间)形式的封装
  let beats = {
    name: '狼',
    setName: function (name) {
      this.name = this.name;
    },
    getName() {
      console.log(this.name);
    }
  }

  beats.setName('熊');
  beats.getName();
</script>

以往以普通对象(命名空间)形式封装的代码只是单纯把一系列的变量或函数组合到一起,所有的数据变量都被用来共享(使用 this 访问)。

构造函数

同样的将变量和函数组合到了一起并能通过 this 实现数据的共享,所不同的是借助构造函数创建出来的实例对象之间是彼此不影响的。

<script>
  function Person() {
    this.name = '佚名';
    // 设置名字
    this.setName = function (name) {
      this.name = name;
    }
    // 读取名字
    this.getName = () => {
      console.log(this.name);
    }
  }

  // 实例对像,获得了构造函数中封装的所有逻辑
  let p1 = new Person();
  p1.setName('小明');
  console.log(p1.name);

  // 实例对象
  let p2 = new Person();
  console.log(p2.name);
</script>

总结:

  1. 构造函数体现了面向对象的封装特性

  2. 构造函数实例创建的对象彼此独立、互不影响

  3. 命名空间式的封装无法保证数据的独立性

 封装是面向对象思想中比较重要的一部分,js面向对象可以通过构造函数实现的封装.前面学过的构造函数方法很好用,但是存在浪费内存的问题,面向对象编程的特性:比如封装性、继承性等,可以借助于构造函数来实现.

    <script>
        function Get(uname,age) {
            this.uname = uname
            this.age = age
            this.sing = function () {
                console.log('唱歌')
            }
        }
        const ldh = new Get('刘德华',14)
        const zxy = new Get('张学友',14)
        console.log(ldh)
        console.log(zxy)
        console.log(ldh.sing === zxy.sing)(false)
        //构造函数 里面的方法 会造成浪费内存的问题
    </script>

原型对象

实际上每一个构造函数都有一个名为 prototype 的属性,译成中文是原型的意思,prototype 的是对象类据类型,称为构造函数的原型对象,每个原型对象都具有 constructor 属性代表了该原型对象对应的构造函数。

 当访问对象的属性或方法时,先在当前实例对象是查找,然后再去原型对象查找,并且原型对象被所有实例共享。

结合构造函数原型的特征,实际开发重往往会将封装的功能函数添加到原型对象中。

    <script>
        //每个构造函数 身上有一个属性 prototype 这个属性是一个对象 原型对象
        //js规定,需要给构造函数添加方法  给构造函数的原型对象添加方法
        function Get(uname, age) {
            this.uname = uname
            this.age = age
        }
        Get.prototype.sing = function () {
            console.log('唱歌')
        }
        const ldh = new Get('刘德华',14)
        const zxy = new Get('张学友',14)
        console.log(ldh)
        console.log(zxy)
        console.log(ldh.sing === zxy.sing)
        
    </script>

 

    <script>
        const arr = new Array(12, 111, 30, 69, 1)
        Array.prototype.max = function () {
            return Math.max(...this)
        }
        console.log(arr.max())

        Array.prototype.sum = function () {
            return this.reduce((temp, item) => temp + item, 0)
        }
        console.log(arr.sum())
    </script>

constructor 属性

每个原型对象里面都有个constructor 属性(constructor 构造函数)

作用:该属性指向该原型对象的构造函数, 简单理解,就是指向我的爸爸,我是有爸爸的孩子

 

    <script>
        function Get(uname, age) {
            this.uname = uname
            this.age = age
        }

        Get.prototype = {
            constructor: Get,
            eat: function () { },
            song: function () { }
        }
        
        console.log(Get.prototype.constructor === Get) (true)
    </script>

对象原型

 __proto__ 是JS非标准属性

[[prototype]]和__proto__意义相同

用来表明当前实例对象指向哪个原型对象prototype

__proto__对象原型里面也有一个 constructor属性,指向创建该实例对象的构造函数

    <script>
       function Get(uname, age) {
            this.uname = uname
            this.age = age
        }
        //给构造函数添加方法
        Get.prototype.sing = function () {
            console.log(this.uname)
        }
        
        const a = new Get('滚滚滚',12)
        console.dir(a)

        console.log(a.__proto__ === Get.prototype)
    </script>

    <script>
        //原型链作用:成员的访问规则
        function Star() {
            this.abcd = function () {
                console.log('你好')
            }
        }
        console.dir(Object)
        const a = new Star()

        //实例化对象__proto__ 指向当前构造函数本身
        console.log(a.__proto__ === Star.prototype)
        console.log(a.__proto__.__proto__ === Object.prototype)
        console.log(a.__proto__.__proto__.__proto__)
    </script>

 原型继承

继承是面向对象编程的另一个特征,通过继承进一步提升代码封装的程度,JavaScript 中大多是借助原型对象实现继承 的特性。

    <script>
        function Father(uname, age) {
            this.uname = uname
            this.age = age
        }
        Father.prototype.sing = function () {
            console.log('哈哈哈')
        }

        function Son(uname, age, gender) {
            Father.call(this, uname, age)
            this.gender = gender
        }
        Son.prototype.eat = function () {
            console.log('订单')
        }

        // Son.prototype = Father.prototype  会互相继承,所以不用
        Son.prototype = new Father()
        //将实例化 父构造函数 的实例化对象 赋值给 子构造函数的原型对象

        const s = new Son('ff', 18, '男')
        
        s.sing()
        // s.eat()
        console.log(s)
        //子构造函数 借用 父构造函数属性
    </script>

原型链

基于原型对象的继承使得不同构造函数的原型对象关联在一起,并且这种关联的关系是一种链状的结构,我们将原型对 象的链状结构关系称为原型链

 原型链-查找规则

① 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性。

② 如果没有就查找它的原型(也就是 __proto__指向的 prototype 原型对象)

③ 如果还没有就查找原型对象的原型(Object的原型对象)

④ 依此类推一直找到 Object 为止(null)

⑤ __proto__对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线

⑥ 可以使用 instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上

    <script>
        function A() {}
        function B() {}
        const c = new A()
        const d = new B()
        console.log(c instanceof A)
        console.log(c instanceof Array)
        console.log(d instanceof A)
        const arr = [1,2,3,4,5]
        console.log(arr instanceof Array)
        //instabceof 运算符 判断 实例化对象 是否 属于 当前的构造函数 属于返回true 否则返回false
    </script>


网站公告

今日签到

点亮在社区的每一天
去签到