JavaScript入门【3】面向对象

1.对象:

1.概述:

复制代码
   在js中除了5中基本类型之外,剩下得都是对象Object类型(引用类型),他们的顶级父类是Object;

2.形式:

复制代码
   在js中,对象类型的格式为key-value形式,key表示属性,value表示属性的值

3.创建对象的方式:

方式1:通过new关键字创建(不常用)
plain 复制代码
    let person = new Object();
       // 添加属性  与 值
       person.name="张三";
       person.age = 22;
       console.log(person)      
方式2:通过{}类似于JSON字符串的形式创建:
plain 复制代码
     let person = {
        name: '张三',
        age: 20
    }
    console.log(person);   

4.对象的相关操作:

1.添加属性:
plain 复制代码
    let person = new Object();
       // 添加属性  与 值
       person.name="张三";
       person.age = 22;
       console.log(person)      
plain 复制代码
let person = {
        name: '张三',
        age: 20
    }
    console.log(person);
    // 添加属性
    person.sex = '男';
   
2.获取属性值:

对于new方式创建的对象,获取属性值的方式如下:

plain 复制代码
    let person = new Object();
       // 添加属性  与 值
       person.name="张三";
       person.age = 22;
       
       console.log(person.name)       

对于{}方式创建的对象,获取属性值得方式如下(上面得方式也适用):

plain 复制代码
let person = {
        name: '张三',
        age: 20
    }
    console.log(person['age']);
    
   
3.删除属性:通过delete关键字实现(两种创建方式都适用)
plain 复制代码
let person = {
        name: '张三',
        age: 20,
        sex:'男'
    }
   delete person.sex;
    console.log(person);
    
    
4.遍历对象属性:for-in循环
plain 复制代码
let person = {
        name: '张三',
        age: 20,
        sex:'男'
    }
    for (let personKey in person) {
        console.log(personKey,person[personKey]);
    }
    
      

2.函数:不依赖于实例对象

1.概述:

复制代码
是由一连串的子程序(语句的集合)组成的,可以被 外部调用,向函数内部传入参数之后,函数可以返回的一定的值得代码集合;

2.函数对象的创建:

方式1;通过new关键字:(不常用)
plain 复制代码
let  函数名=new Function("执行的语句");
plain 复制代码
let funA = new Function("console.log('函数对象执行了')");
//调用函数:
funA();
方式2:声明式创建函数
plain 复制代码
function 函数名(形参1,形参2.....) {
        语句
    }
plain 复制代码
function sum(num1, num2, num3) {
        console.log("执行了sum函数:收到了参数:", num1, num2, num3)
        //可以使用return 返回结果
        return num1 + num2;
    }
    //调用函数
    let ret=sum(1,2);
    console.log(ret);

注意事项:

复制代码
    调用有参函数时,传入的参数不受函数定义的函数列表影响,可以多传,少穿,或者不传;
复制代码
    创建函数时,不涉及返回值(类似于Java中的构造方法),但函数体内可以return执行结果;

3.函数的类型:

1.常规函数:上述函数即为常规函数
plain 复制代码
function 函数名(形参1,形参2.....) {
        语句
    }   
2.匿名函数:没有函数名,而是由一个变量进行接收
plain 复制代码
 let 变量名(函数名)=function(形参1,形参2.....) {
         执行语句
    }
3.嵌套函数:即函数体内包含一个子函数
plain 复制代码
 function 父函数名(形参1,形参2.....) {
        function 子函数名(形参1,形参2.....) {
            语句
       }   
    }   

注意:直接调用父函数时,无法执行子函数

plain 复制代码
function fu() {
        function zi() {
            console.log("我是子函数");
        }
        父函数的其他执行语句
    }   
 //调用父函数
  fu();

如果需要执行子函数,则需要在父函数中手动调用子函数

plain 复制代码
function fu() {
        function zi() {
            console.log("我是子函数");
        }
        zi();
       // 父函数的其他执行语句
    }   
 //调用父函数
  fu();
4.立即执行函数:可理解为函数一创建出来就被调用执行
plain 复制代码
(function (形参1,.....) {
        执行语句
    })(实参.........)
plain 复制代码
(function (msg) {
        console.log("我是一个匿名函数",msg)
    })('我是一段消息');

3.方法:需依赖于示例对象

1.方法的定义:

复制代码
      需要先创建对象,然后依赖对象在创建方法;
plain 复制代码
 let person = {
           name:'张三',
           age:23,
           //定义方法
           sayHello:function () {
               console.log(this.name+",Hello")
           }
       }
       console.log(person)
       //调用方法
       person.sayHello();     

2.this关键字:

1.this出现在函数中:被直接调用,则this表示window对象
plain 复制代码
 <script>
      function fun() {
          console.log(this.constructor+ ",Hello")
      }
      fun();
  </script>      

通过输出的构造方法名,可以看出此时的this表示window对象

2.this出现在方法中:this表示当前对象(谁调用,this就指代谁)
plain 复制代码
  function fun() {
        console.log(this.name + ",Hello")
    }

    let person = {
        name: '张三',
        age: 23,
        sayHello: fun
    }
    let person2 = {
        name: '李四',
        age: 23,
        sayHello: fun
    }
    person.sayHello(); //对象调用方法
    person2.sayHello();

通过测试结果可以看出,在方法中的this被那个对象调用,this就指代那个对象

4.创建对象的几种方式:

1.直接创建:

存在问题:
复制代码
       如果需要创建的对象过多,直接创建无法实现代码的高复用性,代码冗余严重;

2.通过工厂模式,封装创建对象的方法:

实现:
plain 复制代码
 function createPerson(name, age) {
        let obj = new Object();
        obj.name = name;
        obj.age = age;

        obj.sayHello = function () {
            console.log(this.name)
        }
        return obj;
    }
    let person1 = createPerson('张三', 22); //Object 类型
    let person2 = createPerson('李四', 22); //Object 类型
    console.log(person1,person2)
       
存在问题:
复制代码
通过下面测试结果可以看出,使用工厂模式创建的对象没有独立的类型,全部都是Object;
plain 复制代码
 function createPerson(name, age) {
        let obj = new Object();
        obj.name = name;
        obj.age = age;

        obj.sayHello = function () {
            console.log(this.name)
        }
        return obj;
    }
    let person1 = createPerson('张三', 22); //Object 类型
    let person2 = createPerson('李四', 22); //Object 类型
    console.log(typeof person1, typeof person2)
       

3.通过构造函数创建对象:

构造函数是什么:
复制代码
     构造函数就是一个普通的函数,创建方式和普通函数没有区别;
复制代码
     不同的是 构造函数一般首字母大写,调用时不同 需要使用new关键字;
构造函数的执行流程:
复制代码
   1.调用构造函数,会立刻创建一个新的对象
复制代码
   2.将新建的对象设置为函数中的this,在构造函数中可以使用this开引用新建的对象
复制代码
   3.逐行执行函数中的代码
复制代码
   4.将新建的对象返回
实现通过构造函数创建对象:
plain 复制代码
  function Person(name, age) {
        this.name = name;
        this.age = age;
        this.sayName = function () {
            console.log(this.name)
        }
    }

    let person1 = new Person('张三', 23);
    let person2 = new Person('李四', 23);
    console.log(person1, person2)
说明:

通过此种方式创建的对象,都有独立的类型,不再是object,而是与构造函数有关

plain 复制代码
  function Person(name, age) {
        this.name = name;
        this.age = age;
        this.sayName = function () {
            console.log(this.name)
        }
    }

    let person1 = new Person('张三', 23);
    let person2 = new Person('李四', 23);
    console.log(person1, person2) 
    function Person2(name, age) {
        this.name = name;
        this.age = age;
        this.sayName = function () {
            console.log(this.name)
        }
    }
    let person3 = new Person2('李四', 23); 
    //  判断 person3 是 Person2 / Person 创建的
    console.log(person3 instanceof Person)
    console.log(person2 instanceof Person)
    console.log(person3 instanceof Person2)

由于person3是通过Person2构造函数创建的,所以 console.log(person3 instanceof Person)输出为false,console.log(person3 instanceof Person2)输出为true;

5.原型:

1.概述:

复制代码
     我们创建的每一个函数,解析器都会向函数中添加一个属性prototype,这个属性对应着一个对象,这个对象就是我们所谓的原型对象,即显式原型,
复制代码
     原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中。
复制代码
     普通函数调用prototype没有任何作用,当函数以构造函数的形式调用时,它所创建的对象中都会有一个隐含的属性,指向该构造函数的原型对象,我们可以通过__proto__(隐式原型)来访问该属性。

2.案例解析:

plain 复制代码
  //创建构造函数
  function Person(name, age) {
        this.name = name;
        this.age = age;
        this.sayName = function () {
            console.log(this.name)
        }
    }

    //将属性 或者 函数 存在原型中  共享的
    Person.prototype.xxx = "我是测试数据";
    Person.prototype.showInfo = function () {
        console.log(this.name, "我是原型中的方法")
    } 
    let person1 = new Person('张三', 23);
    let person2 = new Person('李四', 23);
    console.log(person1, person2);
     
plain 复制代码
    //创建构造函数
  function Person(name, age) {
        this.name = name;
        this.age = age;
        this.sayName = function () {
            console.log(this.name)
        }
    }

    //将属性 或者 函数 存在原型中  共享的
    Person.prototype.xxx = "我是测试数据";
    Person.prototype.showInfo = function () {
        console.log(this.name, "我是原型中的方法")
    } 
    let person1 = new Person('张三', 23);
    let person2 = new Person('李四', 23);
   //person1 访问xxx 先从person1对象自身寻找xxx属性
    //没有的 就去原型中找
    // 在没有  就通过父类找
    console.log(person1.xxx, person2.xxx)
    person2.showInfo();
      
复制代码
          ![](https://cdn.nlark.com/yuque/0/2024/png/45532635/1731664800063-21502bd1-ec70-43a7-8239-67f169e58805.png)

因为在Person类的原型对象中添加了属性xxx和showinfo方法,而person1和person2作为它的实例对象,在访问时,由于从自身无法获取到,就向上在共享的原型对象中访问到了属性xxx和方法showinfo;

plain 复制代码
  //创建构造函数
  function Person(name, age) {
        this.name = name;
        this.age = age;
        this.sayName = function () {
            console.log(this.name)
        }
    }

    //将属性 或者 函数 存在原型中  共享的
    Person.prototype.xxx = "我是测试数据";
    Person.prototype.showInfo = function () {
        console.log(this.name, "我是原型中的方法")
    } 
    let person1 = new Person('张三', 23);
    let person2 = new Person('李四', 23);
     //使用 in 检查对象中是否 含有某个属性  有  true 没有 false
     console.log('name' in person1);
     console.log('xxx' in person1);

    console.log(person1.hasOwnProperty('name'))
    //true  xxx不是person1 自身的数据
    console.log(person1.hasOwnProperty('xxx'))
    console.log(person1.__proto__.hasOwnProperty('xxx'))
       

由于xxx属性是原型对象中的属性,而不是person1自身的属性,所以测试结果为false;

6.继承:

1.方式:

复制代码
  ** * 1.原型链继承**
复制代码
  **     * 2.构造方法继承**
复制代码
  **     * 3.组合继承**
复制代码
  **     * 4.原型式继承**
复制代码
  **     * 5.寄生继承**
复制代码
  **     * 6.寄生组合继承**

2.原型链继承:

实现:

plain 复制代码
 function SupperType() {
        this.supProp = "我是父类型中的属性"
    }

    //给父类的原型添加方法
    SupperType.prototype.showSupperProp = function () {
        console.log(this.supProp);
    }

    //创建子类原型
    function SubType() {
        this.subType = "我是子类的属性"
    }

    //继承  让子类的原型属性 指向 父类类型
    SubType.prototype = new SupperType();
    //将子类的原型的构造方法属性设置为子类自己
    SubType.prototype.constructor = SubType;
    //子类的原型对象添加方法
    SubType.prototype.showSubProp = function () {
        console.log(this.subType)
    }
    let subType = new SubType();
    //调用父类的原型中的方法
    subType.showSupperProp();
    //调用子类自己的原型中的方法
    subType.showSubProp();
    //获取父类中的属性
    console.log(subType.supProp)
    console.log(subType)
   

说明:

复制代码
 上述代码中是通过将子类的原型对象指向父类对象来实现的,在子类访问父类属性或方法时,先在自身找,如果找不到,再在子类的原型对象中找,要是还找不到,就在父类对象中找,父类对象属性中也没有,就在父类的原型对象中找,就这样以引用链的形式查找,进而实现了继承;

存在问题:

复制代码
     1.不能为父类传参;
复制代码
     2.原型链继承多个实例的引用类型属性,且由于指向是相同的,一个实例修改了原型属性,另一个实例的原型属性也会被影响;

3.构造函数继承:

实现:

plain 复制代码
  //1.定义父类的构造函数
    function SupperType(name) {
        this.name = name;
        this.showSupperName = function (){
            console.log(this.name,"这是方法")
        }
    }
    SupperType.prototype.xxx= "父类原型属性";
    //2.创建子类构造函数
    function SubType(name,age) {
        //在子类中 调用call 继承父类中的属性与方法
        SupperType.call(this,name);
        this.age = age;
    }
    SubType.prototype.showInfo = function () {
        console.log(this.name,this.age)
    }

    let subType = new SubType('张三',20);
    subType.showSupperName();
    //子类原型中的方法
    subType.showInfo();
    //通过子类实例 调用父类原型中的属性
    console.log(subType.xxx);

    console.log(subType)

说明:

复制代码
通过此方式实现继承与原型链的方式不同,此方式更像是把父类的属性和方法复制到子类中,虽然最后看似在调用父类的属性和方法,但其实是调用本类的属性和方法,因此通过此方式并没有建立完全的继承关系,所以subType的父类是Object,而非SupperType,因此子类SubType是无法访问到父类SupperType的原型对象中的属性和方法的;

存在问题:

复制代码
 通过构造函数实现继承关系,解决了原型链继承的问题.但又出现了下面的新问题:
复制代码
    无法访问父类原型对象中的方法或属性;

4.组合继承:

实现:

plain 复制代码
<script>
     //1.定义父类的构造函数
     function SupperType(name) {
         this.name = name;
         this.showSupperName = function (){
             console.log(this.name,"这是父类方法")
         }
     }
     SupperType.prototype.xxx= "父类原型属性";
     //2.创建子类构造函数
     function SubType(name,age) {
         //在子类中 调用call 继承父类中的属性与方法
         SupperType.call(this,name);
         this.age = age;
     }
     SubType.prototype=Object.create(new SupperType());
      SubType.prototype.constructor=SubType;
     SubType.prototype.showinfo=function(){
         console.log(this.name,this.age,"子类方法")
     }
      let sub=new SubType("张三",22);
       sub.showSupperName();
       sub.showinfo();
     console.log(sub.xxx);
     console.log(sub);
</script>   

说明:

此方式结合了引用链继承和构造方法继承的特点,解决了它们自身存在的问题

存在问题:

虽然组合继承解决了引用链继承和构造方法继承所存在的问题,但有出现了新的问题;

复制代码
  父类中的实例属性和方法在子类实例中又在子类原型中,内存开销变大;
相关推荐
JuneXcy1 小时前
leetcode933最近的请求次数
开发语言·javascript·ecmascript
Fly-ping8 小时前
【前端】JavaScript 的事件循环 (Event Loop)
开发语言·前端·javascript
在逃的吗喽9 小时前
黑马头条项目详解
前端·javascript·ajax
JHCan33310 小时前
一个没有手动加分号引发的bug
前端·javascript·bug
天涯学馆11 小时前
为什么越来越多开发者偷偷用上了 Svelte?
前端·javascript·svelte
拾光拾趣录11 小时前
为什么浏览器那条“假进度”救不了我们?
前端·javascript·浏览器
香菜狗11 小时前
vue3响应式数据(ref,reactive)详解
前端·javascript·vue.js
油丶酸萝卜别吃12 小时前
SSE与Websocket有什么区别?
前端·javascript·网络·网络协议
276695829212 小时前
拼多多小程序 anti_content 分析
java·javascript·python·node·拼多多·anti-content·anti_content
The_era_achievs_hero12 小时前
uni-appDay02
javascript·vue.js·微信小程序·uni-app