创建对象的几种方法

本文目录:

  • 1.字面量的方式
  • 2.工厂模式
  • 3.构造函数
  • 4.前置知识:对象引用
  • 5.延伸知识:原型模式

1.字面量的方式

var obj = {}; //空对象
//添加属性
obj.name = "小白";
obj.age = 10;
obj.sayHi = function () {
    console.log("我是:" + this.name);
}
obj.sayHi();

优化后的写法

var obj2 = {
    name: "小明",
    age: 20,
    sayHi:function () {
        console.log("我是:" + this.name);
    },
    eat:function () {
        console.log('吃了');
    }
}
 
obj2.sayHi();
obj2.eat();

2.工厂模式

我们先通过new Object()的方式创建出了一个叫小强的对象,那如果我需要创建另一个叫小花的对象,应该如何做呢?如果想创建100个对象又应该怎么实现呢?
按照最原始的方法,创建另一个对象也非常容易,把代码复制一份改改就OK了

var person = new Object()
person.name = "小花"
person.showName = function(){
    alert(this.name)
}
//调用对象上的方法
person.showName()

问题来了,如果想创建100或者更多的对象,我们不可能把代码复制100份,这样会产生非常多的冗余代码,这个时候最容易想到的就是把相同的代码进行封装,所以得到一个封装的函数

function createPerson(name){
    var person = new Object()
    person.name = name
    person.showName = function(){
        alert(this.name)
    }
    return person
}
//创建对象 小强
var p1 = createPerson("小强")
p1.showName()
//创建对象 小花
var p2 = createPerson("小花")
p2.showName()

上面封装函数的形式来节约代码,和工厂流水线生产非常相似,首先new一个对象出来,相当于工厂去买原材料,接下来,给对象加属性和方法,相当于工厂对原材料进行加工,最后,return一个对象,相当于工厂生产出了产品,我们把上面封装函数的这种方式叫做工厂方式,这里最主要要把我工厂方式的特点,流水线生产

3.构造函数

在上面工厂方式的基础上,我们再来做进一步的优化,当我们使用系统内置对象的时候,非常的方便,直接new一下就ok了,例如:new Date(), 这样就会创建出一个时间对象,那我们自定义的对象能不能也像内置对象一样直接使用new的形式来创建呢?答案是肯定的,具体写法如下:

function CreatePerson(name){
    this.name = name
    this.showName = function(){
        alert(this.name)
    }
}
//创建对象 小强
var p1 = new CreatePerson("小强")
p1.showName()

//创建对象 小花
var p2 = new CreatePerson("小花")
p2.showName()

上面函数能简写是因为new关键字,以new去调用一个函数的方式来创建对象,实际上会经历以下4个步骤:

  • 1.创建一个新对象
  • 2.将构造函数的作用域赋给新对象(因此,this就指向了这个新对象)
  • 3.执行构造函数中的代码(为这个新对象添加属性和方法)
  • 4.返回新对象

因此,new关键字的作用就是当new去调用一个函数的时候,这个函数里面的this会指向创建出来的对象并且这个函数的返回值就是这个创建出来的对象,总结起来就是说new可以让被调用的函数返回一个创建好的对象,并且这个函数里面的this都是指向这个对象的,同时还引出一个构造函数的概念,当new去调用一个函数的时候,这个被调用的函数就是构造函数

4.前置知识:对象引用

在javascript中数据类型可以分为基本类型和引用类型,基本类型就是我们已经学过的Undefined、Null、Boolean、Number、String,引用类型有Object、Array、Date、RegExp、Function等,基本类型的值是简单的数据段,例如:数字5、字母A,引用类型值指那些可能由多个值构成的对象。

在从一个变量向另一个变量复制基本类型值和引用类型值时,有一些不同,如果从一个变量向另一个变量复制基本类型的值,会在变量对象上创建一个新值,然后把该值复制到为新变量分配的位置上。

var num1 = 10
var num2 = num1

在上面代码中,num1存了变量10,在给num2赋值的时候,相当于拷贝了num1的副本然后赋值给了num2,这个时候,num1和num2可以参与任何操作而互相不会影响。这里需要注意的关键点就是num1和num2以后参与任何运算不会相互影响
如果从一个变量向另一个变量复制引用类型的值,同样还是会将存储在变量对象中的值复制一份放到新变量分配的空间中,不同的是,复制的这个副本是一个指针,并不是具体的对象,这个指针指向存储在堆内存中的一个对象,复制操作结束后,两个变量实际上将引用同一个对象

var arr1 = [1, 2, 3]
var arr2 = arr1
arr2.push(4)
console.log(arr1)
引用类型的赋值过程.png

5.延伸知识:原型模式

我们理解了对象的引用造成的影响就是两个变量引用一个对象的时候会相互影响,基于这个知识点,我们来看看构造函数方式创建对象有什么问题

function CreatePerson(name) {
    this.name = name
    this.showName = function () {
        alert(this.name)
    }
}
var p1 = new CreatePerson("小强")
var p2 = new CreatePerson("小李")
alert(p1.showName == p2.showName) //执行结果为false

从上面的代码中我们知道,虽然p1和p2都有showName这个方法,但是它们并不是相同的,那意味着如果要创建100个或者更多的对象出来,那么在内存中同样会有非常多的showName方法被创建,然而创建出这么多showName是没有必要的,非常的浪费资源,面对这种问题,我们希望做的事情就是让p1和p2共用一个showName,如果有更多的对象被创建,这个showName在内存中也是唯一的,这个时候,就需要学习js中的原型了。

我们创建的每一个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法,这个对象就是构造函数CreatePerson的原型对象,当调用CreatePerson创建出一个具体对象p1后,p1内部也有一个指针指向构造函数的原型对象,同样的道理,不管创建出多少对象,这些对象内部都会有个指针指向构造函数的原型对象,使用原型对象的好处是让所有被创建出来的对象共享这个原型对象所包含的属性和方法

function CreatePerson(name){
    this.name = name
}
CreatePerson.prototype.showName = function(){
    alert(this.name)
}
var p1 = new CreatePerson("小强")
var p2 = new CreatePerson("小花")
var p3 = new CreatePerson("小豆")
alert(p1.showName === p2.showName) // true
alert(p2.showName === p3.showName) // true

我们可以通过hasOwnProperty看看是不是对象自身的属性

function Fn1(){
    this.num = 10
}
Fn1.prototype.num2 = 20

var f1 = new Fn1()

alert(f1.hasOwnProperty("num"))  //true
alert(f1.hasOwnProperty("num2")) //false
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。