当前位置: 首页 > news >正文

【JavaScript】10-深入面向对象

本文介绍JS中深入面向对象的内容。

目录

1. 编程思想

1.1 面向过程

1.2 面向对象oop

2. 构造函数

3. 原型

3.1 原型对象

3.2 constructor属性

3.3 对象原型 __proto__

3.4 原型继承

3.5 原型链


1. 编程思想

1.1 面向过程

分析出解决问题的步骤,用函数将这些步骤一步步实现,使用的时候再一个个依次调用。

1.2 面向对象oop

把事务分解成一个个对象,然后由对象之间分工合作,相当于以功能划分问题,而非步骤。

每一个对象都是功能中心,具有明确分工。

面向对象具有灵活、代码可复用、容易维护和开发的优点,更适合多人合作的大型软件。

面向对象的特点:封装性、继承性、多态性。


2. 构造函数

封装是面向对象思想中比较重要的一部分,JS面向对象可以通过构造函数来实现对函数的封装。

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

    <script>
        // 构造函数  公共的属性和方法封装到 Star 构造函数里
        function Star(uname, age){
            // this 指向实例对象
            this.uname = uname;
            this.age = age;
            this.sing = function(){
                console.log('唱歌');
            }
        }

        // 创建实例对象 默认返回
        const c1 = new Star('LIUDEHUA',55);
        const c2 = new Star('ZHANGXUEYOU',58);

        console.log(c1.sing === c2.sing);  // false
        // 每创建一次实例对象就会开辟一个新的空间
    </script>

每创建一次实例对象就会开辟一个新的空间

所以存在浪费内存的问题

我们希望所有对象使用同一个函数,这样比较节省内存,如何做?

—— 原型


3. 原型

JS规定每一个构造函数都有一个 prototype 属性,指向另一个对象,,所以我们也称为原型对象。

        console.log(Star.prototype);  
        // {constructor: ƒ}
            // constructor: 
            // ƒ Star(uname, age)
            // [[Prototype]]: Object

这个对象可以挂载函数,对象实例化不会多次创建原型上函数,节约内存。

我们可以把那些不变的方法,直接定义在prototype对象上,这样所有对象的实例就可以共享这些方法。且构造函数和原型对象中的this都指向实例化的对象。

    <script>
        // 构造函数  公共的属性和方法封装到 Star 构造函数里
        function Star(uname, age){
            // this 指向实例对象
            this.uname = uname;
            this.age = age;
        }
        // Star的原型对象的sing方法
        Star.prototype.sing = function(){
            console.log('唱歌');
        }
        // 创建实例对象 默认返回
        const c1 = new Star('LIUDEHUA',55);
        const c2 = new Star('ZHANGXUEYOU',58);
        c1.sing();
        c2.sing();
        console.log(c1.sing === c2.sing);  // true
        // 不管实例化多少对象,都只调用同一个方法
    </script>

原型对象就类似一个固定的模型,任何对象都可以用其编写公共的方法并调用。不浪费空间。

注意

公共的属性写在构造方法中,用this实例化

公共的方法写在原型对象中,用prototype


3.1 原型对象

原型对象和构造函数里的this的指向

都是同一个对象

    <script>
        let a;
        let b;
        function Star(uname){
            a = this;
            console.log(this);
            this.uname = uname;   // this指向创建的实例
        }
        Star.prototype.sing = function(){
            b = this;
            console.log('唱歌');
        }
        // 实例对象 c1
        const c1 = new Star('liudehua');
        // 构造函数里的this指向的就是实例对象c1
        console.log(a === c1);  //true
        c1.sing();  // c1调用了sing方法
        // 原型对象里的this指向的还是实例对象c1
        console.log(b === c1);  //true
    </script>

练习

给数组拓展方法

需求:

1. 给数组拓展求最大值方法和求和方法

比如 const arr = [1,2,3]

arr.reverse()  后  [3,2,1]

拓展完毕后:
arr.sum()  后 结果为6

分析:

给数组的原型挂上方法,达到拓展方法的目的

    <script>
        // 自己定义数组拓展方法 求和 最大值
        // 1.定义的方法 任何数组的实例对象都能使用
        // 2.自定义的方法写到 数组.prototype上
        // Array.prototype

        const arr = [1,2,3];
        // 1.最大值
        Array.prototype.max = function(){
            // return Math.max(...arr);  // 展开运算符
            return Math.max(...this);
            // 原型函数里的this指向 实例对象 arr
        }

        // 2.最小值
        Array.prototype.min = function(){
            return Math.min(...this);
            // 原型函数里的this指向 实例对象 arr
        }
        console.log(arr.max());  // arr调用的max() this指向的arr
        console.log([2,4,6].max());
        console.log([2,9].min());

        // 3.求和
        Array.prototype.sum = function(){
            // reduce 方法
            return this.reduce((prev,item) => prev + item , 0);  // 累加,起始值
        }
        console.log([1,2,3].sum());
    </script>

3.2 constructor属性

每个原型对象都有constructor属性

该属性指向该原型对象的构造函数

    <script>
        // constructor 
        function Star(){

        }
        const c1 = new Star('LIUDEHUA');
        // console.log(Star.prototype);  
        // 得到构造函数的原型对象  可以看到默认都有constructor属性

        console.log(Star.prototype.constructor === Star);  // true
    </script>

但是如果对prototype这个原型对象进行重写,可以覆盖原来的constructor

    <script>
        // constructor 
        function Star(){

        }

        console.log(Star.prototype);
        Star.prototype = {
            sing: function(){
                console.log('唱歌');
            },
            dance: function(){
                console.log('跳舞')
            },
        }
        // 这和Star.prototype.sing还不一样,上面是全覆盖,这个只是追加而已
        console.log(Star.prototype);

    </script>

如图:

也就是原型对象的constructor属性被覆盖了 无法指回原来的。

那该怎么做呢?

        Star.prototype = {
            constructor:Star,  // 自己再加回去
            sing: function(){
                console.log('唱歌');
            },
            dance: function(){
                console.log('跳舞')
            },
        }


3.3 对象原型 __proto__

构造函数可以创建实例对象 new XXX()

构造函数有一个原型对象 prototype,一些共享的属性和方法可以放在这个原型对象上

但是为什么实例对象可以访问原型对象里的属性和方法?

是因为有对象原型

__proto__ (两个下划线)

每个对象都会有一个属性 __proto__指向构造函数的 prototype 原型对象

注意:

__proto__ 是JS的非标准属性 只读无法修改

[[prototype]]和__proto__意义相同

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

即 对象原型 指向 原型对象

    <script>
        function Star(){

        }
        const ldh = new Star();
        console.log(ldh.__proto__);  
        // 对象原型 __proto__ 指向 该构造函数的原型对象
        console.log(ldh.__proto__ === Star.prototype);
    </script>

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

        // 对象原型也有constructor指向构造函数 Star
        console.log(ldh.__proto__.constructor === Star);  // true


3.4 原型继承

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

    <script>
        // 女人 构造函数
        function Woman(){
            this.eyes = 2;
            this.head = 1;
        }
        const red = new Woman();
        console.log(red);
        
        // 男人 构造函数
        function Man(){
            this.eyes = 2;
            this.head = 1;
        }
        const pink = new Man();
        console.log(pink);

        // 男人和女人都有人的特性
        // 继续抽取封装
    </script>

开始抽取封装

    <script>
        // 人  公共的部分放到原型上可以使用  
        // 下面可以通过原型继承Person
        const Person = {
            eyes : 2,
            head : 1
        } 
        // 女人 构造函数 继承Person
        function Woman(){
            
        }
        Woman.prototype = Person;
        // 但是不要忘了指回原来的构造函数
        Woman.prototype.constructor = Woman;
        const red = new Woman();
        console.log(red);
        console.log(Woman.prototype);  // 属性和constructor
        
        // 男人 构造函数 继承Person
        function Man(){

        }
        Man.prototype = Person;
        // 但是不要忘了指回原来的构造函数
        Man.prototype.constructor = Man;
        const pink = new Man();
        console.log(pink);
        console.log(Man.prototype);

    </script>

现在给女人部分添加方法

        // 女人 构造函数 继承Person
        function Woman(){
            
        }
        Woman.prototype = Person;
        // 但是不要忘了指回原来的构造函数
        Woman.prototype.constructor = Woman;

        // 给女人添加一个方法 生育
        Woman.prototype.baby = function(){
            console.log('生孩子');
        }
        
        const red = new Woman();
        console.log(red);
        console.log(Woman.prototype);  // 属性和constructor

如图可以看到男人也有了此方法

男人和女人都同时使用了同一个对象,根据引用类型的特点,他们指向同一个对象,修改一个就都会影响,所以可以选择让男人和女人继承不同的person对象即可解决。

        const Person1 = {
            eyes : 2,
            head : 1
        } 
        const Person2 = {
            eyes : 2,
            head : 1
        } 

后面的继承也修改对象的person即可

更好的是 直接用构造函数 他们new出来的对象是不一样

    <script>
        // 通过构造函数 new 出来的对象 结构一样,但是对象不一样
        function Person(){
            this.eyes = 2;
            this.head = 1;
        }
        console.log(new Person);
        // new
        // 女人 构造函数 继承Person
        function Woman(){
            
        }
        Woman.prototype = new Person();  // 创建一个对象
        // 但是不要忘了指回原来的构造函数
        Woman.prototype.constructor = Woman;

        // 给女人添加一个方法 生育
        Woman.prototype.baby = function(){
            console.log('生孩子');
        }

        const red = new Woman();
        console.log(red);
        console.log(Woman.prototype);  // 属性和constructor
        
        // 男人 构造函数 继承Person
        function Man(){

        }
        Man.prototype = new Person();  // 创建一个对象
        // 但是不要忘了指回原来的构造函数
        Man.prototype.constructor = Man;
        const pink = new Man();
        console.log(pink);
        console.log(Man.prototype);
    </script>

现在给 女人和男人 各自添加方法互不影响


3.5 原型链

    <script>
        function Person(){

        }
        const ldh = new Person();  // 创建实例对象
        console.log(ldh.__proto__ === Person.prototype);  //true
        console.log(Person.prototype); // 里面还嵌套了一层 prototype
        console.log(Person.prototype.__proto__ === Object.prototype);  // true

        // Object.prototype 是所有原型对象的对象原型  
        // 原型对象也是一个对象 该对象的原型 是 Object的原型对象
        // 还是满足 对象原型 指向 原型对象

        console.log(Object.prototype.__proto__);  // null

    </script>

如图可以看到一个原型链

ldh.__proto__   ——  Person.prototype.__proto  ——  Object.prototype.__proto  ——  null

只要是对象 就有 __proto__ 指向原型对象

只要是原型对象就有 constructor 指回构造函数

原型链 — 查找规则:

instanceof 运算符:

        console.log(ldh instanceof Person);  // ldh是Person创建出来的 true
        console.log(ldh instanceof Object); // true
        console.log(ldh instanceof Array);  // false
        console.log(Array instanceof Object);  // true

本文介绍JS中深入面向对象的内容。

相关文章:

  • [设计模式]1_设计模式概览
  • NRBO-Transformer牛顿-拉夫逊算法优化编码器多变量时间序列预测(Matlab实现)
  • Maximize Rating
  • Git使用和原理(3)
  • 从0到1构建AI深度学习视频分析系统--基于YOLO 目标检测的动作序列检查系统:(2)消息队列与消息中间件
  • 2025-03-15 学习记录--C/C++-PTA 习题3-3 出租车计价
  • Thinkphp的belongsToMany(多对多) 和 hasManyThrough(远程一对多)的区别是什么?
  • 【蓝桥杯】省赛:连连看(暴力 非AC)
  • 浅析Redis分布式锁的实现方法
  • 使用OpenAI Python库探索新一代AI接口:从基础到智能体开发
  • 文本组件+Image组件+图集
  • 数学复习(8)连续性
  • STM32F407ZGT6--工程模版
  • 10.2linux内核定时器实验(详细编程)_csdn
  • Windows环境下安装部署dzzoffice+onlyoffice的私有网盘和在线协同系统
  • 【USTC 计算机网络】第一章:计算机网络概述 - Internet、网络边缘、网络核心、接入网与物理媒体
  • 基于变分推理与 Best‑of‑N 策略的元 Prompt 自动生成与优化框架
  • 学习路之TP6 --重写vendor目录下的文件(服务覆盖command---优点:命令前后一致)
  • 在线 SQL 转 SQLAlchemy:一键生成 Python 数据模型
  • 谷歌Gemini 3大模型发布,AI领域再掀波澜!(2)
  • 上海这个咖啡文化节首次“走出去”,率本土品牌亮相英国伦敦
  • 北京韩美林艺术馆党支部书记郭莹病逝,终年40岁
  • 新华时评:博物馆正以可亲可近替代“高冷范儿”
  • 贵州省委军民融合发展委员会办公室副主任李刚接受审查调查
  • 江西3人拟提名为县(市、区)长候选人
  • 巴基斯坦与印度停火延长至18日