资讯专栏INFORMATION COLUMN

JavaScript 工厂函数 vs 构造函数

RayKr / 2005人阅读

摘要:当谈到语言与其他编程语言相比时,你可能会听到一些令人困惑东西,其中之一是工厂函数和构造函数。好的,让我们用构造函数做同样的实验。当我们使用工厂函数创建对象时,它的指向,而当从构造函数创建对象时,它指向它的构造函数原型对象。

当谈到JavaScript语言与其他编程语言相比时,你可能会听到一些令人困惑东西,其中之一是工厂函数和构造函数。

想阅读更多优质文章请猛戳GitHub博客,一年百来篇优质文章等着你!

工厂函数

所谓工厂函数,就是指这些内建函数都是类对象,当你调用他们时,实际上是创建了一个类实例”。意思就是当我调用这个函数,实际上是先利用类创建了一个对象,然后返回这个对象。由于 Javascript 本身不是严格的面向对象的语言(不包含类),实际上来说,Javascript 并没有严格的“工厂函数”,但是在 Javascript中,我们能利用函数模拟类。来看下面一个例子:

function person(firstName, lastName, age) {
  const person = {};
  person.firstName = firstName;
  person.lastName = lastName;
  person.age = age;
  return person;
}

上述代码,创建一个新对象,并将传递参数作为属性附加到该对象上并返回新对象。 这是一个简单的 JavaScript 工厂函数。

实际上工厂函数也很好理解了:

它是一个函数。

它用来创建对象。

它像工厂一样,“生产”出来的函数都是“标准件”(拥有同样的属性)

构造函数

不同于其它的主流编程语言,JavaScript的构造函数并不是作为类的一个特定方法存在的;当任意一个普通函数用于创建一类对象时,它就被称作构造函数,或构造器。一个函数要作为一个真正意义上的构造函数,需要满足下列条件:

在函数内部对新对象(this)的属性进行设置,通常是添加属性和方法。

构造函数可以包含返回语句(不推荐),但返回值必须是this,或者其它非对象类型的值。

function Person(firstName, lastName, age) {
  this.firstName = firstName;
  this.lastName = lastName;
  this.age = age;
}
使用 new 关键字创建对象

正如上面所说的,我们可以使用 new 来类或者对象,那么你可能会有以下几个问题:

我们可以在工厂函数中使用 new 关键字吗?

如果我们在工厂和构造函数中使用new关键字会发生什么

如果在使用构造函数创建对象实例时不使用new关键字会发生什么

好的,试着找出以上问题的答案之前,我们先做一个小练习来理解这里面发生了什么。

使用new关键字同时使用工厂和构造函数创建两个对象,接着在控制台打印这两个对象。

使用工厂函数
function person(firstName, lastName, age){
  const person = {}
  person.firstName = firstName;
  person.lastName = lastName;
  person.age = age;
  return person;
}

const mike = new person("mike", "grand", 23);



正如我们在上述所看到的,这里的__proto__ 指向其原型对象的指针,让我们试着找出原型对象是什么。为了找出上面mike对象的指向原型对象,让我们做简单的===等式检查。

嗯,有趣的是,它指向 Object.prototype。好的,让我们用构造函数做同样的实验。

理解 JavaScript 的原型

理解原型之前,需要记住以下几点知识:

所有的引用类型(数组、对象、函数),都具有对象特性,即可自由扩展属性(null除外)

所有的引用类型(数组、对象、函数),都有一个__proto__属性,属性值是一个普通的对象

所有的函数,都有一个prototype属性,属性值也是一个普通的对象

所有的引用类型(数组、对象、函数),__proto__属性值指向它的构造函数的prototype属性值

通过代码解释一下:

// 要点一:自由扩展属性
var obj = {}; obj.a = 100;
var arr = []; arr.a = 100;
function fn () {}
fn.a = 100;

// 要点二:__proto__
console.log(obj.__proto__);
console.log(arr.__proto__);
console.log(fn.__proto__);

// 要点三:函数有 prototype
console.log(fn.prototype)

// 要点四:引用类型的 __proto__ 属性值指向它的构造函数的 prototype 属性值
console.log(obj.__proto__ === Object.prototype)
使用构造函数
注意:在JavaScript中,这些构造函数也被称为 constructor,因为它们用于创建对象。
function Person(firstName, lastName, age) {
  this.firstName = firstName;
  this.lastName = lastName;
  this.age = age;
}
const mike = new Person("mike", "grand", 23);

当我们展开第一层的的__proto__时,它内部还有另一个__proto__,我们再次扩展它。

现在让我们试着弄清楚原型对象是否像上面一样。

他们是不同的。 当我们使用工厂函数创建对象时,它的__proto__指向Object.prototype,而当从构造函数创建对象时,它指向它的构造函数原型对象。 那么这里发生了什么?

new 背后所做的事

当我们在创建对象时使用带有构造函数的new关键字时,new 背后所做的事不多。

new 运算符创建一个用户自定义的对象类型的实例或具有构造函数的内置对象的实例。 new 关键字会进行如下操作:

创建一个空的简单 JavaScript 对象 (即 {})

链接该对象(即设置该对象的构造函数)到另一个对象

将步骤1新创建的对象作为 this 的上下文

如果该函数没有返回对象,则返回 this

注释行是伪代码,表示在 new 关键字,JS 背后帮我们做的事情。

function Person(firstName, lastName, age) {
    // this = {};
    // this.__proto__ = Person.prototype;

    this.firstName = firstName;
    this.lastName = lastName;
    this.age = age;
    
    // return this;
}

另外,让我们看看如果将上面的隐式代码添加到工厂函数中会发生什么。

function person(firstName, lastName, age) {
    // this = {};
    // this.__proto__ = Person.prototype; 
 
  
    const person = {};
    person.firstName = firstName;
    person.lastName = lastName;
    person.age = age;
    return person;
    
    // return this;
}

即使使用new关键字调用时将隐式代码添加到工厂函数中,也不会对结果产生任何影响。这是因为,由于我们没有在函数中使用 this 关键字,而且我们显式地返回了一个除this之外的自定义对象,因此没有必要使用隐式代码。无论我们是否对工厂函数使用new关键字,对输出都没有影响。

如果忘记了 new 关键字怎么办

JavaScript 中有许多概念,有时难以掌握。 new 操作符就是其中之一。 如果你不能正确理解它,那么在运行 JavaScript 应用程序时会产生令人讨厌的后果。 在像 Java这 样的语言中,严格限制了如何使用 new 关键字。 但是在 javascript 中,并不是那么严格,如果你不能正确理解它们可能会导致很多问题。

在 JavaScript 中:

可以对任何函数使用 new 运算符

可以使用或不使用 new 关键字将函数作为构造函数调用

让我们看看上面的例子,使用和不使用 new 关键情况

function Person(firstName, lastName, age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
const mike = new Person("mike", "grand", 23);
const bob = Person("bob", "grand", 23);

然后,如果查看创建的对象实例,你希望看到什么?

发生了什么? 使用 new 运算符,正如我们所期待的一样输出正确的对象,但没有new运算符,结果是undefined 怎么可能呢?

如果你对 JavaScript 作用域 this 关键字的工作原理有所了解,那么你可以猜到这里发生了什么? 让我们来看看。

看起来我们传递给没有new关键字的函数的所有属性都已设置为window对象。 那是因为到那个时候函数内部的这个变量引用了globalwindow 对象,基本上我们在这里做的就是污染了全局对象。

这是你可以对你的JavaScript程序做的非常讨厌的事情。 因此,使用new运算符,JavaScript引擎将this 变量设置为引用新创建的对象实例,这就是为什么我们可以看到传递给构造函数的所有属性都已设置为 mike

但是在没有new运算符的情况下调用构造函数的情况下,JavaScript 引擎会将 this 解释为常规函数调用,而没有显式返回语句时返回undefined。 这就是理解new 运算符在JavaScript中的工作原理非常关键的原因。

你的点赞是我持续分享好东西的动力,欢迎点赞!

欢迎加入前端大家庭,里面会经常分享一些技术资源。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/104115.html

相关文章

  • 前端进击的巨人(七):走进面向对象,原型与原型链,继承方式

    摘要:除了以上介绍的几种对象创建方式,此外还有寄生构造函数模式稳妥构造函数模式。 showImg(https://segmentfault.com/img/remote/1460000018196128); 面向对象 是以 对象 为中心的编程思想,它的思维方式是构造。 面向对象 编程的三大特点:封装、继承、多态: 封装:属性方法的抽象 继承:一个类继承(复制)另一个类的属性/方法 多态:方...

    wums 评论0 收藏0
  • 第6章:可维护性软件构建方法 6.2可维护性设计模式

    摘要:抽象工厂模式将具有共同主题的对象工厂分组。对可重用性和可维护性设计模式的高层考虑创造性模式工厂方法模式也称为虚拟构造器意图定义一个用于创建对象的接口,但让子类决定实例化哪个类。 大纲 创造性模式 工厂方法模式创建对象而不指定要创建的确切类。 抽象工厂模式将具有共同主题的对象工厂分组。 Builder模式通过分离构造和表示来构造复杂的对象。 结构模式 Bridge将抽象从其实现中分...

    VioletJack 评论0 收藏0
  • JS程序

    摘要:设计模式是以面向对象编程为基础的,的面向对象编程和传统的的面向对象编程有些差别,这让我一开始接触的时候感到十分痛苦,但是这只能靠自己慢慢积累慢慢思考。想继续了解设计模式必须要先搞懂面向对象编程,否则只会让你自己更痛苦。 JavaScript 中的构造函数 学习总结。知识只有分享才有存在的意义。 是时候替换你的 for 循环大法了~ 《小分享》JavaScript中数组的那些迭代方法~ ...

    melody_lql 评论0 收藏0
  • JavaScript面向对象OOM 2(JavaScript 创建对象的工厂模式和构造函数模式)

    摘要:都是构造函数模式创建的原生构造函数。使用构造函数创建对象经历了以下四个过程创建一个新对象构造函数的作用域交给新对象。   在创建对象的时候,使用对象字面量和 new Object() 构造函数的方式创建一个对象是最简单最方便的方式。但是凡是处于初级阶段的事物都会不可避免的存在一个问题,没有普适性,意思就是说我要为世界上(程序中)的所有使用到的对象都使用一遍 var xxx = {} ,...

    you_De 评论0 收藏0
  • JavaScript面向对象OOM 2(JavaScript 创建对象的工厂模式和构造函数模式)

    摘要:都是构造函数模式创建的原生构造函数。使用构造函数创建对象经历了以下四个过程创建一个新对象构造函数的作用域交给新对象。   在创建对象的时候,使用对象字面量和 new Object() 构造函数的方式创建一个对象是最简单最方便的方式。但是凡是处于初级阶段的事物都会不可避免的存在一个问题,没有普适性,意思就是说我要为世界上(程序中)的所有使用到的对象都使用一遍 var xxx = {} ,...

    liuchengxu 评论0 收藏0

发表评论

0条评论

RayKr

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<