构造函数

198 阅读1分钟

1.工厂函数

  • 工厂函数: 用于批量创建对象的函数
 <script>
        /* 学习总结
        1.工厂函数(了解概念) :用于创建对象的函数
        2.构造函数 :
        3.new关键字工作原理(重点) :
        */


        /* 需求 :  需要创建很多个对象 (姓名、年龄、性别) */
        //封装一个函数解决代码冗余
        let p1 = {
            name: '万叶',
            age: 18,
        }
        let p2 = {
            name: '公子',
            age: 28,
        }
        let p3 = {
            name: '雷神',
            age: 20,
        }

        function fun(name, age) {
            // 创造空对象
            let p = {}
            p.name = name
            p.age = age
            return p
        }
        console.log(fun('万叶', 18)); // name: "万叶" age: 18
        console.log(fun('公子', 28)); // name: "公子" age: 28
        console.log(fun('雷神', 20)); // name: "雷神" age: 20
    </script>

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

2.构造函数

 <script>
       // 构造函数

        function fun(name, age) {
            // 1.创建空对象
            // 2.this只想这个对象
            // 3.对象赋值
            // 4.默认返回这个值
            this.name = name
            this.age = age

        }
        let p1 = new fun('公子', 18)

        console.log(p1); // name: "公子" age: 18 
        function fu() {
            //1.创建空对象
            //2.自定返回对象
        }
        let f1 = fu() //普通函数
        let f2 = new fu() //构造函数
        console.log(f1, f2);

        //new关键字注意点
        // 1. 构造函数首字母一般大写,为了提醒调用者记得加new
        // 2.如果手动在构造函数里面加 return
          // 2.1 如果是简单值数据类型,无效,还是返回new创建的对象
          // 2.2 如果是引用数据类型,无效会覆盖new创建的对象
    </script>

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

总结:

  1. 构造函数体现了面向对象的封装特性
  2. 构造函数实例创建的对象彼此独立、互不影响
  3. 命名空间式的封装无法保证数据的独立性

3.原型对象

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

<script>
        /* 
        //1.构造函数 :  调用一个函数使用了new关键字
        // 构造函数中的方法 弊端 : 浪费内存资源

        /*new关键字工作原理 
            //(1)创建空对象  {}
            //(2)this指向这个对象 this = {}
            //(3)执行赋值代码
            //(4)返回这个对象  return this
        */
        // function Person(name,age){
        //     //(1)创建空对象  {}
        //     //(2)this指向这个对象 this = {}
        //     //(3)执行赋值代码
        //     //(4)返回这个对象  return this
        //     this.name = name;
        //     this.age = age;
        //     this.sayHi = function(){
        //         console.log('万叶');
        //     }
        // };

        // let p1 =  new Person('公子',28);
        // console.log(p1);
        // p1.sayHi();

        // let p2 = new Person('钟离',18);
        // p2.sayHi();
        // //每一个对象的方法都不是同一个
        // console.log(p1.sayHi == p2.sayHi);//false


        //2. 使用全局函数 : 解决内存资源浪费问题

        //弊端 : 全局变量污染的问题

        // function fn(){
        //     console.log('雷神');
        // };

        // function eat(){
        //     console.log('八重'); 
        // };

        // function Person(name,age){
        //     //(1)创建空对象  {}
        //     //(2)this指向这个对象 this = {}
        //     //(3)执行赋值代码
        //     //(4)返回这个对象  return this
        //     this.name = name;
        //     this.age = age;
        //     this.sayHi = fn;
        //     this.eat = eat;
        // };

        // let p1 =  new Person('雷神',28);
        // console.log(p1);
        // p1.sayHi();

        // let p2 = new Person('八重',18);
        // p2.sayHi();
     
        // console.log(p1.sayHi == p2.sayHi);//true



        //3.使用对象 解决 : (1)解决内存资源浪费  (2)全局变量污染
        //弊端 : 对象自身还是全局的,造成新的全局变量污染

        // let obj = {
        //     fn:function(){
        //         console.log('月下风起');
        //     },
        //     eat:function(){
        //         console.log('稍纵即逝'); 
        //     }
        // }
        

        // function Person(name,age){
        //     //(1)创建空对象  {}
        //     //(2)this指向这个对象 this = {}
        //     //(3)执行赋值代码
        //     //(4)返回这个对象  return this
        //     this.name = name;
        //     this.age = age;
        //     this.sayHi = obj.fn;
        //     this.eat = obj.eat;
        // };

        // let p1 =  new Person('雷神',28);
        // console.log(p1);
        // p1.sayHi();

        // let p2 = new Person('八重',18);
        // p2.sayHi();
     
        // console.log(p1.sayHi == p2.sayHi);//true


        /*     4.使用原型 :             */
        
        /* 4.1 原型 : 每一个构造函数在声明的时候,系统会自动的创建一个与之对应的对象,
        称之为原型对象
        */
        function Person(name,age){
            this.name = name;
            this.age = age;
        };

        /*4.2 如何获取原型对象 
            每一个函数都有一个 prototype 属性,指向这个原型对象
        */
       console.log(Person.prototype);

       /* 
       4.3 既然原型是一个对象 : 用于存储数据
       */
      Person.prototype.sayHi = function(){
          console.log('万叶');
          
      };

      /* 
      4.4 谁可以访问 原型中的成员(属性+方法)
        a. 构造函数自身 : 构造函数名.prototype
        b. 这个构造函数所创建(实例化)的每一个对象
      */
     // 实例化对象
     let p1 = new Person('雷神',18);
     p1.sayHi();

     //实例化对象
     let p2 = new Person('八重',20);
     p2.sayHi();

     console.log(p1.sayHi === p2.sayHi);//true
     
    </script>

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

4.原型对象的属性

1. __ proto __ 属性介绍


![snipaste20220428_193439.jpg](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/10bfb0859c4c4a29bde0c66d9d0b1a68~tplv-k3u1fbpfcp-watermark.image?)
   <script>
      
        __proto__属性: 属于实例对象,指向原型对象
            * 作用: 可以让实例对象访问原型中的成员
      

      //1.构造函数
      function Person (name, age) {
        this.name = name
        this.age = age
      }

      //2.原型对象
      Person.prototype.eat = function () {
        console.log('我中午真的吃了红烧武昌鱼')
      }
      Person.prototype.country = '中国人'

      //3.实例对象
      let p1 = new Person('班长', 28)
      console.log(p1)

      /* 
      __proto__ : 属于实例对象,指向原型对象
      */
     console.log( Person.prototype === p1.__proto__ )//true

     //实例对象为什么可以直接访问原型中的成员,其实都是通过__proto__来访问的
     /* 
     1.虽然实例对象可以直接访问原型的原理是 __proto__,但是开发中不推荐使用
     2.原因: __proto__不是ECMA标准语法,有一些浏览器不支持。
     3.结论: 学习阶段,学习原型可以用__proto__. 实际开发中,建议省略__proto__
     */
     p1.eat()//p1.__proto__.eat()
     
      
    </script>

注意: 往原型添加属性方法,最好使用构造函数来访问构造函数名.prototype

2.constructor属性介绍

<script>
       
       * 作用: 可以让实例对象 知道自己被哪一个构造函数创建的
       

      //1.构造函数
      function Person (name, age) {
        this.name = name
        this.age = age
      }

      //2.原型对象
      Person.prototype.type = '哺乳动物'
      Person.prototype.eat = function () {
        console.log('吃东西')
      }

      //3.实例对象
      let p1 = new Person('布丁', 3)
      p1.eat()
      console.log(p1.type)

      /* 
      constructor : 属于原型对象,指向构造函数
        作用:可以让实例对象知道自己被那个构造函数创建的
      */
      console.log(Person.prototype.constructor)//Person
      console.log(Person.prototype.constructor === Person )//true
      console.log( p1.constructor )//Person p1.__proto__.constructor
      


    </script>

静态成员和实例对象

<script>
        /* 
        构造函数也可以有自己的属性: 例如prototype

        静态成员 : 属于函数对象的成员
        实例成员: 属于实例化对象的成员
         */

        //构造函数
         function Person(name,age){
            this.name = name;
            this.age = age;
        };

        Person.aaa = '啊啊啊';
        console.log(Person.aaa);//静态成员
        

        //实例化对象
        let p1 = new Person('张三',20);

        console.log(p1.name);//实例成员
        console.log(p1.age);//实例成员
        
    </script>