美文网首页
12.JavaScript的面向对象2-创建对象方案

12.JavaScript的面向对象2-创建对象方案

作者: 静昕妈妈芦培培 | 来源:发表于2021-10-08 18:59 被阅读0次

    创建对象的方案

    1.字面量方式

    //使用字面量的方式创建三个对象
    var p1 = {
      name: "小名",
      age: 30,
      sex: "male",
      running: function () {
        console.log(this.name + "is running!");
      },
    };
    
    var p2 = {
      name: "小红",
      age: 25,
      sex: "female",
      running: function () {
        console.log(this.name + "is running!");
      },
    };
    
    var p3 = {
      name: "小丽",
      age: 30,
      sex: "female",
      running: function () {
        console.log(this.name + "is running!");
      },
    };
    console.log(p1)
    console.log(p2)
    console.log(p3)
    
    
    image.png
    缺点:
    • 1.做了很多重复工作
    • 2.这样创建的对象没有标出所属的类别
    • 3.每创建一个对象,都需要在堆内存中开辟一块新内存,存储创建的running函数对象,如果创建100个对象,就创建了100running函数,就需要100个这样的堆内存来存储100个这样的running函数对象

    2.工厂模式

    //使用工厂模式创建对象
    function createPerson(name, age, sex) {
      var p = {};
    
      p.name = name;
      p.age = age;
      p.sex = sex;
      p.running = function () {
        console.log(this.name + "is running!");
      };
    
      return p;
    }
    
    var p1 = createPerson("小名", 30, "male");
    var p2 = createPerson("小红", 25, "female");
    var p3 = createPerson("小丽", 30, "female");
    console.log(p1);
    console.log(p2);
    console.log(p3);
    
    image.png
    缺点:
    • 这样创建的对象没有标出所属的类别
    • 每创建一个对象,都需要在堆内存中开辟一块新内存,存储创建的running函数对象,如果创建100个对象,就创建了100running函数,就需要100个这样的堆内存来存储100个这样的running函数对象

    3.构造函数

    3.1.什么是构造函数

    当一个函数被new操作符调用的时候,这个函数就是构造函数

    new操作符调用的作用
    • 1.在内存中创建一个新的对象(空对象)
    • 2.这个对象内部的[[prototype]]属性会被赋值为该构造函数的prototype属性的值
    • 3.构造函数内部的this,会指向创建出来的新对象
    • 4.执行构造函数内部的代码(函数体代码)
    • 5.如果构造函数没有返回非空对象,则返回创建出来的这个新对象
    function person() {}
    
    const p = new person();
    console.log(p);
    
    image.png

    可以看到:通过构造函数创建出来的对象,有标注出来这个对象所属的类的类名person的
    说明,构造函数的调用创建出了一个有标明所属类的类名的对象;那我们使用构造函数去创建对象,不就解决了创建出的带向没有标注出所属类的类名的问题?

    补充:
    function person() {}
    //1.当构造函数创建对象的时候,不需要传参,可以省略小括号
    const p1 = new person;
    console.log(p1)
    
    //2.获取构造函数创建出来的对象所属类的类名
    console.log(p1.__proto__.constructor.name;
    
    //3.为了区分构造函数和普通函数的区别,一般情况下,构造函数的名字会大写
    function Fish() {}
    const fish = new Fish();
    
    
    image.png

    3.2使用构造函数创建对象

    function Person(name, age, sex) {
      this.name = name;
      this.age = age;
      this.sex = sex;
      this.running = function () {
        console.log(this.name + "is running!");
      };
    }
    
    var p1 = new Person("小名", 30, "male");
    var p2 = new Person("小红", 25, "female");
    var p3 = new Person("小丽", 30, "female");
    console.log(p1);
    console.log(p2);
    console.log(p3);
    
    
    image.png
    优点:
    • 通过构造函数创建出的对象,有标注出所属类的类名
    缺点:
    • 每创建一个对象,都需要在堆内存中开辟一块新内存,存储创建的running函数对象
    • 如果创建100个对象,就创建了100running函数,就需要100个这样的堆内存来存储100个这样的running函数对象,浪费内存

    4.构造函数和原型相结合

    4.1对象的原型理解

    每个对象都有一个特殊的内置属性[[prototype]],指向一个对象,这个对象叫做对象的原型
    • 为了和函数的原型做区分,我们一般称之为隐式原型对象

    早起的ECMA是没有规范如何去查看内置属性[[prototype]]这个原型对象的

    • 1.浏览器给对象提供了一个属性__proto__,可以通过此属性去查看对象的原型对象
    • 2.ES5提供了一个方法Object.getPrototype(obj),去查看目标对象的原型对象
    var obj = { name: "why" };
    console.log(obj.__proto__); //[Object: null prototype] {}
    console.log(Object.getPrototypeOf(obj)); //[Object: null prototype] {}
    console.log(obj.__proto__ === Object.getPrototypeOf(obj)); //true
    
    image.png

    可以看到通过__proto__Object.getPrototype(obj)获取到的同一个对象的原型对象,也是同一个对象

    原型的用处

    当我们从一个对象中获取一个属性的时候,它会触发[[get]]操作:

    • 1.在当前对象obj中查找此属性,如果找到,就返回对应的值
    • 2.如果没有找到,会去此对象的原型对象obj.__proto__上查找此属性,如果找到,就返回对应的值
    • 3.如果没有找到,继续去原型对象的原型对象obj.__proto__.__proto__上查找此属性,如果找到,就返回对应的值
    • ....
    • 4.直到找到Object.prototype为止,,此时Object.prototype.__proto__为null,则停止查找,返回undefined
    var obj = { name: "why" };
    obj.__proto__.age = 18;
    console.log(obj.age); //18 虽然obj对象上没有age属性,但是在obj.__proto__对象上找到了age属性,就返回其值18
    

    4.2函数的原型理解

    • 函数作为一个对象而言,它也是有内置属性[[prototype]]这个隐式原型对象的

    • 作为函数,函数还有一个属性prototype,指向一个对象,我们一般称之为显式原型对象

    • 函数作为构造函数,被new调用的时候,会把内部创建的新的对象的[[prototype]]隐式原型对象指向构造函数的prototype显示原型对象

    function foo() {}
    
    console.log(foo.__proto__); //{}
    console.log(foo.prototype); //{}
    
    //函数作为构造函数,被new调用的时候,
    //会把内部创建的新的对象的[[prototype]]隐式原型对象指向构造函数的prototype显示原型对象
    const f = new foo();
    console.log(f.__proto__); //{}
    console.log(foo.prototype); //{}
    console.log(f.__proto__ === foo.prototype); //true
    
    image.png image.png

    4.3函数的原型对象prototype上的constructor

    4.3.1.查看函数原型对象上有哪些属性
    function foo() {}
    
    console.log(foo.prototype); //{}
    console.log(Object.getOwnPropertyDescriptors(foo.prototype));
    
    image.png

    说明,函数原型对象上有一个属性constructor,并且constructor属性是不可枚举的

    function foo() {}
    console.log(foo.prototype.constructor === foo); //true 函数原型对象上的constructor属性指向函数本身
    //获取函数原型对象上的constructor函数的名字
    console.log(foo.prototype.constructor.name); //foo  函数的名字可以通过访问函数的name属性获取
    

    说明:函数原型对象上的constructor属性指向函数本身

    4.4改变函数的prototype的指向

    获取一个对象的属性,其实实在进行[[get]]操作:
    先在当前对象上寻找属性,找到就返回对应的值;找不到就去对象的隐式原型对象[[prototype]]对象上找
    对象的隐式原型对象[[prototype]]指向其构造函数的prototype对象

    foo.prototype.name = "why";
    foo.prototype.age = 18;
    var f = new foo();
    console.log(f.name, f.age); //"why" 18
    

    如果需要在函数的原型对象上添加很多属性,一个一个添加太麻烦,可以通过改变函数prototype指向到一个新对象的方式添加

    //可以直接改变函数的prototype的指向,指向一个新对象
    foo.prototype = {
      name: 'why',
      age: 18
    }
    Object.defineProperty(foo.prototype, "constructor", {
      configurable: true,
      enumerable: false,
      writable: true,
      value: foo
    })
    
    console.log(foo.prototype)
    console.log(Object.getOwnPropertyDescriptors(foo.prototype));
    
    image.png

    4.5使用构造函数和原型相结合创建对象

    function Person(name, age, sex) {
      this.name = name;
      this.age = age;
      this.sex = sex;
    }
    Person.prototype.running = function () {
      console.log(this.name + "is running!");
    };
    
    var p1 = new Person("小名", 30, "male");
    var p2 = new Person("小红", 25, "female");
    var p3 = new Person("小丽", 30, "female");
    console.log(p1);
    console.log(p2);
    console.log(p3);
    
    //当前对象上没有running方法,会去对象的[[prototype]]隐式原型对象上去找,
    //而对象的隐式原型对象正是创建对象的构造函数的prototype显示原型对象
    p1.running()
    p2.running()
    p3.running()
    
    image.png
    优点:
    • 通过构造函数创建出的对象,有标注出所属类的类名
    • 如果创建100个对象,只会创建一个running函数,避免了内存浪费

    非常感谢王红元老师的深入JavaScript高级语法让我学习到很多 JavaScript 的知识

    相关文章

      网友评论

          本文标题:12.JavaScript的面向对象2-创建对象方案

          本文链接:https://www.haomeiwen.com/subject/ujdxoltx.html