JavaScript 工厂函数 vs 构造函数

阿里云最近在作活动,低至2折,有兴趣能够看看:
https://promotion.aliyun.com/...

为了保证的可读性,本文采用意译而非直译。javascript

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

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

工厂函数

所谓工厂函数,就是指这些内建函数都是类对象,当你调用他们时,其实是建立了一个类实例”。意思就是当我调用这个函数,其实是先利用类建立了一个对象,而后返回这个对象。因为 Javascript 自己不是严格的面向对象的语言(不包含类),实际上来讲,Javascript 并无严格的“工厂函数”,可是在 Javascript中,咱们能利用函数模拟类。来看下面一个例子:java

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

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

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

  1. 它是一个函数。
  2. 它用来建立对象。
  3. 它像工厂同样,“生产”出来的函数都是“标准件”(拥有一样的属性)

构造函数

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

  1. 在函数内部对新对象(this)的属性进行设置,一般是添加属性和方法。
  2. 构造函数能够包含返回语句(不推荐),但返回值必须是this,或者其它非对象类型的值。
function Person(firstName, lastName, age) {
  this.firstName = firstName;
  this.lastName = lastName;
  this.age = age;
}

使用 new 关键字建立对象

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

  1. 咱们能够在工厂函数中使用 new 关键字吗?
  2. 若是咱们在工厂和构造函数中使用new关键字会发生什么
  3. 若是在使用构造函数建立对象实例时不使用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);

clipboard.png

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

clipboard.png

嗯,有趣的是,它指向 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);

clipboard.png

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

clipboard.png

如今让咱们试着弄清楚原型对象是否像上面同样。

clipboard.png

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

new 背后所作的事

当咱们在建立对象时使用带有构造函数的new关键字时,new 背后所作的事很少。

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

  1. 建立一个空的简单 JavaScript 对象 (即 {})
  2. 连接该对象(即设置该对象的构造函数)到另外一个对象
  3. 将步骤1新建立的对象做为 this 的上下文
  4. 若是该函数没有返回对象,则返回 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);

而后,若是查看建立的对象实例,你但愿看到什么?

clipboard.png

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

若是你对 JavaScript 做用域 this 关键字的工做原理有所了解,那么你能够猜到这里发生了什么? 让咱们来看看。

clipboard.png

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

这是你能够对你的JavaScript程序作的很是讨厌的事情。 所以,使用new运算符,JavaScript引擎将this 变量设置为引用新建立的对象实例,这就是为何咱们能够看到传递给构造函数的全部属性都已设置为 mike

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

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

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

clipboard.png

相关文章
相关标签/搜索