JavaScript中工厂函数与构造函数示例详解

前言

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

工厂函数

所谓工厂函数,就是指这些内建函数都是类对象,当你调用他们时,实际上是创建了一个类实例”。意思就是当我调用这个函数,实际上是先利用类创建了一个对象,然后返回这个对象。由于 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对象。 那是因为到那个时候函数内部的这个变量引用了global 或 window 对象,基本上我们在这里做的就是污染了全局对象。

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

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

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对我们的支持。

(0)

相关推荐

  • javascript设计模式之对象工厂函数与构造函数详解

    下面通过文字详解加代码分析的方式给大家分享下javascript设计模式之对象工厂函数与构造函数的相关知识. 概述使用对象字面量,或者向空对象中动态地添加新成员,是最简单易用的对象创建方法.然而,除了这两种常用的对象创建方式,JavaScript还提供了其他方法创建对象.1).使用工厂函数创建对象我们可以编写一个函数,此函数的功能就是创建对象,可将其. 概述 使用对象字面量,或者向空对象中动态地添加新成员,是最简单易用的对象创建方法. 然而,除了这两种常用的对象创建方式,JavaScript还提

  • JS特殊函数(Function()构造函数、函数直接量)区别介绍

    函数定义 函数是由这样的方式进行声明的:关键字 function.函数名.一组参数,以及置于括号中的待执行代码. 函数的构造语法有这三种: Js代码 复制代码 代码如下: 1.function functionName(arg0, arg1, ... argN) { statements }//function语句 2.var function_name = new Function(arg1, arg2, ..., argN, function_body);//Function()构造函数 3

  • JS面向对象基础讲解(工厂模式、构造函数模式、原型模式、混合模式、动态原型模式)

    什么是面向对象?面向对象是一种思想!(废话). 面向对象可以把程序中的关键模块都视为对象,而模块拥有属性及方法.这样我们如果把一些属性及方法封装起来,日后使用将非常方便,也可以避免繁琐重复的工作.接下来将为大家讲解在JS中面向对象的实现.   工厂模式 工厂模式是软件工程领域一种广为人知的设计模式,而由于在ECMAScript中无法创建类,因此用函数封装以特定接口创建对象.其实现方法非常简单,也就是在函数内创建一个对象,给对象赋予属性及方法再将对象返回即可. function createBlo

  • JavaScript中的工厂函数(推荐)

    在学习jQuery的时候,我们经常会看到"工厂函数"这个概念,那么究竟什么是"工厂函数"呢?我们来看看概念,"所谓工厂函数,就是指这些内建函数都是类对象,当你调用他们时,实际上是创建了一个类实例".意思就是当我调用这个函数,实际上是先利用类创建了一个对象,然后返回这个对象.由于Javascript本身不是严格的面向对象的语言(不包含类),实际上来说,Javascript并没有严格的"工厂函数",但是在Javascript中,我

  • 深入理解javascript构造函数和原型对象

    常用的几种对象创建模式 使用new关键字创建 最基础的对象创建方式,无非就是和其他多数语言一样说的一样:没对象,你new一个呀! var gf = new Object(); gf.name = "tangwei"; gf.bar = "c++"; gf.sayWhat = function() { console.log(this.name + "said:love you forever"); } 使用字面量创建 这样似乎妥妥的了,但是宅寂的

  • 详解Javascript 中的 class、构造函数、工厂函数

    到了ES6时代,我们创建对象的手段又增加了,在不同的场景下我们可以选择不同的方法来建立.现在就主要有三种方法来构建对象,class关键字,构造函数,工厂函数.他们都是创建对象的手段,但是却又有不同的地方,平时开发时,也需要针对这不同来选择. 首先我们来看一下,这三种方法是怎样的 // class 关键字,ES6新特性 class ClassCar { drive () { console.log('Vroom!'); } } const car1 = new ClassCar(); consol

  • JavaScript构造函数详解

    构造函数就是初始化一个实例对象,对象的prototype属性是继承一个实例对象. 构造函数注意事项: 1.默认函数首字母大写 2.构造函数并没有显示返回任何东西.new 操作符会自动创建给定的类型并返回他们,当调用构造函数时,new会自动创建this对象,且类型就是构造函数类型. 3.也可以在构造函数中显示调用return.如果返回的值是一个对象,它会代替新创建的对象实例返回.如果返回的值是一个原始类型,它会被忽略,新创建的实例会被返回. function Person( name){ this

  • JS中的构造函数详细解析

    在JavaScript中,任何合法的函数都可以作为对象的构造函数,这既包括系统内置函数,也包括用户自己定义的函数.一旦函数被作为构造函数执行,它内部的this属性将引用函数本身. 通常来说,构造函数没有返回值,它们只是初始化由this指针传递进来的对象,并且什么也不返回.如果一个函数有返回值,被返回的对象就成了new表达式的值.从形式上看,一个函数被作为构造函数还是普通函数执行的唯一区别,是否用new运算符. 上面的描述事实上有着更为精确的含义,这要把函数如果有返回值的情况分为函数的返回值是引用

  • JavaScript中工厂函数与构造函数示例详解

    前言 当谈到JavaScript语言与其他编程语言相比时,你可能会听到一些令人困惑东西,其中之一是工厂函数和构造函数. 工厂函数 所谓工厂函数,就是指这些内建函数都是类对象,当你调用他们时,实际上是创建了一个类实例".意思就是当我调用这个函数,实际上是先利用类创建了一个对象,然后返回这个对象.由于 Javascript 本身不是严格的面向对象的语言(不包含类),实际上来说,Javascript 并没有严格的"工厂函数",但是在 Javascript中,我们能利用函数模拟类.来

  • JavaScript中new操作符的原理示例详解

    new的用处 new的作用是通过构造函数来创建一个实例对象,该实例与原型和构造函数之间的关系如下图所示: 先来总结一下 创建一个空对象 空对象的内部属性 __proto__ 赋值为构造函数的 prototype 属性 将构造函数的 this 指向空对象 执行构造函数内部代码 返回该新对象 详细说明 执行 new 操作时会依次经过以下步骤: 1.创建一个空对象 空对象是 Object 的实例,即 {} . let obj = {} 2.空对象的内部属性 __proto__ 赋值为构造函数的 pro

  • mongoDB中聚合函数java处理示例详解

    1.问题 最近在做项目的时候碰到一个对mongoDB的数据处理,从MongoDB中拿到内嵌文档的时间排序的list. 一开始考虑到直接对mongoDB中的属性排序,后面发现属性存在内嵌文档中,所以处理中需要用到聚合函数. 思考 (key)解决这个问题的过程让我学到很多,发现自己在解决一个问题不仅查找问题的姿势不对,浪费太多时间.而且在碰到问题之后,应该多看看解决办法,甚至解决了之后要去思考问题,回顾问题.而不是像以前一样,解决问题了就万事大吉,抛之脑后. 2.解决 需要对document中的一个

  • JavaScript函数封装的示例详解

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width,

  • JavaScript箭头函数与普通函数的区别示例详解

    目录 箭头函数与普通函数的区别 箭头函数的理解 箭头函数里的this指向 总结 箭头函数与普通函数的区别 要讨论箭头函数和普通函数的区别,首先来看看两者的基本格式 普通函数和箭头共同点就是圆括号和大括号,圆括号里面一般放置参数,大括号一般放置函数主体,很明显箭头函数不需要写那么长,举个例子,有一个数组,使用map方法为数组的每个元素增加字符 let arr=['昨天','今天','明天'] let newarr=arr.map(function(item){ return item+='放假'

  • Swift中的高阶函数功能作用示例详解

    目录 高阶函数的作用 1. 简化代码 2. 提高可读性 3. 支持函数式编程 4. 提高代码的可重用性 常见的高阶函数 1. map() 2. filter() 3. reduce() 4. sorted() 5. forEach() 6. compactMap() 7. flatMap() 8. zip() 9. first() 10. contains() 高阶函数的作用 Swift中的高阶函数是指那些参数或返回值是函数的函数.它们的存在使得我们可以用非常简洁和优雅的代码来解决许多问题. 1

  • JavaScript中object和Object的区别(详解)

    JavaScript中object和Object有什么区别,为什么用typeof检测对象,返回object,而用instanceof 必须要接Object呢 这个问题和我之前遇到的问题非常相似,我认为这里有两个问题需要解决,一个是运算符new的作用机制,一个是function关键字和Funtion内置对象之间的区别.看了一些前辈的博客和标准,这里帮提问者总结一下. 1.new new运算符的作用是创建一个对象实例.这个对象可以是用户自定义的,也可以是带构造函数的一些系统自带的对象.如果 new

  • Go语言中的字符串处理方法示例详解

    1 概述 字符串,string,一串固定长度的字符连接起来的字符集合.Go语言的字符串是使用UTF-8编码的.UTF-8是Unicode的实现方式之一. Go语言原生支持字符串.使用双引号("")或反引号(``)定义. 双引号:"", 用于单行字符串. 反引号:``,用于定义多行字符串,内部会原样解析. 示例: // 单行 "心有猛虎,细嗅蔷薇" // 多行 ` 大风歌 大风起兮云飞扬. 威加海内兮归故乡. 安得猛士兮守四方! ` 字符串支持转义

  • JavaScript稀疏数组与孔hole示例详解

    目录 稀疏数组是什么 JavaScript数组天生就是稀疏数组 JavaScript数组稀疏特性带来的“怪异现象” slice会复制孔 forEach.every会跳过孔(不对孔调用回调函数) map不对孔调用回调函数,但是孔会保留 filter不对孔调用回调函数,但是孔会被过滤掉 join会将孔转化为一个空字符串进行拼接,与undefined一样 初始化无孔数组的方法 Array.apply(null, Array(n))的原理 稀疏数组是什么 在绝大多数JavaScript的实现中,数组是稀

  • JavaScript实现基础排序算法的示例详解

    目录 前言 正文 1.冒泡排序 2.选择排序 3.插入排序 4.快速排序 前言 文本来总结常见的排序算法,通过 JvavScript  来实现 正文 1.冒泡排序 算法思想:比较相邻两个元素的大小,如果第一个比第二个大,就交换它们.从头遍历到尾部,当一轮遍历完后,数组最后一个元素是最大的.除去最后一个元素,对剩下的元素重复执行上面的流程,每次找出剩余元素中最大的,遍历完后,数组是升序的 算法分析:总共需要进行length * (length - 1) / 2 次比较,所以时间复杂度为O(n^2)

随机推荐