JavaScript高级语法-对象的创建方式及优缺点(基础)

199 阅读8分钟

@写在前面 :对于初学者友好,变量声明没有采取ES6标准,请自行食用

以下为个人学习中结合老师所讲的个人理解,如果帮助到喜欢学习的你我将倍感荣幸

对象的创建方式及优缺点

以下使用举例的方法描述该知识点


1.直接创建法

/*
    创建一个人类对象,包含:
                         属性:其姓名、性别、年龄、身高、体重 
                         方法:控制台输出个人信息
*/
var person={
    name:"张三",
    gender:"男",
    age:28,
    height:180,
    weight:74,
    showInfo:function(){
        console.log(this.name,this.age,this.height);
    } 
};
//这里调用showInfo输出该对象的姓名,年龄以及身高
person.showInfo();

方法一

优点: 简单粗暴,快捷创建。适用于简单项目中即时创建并使用 使用完毕后可赋值为null以释放内存
缺点: 代码复用率低,例如创建多个不一样的人,就需要创建多个对象,造成代码资源存储空间浪费,不宜与中大型项目的开发,这样的方式可以临时创建并使用单个对象。
注意: 多个对象用此方法创建会拖慢加载进度,不宜与项目优化


2.工厂模式

/*创建三个不同人种,包含:
                        属性:肤色、姓名、年龄、身高 
                        方法:显示个人信息
  封装一个函数,将属性作为参数传入 并新建一个空对象 添加属性及方法为传入参数
  每调用一次该方法,就创建一个人种对象并输出(返回该对象)结果
*/
function createPerson(skin,name,age,height){
    var obj={};
    obj.skin=skin;
    obj.name=name;
    obj.age=age;
    obj.height=height;
    return obj;
}
//这里调用该函数,创建3个人种对象
var p1=createPerson("绿种人","亚当斯",43,184);
var p2=createPerson("蓝种人","阿凡达",88,197);
var p3=createPerson("红种人","红魔",500,195);
console.log(p1,p2,p3);

Method_2.png

工厂模式是在直接创建法的基础上封装了一个函数,这样调用函数可以实现对象的量产
优点: 使用封装函数的方法可以实现对象的量产,提高了代码的复用率
缺点: 因为每调用一次创建函数方法,都会定义一个obj对象,所有创建的对象都直接继承自Object,无法明确每个创建的对象的具体类型,如果在业务中需要判断其原型的时候,就无法适用了


当然,有需求就有满足需求的方法,下面的方法是当上面方法无法适用的情况下,通过对类的理解(要不是因为js没有类),利用构造函数来模拟类所创建出的对象,并且有一个优化的方法


3.构造函数方法

知识补充:普通函数VS构造函数
普通函数:function personEat([参1,参2,参...]){函数体}   []-中的内容可有可无根据需求改变
构造函数:function Person([参1,参2,参...]){函数体}
普通函数与构造函数的定义基本一样,除了命名习惯有所不同外,因为使用方法不同而有大差别

普通函数:使用: 函数名([参1,参2,参...]); 直接调用,使用其步骤功能。
构造函数:构造函数使用new关键字实例化为对象。


//类(class):一类事物的总称  --通过类创建对应的对象
//js没有类  所以可以通过构造函数模拟类,*核心思想:改变this的指向性
//模拟(创建)一个学生类(构造函数)
function Student(name,age,height){
//---虽然和直接声明一个函数是同一种方法,但是因为其内容不同
//---因为需要通过new来初始化对象这是一个标准的构造函数(*注意:这里函数名首字母要大写)
    this.name=name;
    this.age=age;
    this.height=height;
    this.showInfo=function(){
        console.log(this.name);
    }
}
//利用new创建并初始化该Student对象的实例
var stu1=new Student("张三",18,180);
var stu2=new Student("李四",34,175);
var stu3=new Student("王麻子",32,190);
console.log(stu1,stu2,stu3);

//这里需要理解理解,new做了什么?
//1.自己创建一个空对象{}
//2.执行函数中的代码,让函数内部this指向空对象
//3.将创建的对象返回
Method_3-1.png
//这样不仅能实现量产,而且使用instanceof 可以检测该对象的原型是具体的某一类
//虽然同样继承自Object,但是原型是Student继承自Object的
console.log(stu1 instanceof Student);
console.log(stu1 instanceof Object );
console.log(Student instanceof Object );

Method_3-2.png

当然,这样的方法相对于最简单最直接的创建与方法封装这样的方法来创建对象,已经是 "程序员一小步,世界开发一大步" 了,但是能不能再优化一下呢? 比如在解析代码的速度上,提高浏览器性能。 仔细观察就会发现,当创建相同类(构造函数)不同对象后,除了各自属性参数不同外,行为或者其他动作也有重复的地方,例如:

//创建一个学生类(构造函数)
function Student(gender,name,age,height){
    this.gender
    this.name=name;
    this.age=age;
    this.showInfo=function(){
        console.log(this.name);
    }
}

var stu1=new Student("张三",18,179);
var stu2=new Student("王四",22,181);

三个不同的对象,可以直接看出同属一个Student构造函数,都有相同名称及功能的showInfo
既然每个对象所实现的功能是一样的,那么每个对象的功能是否相等
由此:
console.log(stu1.showInfo==stu2.showInfo); //false
输出结果为false,说明虽然相同类创建出来的对象所实现的某些功能一样,但是并不能说明它们相等,也就是说没每个
对象中的该功能都各自开辟了独立的空间,如果项目中某些相同功能的方法所主导的逻辑较多,就会增加内存压力

优点: 使用构造函数方法即实现了对象的量产,也可以判断某个对象的具体类型,这样的方法在项目还原时简直是神之一手
缺点: 不同对象因为用了同一个构造函数,具有某些相同的方法,但这些方法并没有开辟共同的空间,导致内存消耗,不利于项目优化

所以还有没有方法可以优化该操作,在内存中能否开辟一个共同空间存储功能相同的方法? 答案是有的!

可以使用`原型创建`的方法,该方法是构造函数法的加强版
既实现了对象创建时的各种问题,也节省了内存资源,改善了面向对象编程的资源消耗问题。

4.原型创建对象---其实是优化构造函数创建法🤣

在解释这一方法之前,我们可以先来了解一下优化原理
通过构造函数实例出来的对象

Method_4.png

发现:每一个相同构造函数实例化的对象,都存在一个[[prototype]]属性 这个属性值就是该对象的原型 通过相同构造函数创建出来的对象,其原型相同。

//获取对象的原型
//通过: 对象.__proto__  获取对象原型
//例如:
var stu1=new Student();
var stu2=new Student();
console.log(stu1.__proto__==stu2.__proto__); //结果是true  

说明虽然同一个类型创建的对象所实现的某些功能相同但不在一个内存空间中,它们的原型却是相同的。
这样我们就可以将构造函数中某些相同的方法定义到同一个类的原型中,这样不同对象可以通过原型调用相同的功能,
节省了内存资源的消耗,优化了浏览器性能

注意:这里构造函数的原型==实例化对象的原型,
把共有的方法(功能)写入到构造函数的原型(prototype)中,
也就意味着把共有方法(功能)写到了实例化对象的原型(__proto__)中,
构造函数的原型(prototype)对象中的方法,是可以被实例化对象直接访问的

所以,优化的方法就有了,如下:
//定义一个老师构造函数
function Teacher(name,age,height){
    this.name=name;
    this.age=age;
    this.height=height;
}
Teacher.prototype.showInfo=function(){
    console.log(this.name);
}
//还有一种写法---应对实现多个功能(方法)时
Teacher.prototype={
    showInfo:function(){
        console.log(this.name);
    },
    say:function(){
        console.log("hello world");
    }
};

//实例化对象
var tea1=new Teacher("王晗",23,182);
var tea2=new Teacher("张益达",24,175);
console.log(tea1,tea2);

Method_5.png

这样,不同的对象因为相同的构造函数及原型相同,所以当实现的功能放入原型后,兄弟们(对象们)从此就可以开启共享模式了 🤏拿捏~

最后,再来让我们看一下

var tea1=new Teacher("王晗",23,182);
var tea2=new Teacher("张益达",24,175);
console.log(tea1.showInfo==tea2.showInfo);
答案是肯定的---true

调用时,根据对象不同this的指向不同输出不同的结果
tea1.showInfo();
tea2.showInfo();

Method_6.png

优点: 使用构造函数方法即实现了对象的量产,也可以判断某个对象的具体类型、而且还优化了内存资源消耗(节约内存),一举多得
注意: 原型必须是共有的 --- 使用优化方法创建对象,共有的方法放在构造函数的原型中,私有的方法放入构造函数里


-------本次内容到此结束--------

等等等等! 我好像遗漏掉了什么

对了! 是new

当然,我们有时候还可以用最原始的手段创建对象

//创建一个对象,拿来结婚
let girlfriend = new Object();
girlfrend.name="小芳";
girlfrend.age=18;
girlfrend.height=178;
girlfrend.say=function(isay){
    while(true){
        alert("说爱我");
        if(isay=="我爱小芳"){
            break;
        }
    }
}
grilfriend.say("我只当是做了个梦");