慢慢认识JavaScript面向对象(一)创建对象

JavaScript面向对象——创建对象

虽然Object构造函数或对象字面量可以用来创建单个对象
但是这些方式有个明显的缺点: 使用同一个接口创建很多对象, 会产生大量的重复代码.
我们会有一些列的方式来解决这个问题, 最终得到我们最佳理想的方式来创建对象.

1.使用工厂模式创建对象

工厂模式是一种非常常见的设计模式, 这种模式抽象了创建具体对象的过程.

因为JavaScript中没法创建类, 开发人员就发明了一种函数, 用函数来封装以特定接口创建对象的细节.

工厂模式创建对象:

// 创建工厂函数
function createPerson(name, age, height) {
    var o = new Object()
    o.name = name
    o.age = age
    o.height = height

    o.sayHello = function () {
        alert("Hello, My name is " + this.name)
    }
    return o
}

// 创建三个对象
var person1 = createPerson("Coderwhy", 18, 1.88)
var person2 = createPerson("Coderwhy", 18, 1.88)
var person3 = createPerson("alon", 19, 1.88)
person1.sayHello() // Hello, My name is Coderwhy
person2.sayHello() // Hello, My name is Coderwhy
person3.sayHello() // Hello, My name is alon
alert(person1.sayHello === person2.sayHello) // false
alert(person2.sayHello === person3.sayHello) // false

代码解析:

  • 函数createPerson()能够根据接受的参数来构建一个包含所有必要信息的Person对象
  • 可以无数次地调用这个函数,而每次它都会返回一个包含三个属性一个方法的对象。
  • 工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。
  • 随着JavaScript的发展,又一个新模式出现了。

主要好处就是可以消除对象间的耦合,通过使用工程方法而不是new关键字。将所有实例化的代码集中在一个位置防止代码重复。

工厂模式解决了重复实例化的问题 ,但还有一个问题,那就是识别问题,因为根本无法 搞清楚他们到底是哪个对象的实例。

2.使用构造函数函数创建对象

工厂模式是一种非常常见的设计模式, 这种模式抽象了创建具体对象的过程.

因为JavaScript中没法创建类, 开发人员就发明了一种函数, 用函数来封装以特定接口创建对象的细节.

function Person(name, age, height) {
    this.name = name
    this.age = age
    this.height = height

    this.sayHello = function () {
        alert(this.name)
    }
}

// 使用构造函数创建对象
var person1 = new Person("Coderwhy", 18, 1.88)
var person2 = new Person("Kobe", 30, 1.98)
person1.sayHello() // Coderwhy
person2.sayHello() // Kobe
alert(person1.constructor === Person) // true
alert(person2.constructor === Person) // true

代码解析:Person()中的代码除了与 createPerson()中相同的部分外,还存在以下不同之处

  • 没有显式地创建对象;
  • 直接将属性和方法赋给了 this 对象;
  • 没有 return 语句

按照惯例,构造函数始终都应该以一个大写字母开头,而非构造函数则应该以一个小写字母开头。
要创建 Person 的新实例,必须使用 new 操作符。以这种方式调用构造函数实际上会经历以下 4个步骤:

  • 创建一个新对象;
  • 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象);
  • 执行构造函数中的代码(为这个新对象添加属性);
  • 返回新对象。

在前面例子的最后,person1 和 person2 分别保存着 Person 的一个不同的实例。这两个对象都有一个 constructor(构造函数)属性,该属性指向 Person,如下所示。

alert(person1.constructor === Person) // true
alert(person2.constructor === Person) // true

对象的 constructor 属性 最初是用来标识对象类型的(在本文中标识createPerson和Person两个函数的类型)。但是,提到检测对象类型,还是 instanceof 操作符要更可靠一些。我们在这个例子中创建的所有对象既是 Object 的实例,同时也是 Person的实例,这一点通过 instanceof 操作符可以得到验证。

        alert(person1 instanceof Object); //true 
        alert(person1 instanceof Person); //true 
        alert(person2 instanceof Object); //true 
        alert(person2 instanceof Person); //true

上文工厂模式下:

         alert(person1 instanceof Object) // true
         alert(person2 instanceof createPerson) // false

创建自定义的构造函数意味着将来可以将它的实例标识为一种特定的类型;而这正是构造函数模式胜过工厂模式的地方。

关于构造函数

  • 我们知道, 构造函数也是一个函数, 只是使用的方式和别的函数不太一样.(使用new)
  • 但是, 构造函数毕竟也是函数, 因此也可以像普通的函数一样去使用
  • 而且, 其他任何的函数, 也可以通过new关键字来调用, 这个时候这个函数也可以被称为构造函数.

把构造函数当做普通的函数去调用

// 当做构造函数使用
var person = new Person("Coderwhy", 18, 1.88) // person对象
person.sayHello()

// 作为普通的函数调用
Person("Kobe", 30, 1.98) // window对象
window.sayHello()

// 在另外一个对象的作用域调用
var o = new Object()
Person.call(o, "Curry", 28, 1.93) // o对象
o.sayHello()

构造函数来创建对象的缺陷:

  • 使用构造函数的主要问题,就是每个方法都要在每个实例上重新创建一遍。
  • 在前面的例子中,personl和person2都有一个名为sayName()的方法,但那两个方法不是同一个Function的实例。
  • JavaScript中的函数也是对象,因此每定义一个函数,也就是实例化了一个对象

构造函数的换一种形式:

  • 也就是上面的代码类似于下面的写法
function Person(name, age, height) {
    this.name = name
    this.age = age
    this.height = height

    this.sayHello = new Function("alert(this.name)")
}

有什么问题呢?

  • 从这个角度上来看构造函数,更容易明白每个Person实例都包含一个不同的Function实例.
  • 但是, 有必要创建多个Function实例吗? 它们执行的代码完全相同.
  • 你也许会考虑, 它们需要区分不同的对象, 不过, 在调用函数时, 我们传入的this就可以区分了. 没有必要创建出多个Function的实例.

我们可以验证一下这是两个不同的函数:

alert(person1.sayHello === person2.sayHello) // false

工厂模式下也一样:

alert(person1.sayHello === person2.sayHello) // false
alert(person2.sayHello === person3.sayHello) // false

有没有办法让它们是同一个函数呢? 使用全局函数即可

// 定义全局和函数
function sayHello() {
    alert(this.name)
}

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

    this.sayHello = sayHello
}

// 使用构造函数创建对象
var person1 = new Person("Coderwhy", 18, 1.88)
var person2 = new Person("Kobe", 30, 1.98)

alert(person1.sayHello === person2.sayHello) // true

新的问题:

  • 这样做确实解决了两个函数做同一件事的问题,可是新问题又来了: 在全局作用域中定义的函数我们的目的却是只能被某个对象调用,这让全局作用域有点名不副实。
  • 而且我们进一步思考: 如果对象需要定义很多方法,那么就要定义很多个全局函数,于是我们这个自定义的引用类型就丝毫没有封装性可言了。

有没有新的解决方案呢?使用原型模式。

3.使用原型模式创建对象

我们创建的每个函数都有一个 prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。

如果按字面意思来理解,那么prototype就是通过调用构造函数而创建的那个对象实例的原型对象。使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。换句话说,不必在构造函数中定义对象实例的信息,而是可以将这些信息直接添加到原型对象中。例如:

  function Person() {
        }

        Person.prototype.name = 'alon';
        Person.prototype.age = 29;
        Person.prototype.job = 'web developer';
        Person.prototype.sayName = function () {
            alert(this.name);
        }

        var person1 = new Person();
        person1.sayName();//"alon"

        var person2 = new Person();
        person2.sayName(); //"alon" 
        
        alert(person1.sayName == person2.sayName); //true

代码解析:

  • 在此,我们将 sayName()方法和所有属性直接添加到了 Person 的 prototype 属性中,构造函数变成了空函数。
  • 即使如此,也仍然可以通过调用构造函数来创建新对象,而且新对象还会具有相同的属性和方法。但与构造函数模式不同的是,新对象的这些属性和方法是由所有实例共享的。换句话说,person1 和 person2 访问的都是同一组属性和同一个 sayName()函数。
  • 要理解原型模式的工作原理,必须先理解 ECMAScript 中原型对象的性质。

文中参考链接:https://www.cnblogs.com/wongh/p/7670277.htmlJavaScript面向对象详解(一)

文中有不足的地方还请各位小伙伴指出,共同学习。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值