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


Posted in Javascript onMay 06, 2019

前言

当谈到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);

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

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

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

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

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

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

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

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

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

他们是不同的。 当我们使用工厂函数创建对象时,它的__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);

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

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

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

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

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

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

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

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

总结

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

Javascript 相关文章推荐
超级酷和最实用的jQuery实例收集(20个)
Apr 21 Javascript
JavaScript创建类/对象的几种方式概述及实例
May 06 Javascript
固定背景实现的背景滚动特效示例分享
May 19 Javascript
完美兼容各大浏览器的jQuery仿新浪图文淡入淡出间歇滚动特效
Nov 12 Javascript
JavaScript控制listbox列表框的项目上下移动的方法
Mar 18 Javascript
js控制多图左右滚动切换效果代码分享
Aug 26 Javascript
关于使用js算总价的问题
Jun 23 Javascript
JavaScript 自定义事件之我见
Sep 25 Javascript
关于Vue Router中路由守卫的应用及在全局导航守卫中检查元字段的方法
Dec 09 Javascript
Node.js中Koa2在控制台输出请求日志的方法示例
May 02 Javascript
JavaScript动态检测密码强度原理及实现方法详解
Jun 11 Javascript
vue实现动态给id赋值,点击事件获取当前点击的元素的id操作
Nov 09 Javascript
微信小程序登录数据解密及状态维持实例详解
May 06 #Javascript
一文了解Vue中的nextTick
May 06 #Javascript
angular 服务随记小结
May 06 #Javascript
详解如何使用nvm管理Node.js多版本
May 06 #Javascript
关于AOP在JS中的实现与应用详解
May 06 #Javascript
JS使用iView的Dropdown实现一个右键菜单
May 06 #Javascript
一文读懂ES7中的javascript修饰器
May 06 #Javascript
You might like
PHP5下$_SERVER变量不再受magic_quotes_gpc保护的弥补方法
2012/10/31 PHP
PHP连接局域网MYSQL数据库的简单实例
2013/08/26 PHP
PHP实现把MySQL数据库导出为.sql文件实例(仿PHPMyadmin导出功能)
2014/05/10 PHP
PHP实现一个简单url路由功能实例
2016/11/05 PHP
深入理解Yii2.0乐观锁与悲观锁的原理与使用
2017/07/26 PHP
javascript动画效果类封装代码
2007/08/28 Javascript
JavaScript 节点操作 以及DOMDocument属性和方法
2007/12/06 Javascript
jquery合并表格中相同文本的相邻单元格
2015/07/17 Javascript
jQuery实现form表单基于ajax无刷新提交方法详解
2015/12/08 Javascript
JavaScript脚本库编写的方法
2015/12/09 Javascript
基于BootStrap Metronic开发框架经验小结【一】框架总览及菜单模块的处理
2016/05/12 Javascript
Three.js学习之正交投影照相机
2016/08/01 Javascript
让bootstrap的carousel支持滑动滚屏的实现代码
2017/11/27 Javascript
webpack+vue-cil中proxyTable处理跨域的方法
2018/07/20 Javascript
详解如何在微信小程序开发中正确的使用vant ui组件
2018/09/13 Javascript
React 路由懒加载的几种实现方案
2018/10/23 Javascript
JS实现的A*寻路算法详解
2018/12/14 Javascript
解决Layui中layer报错的问题
2019/09/03 Javascript
JS中数组实现代码(倒序遍历数组,数组连接字符串)
2019/12/29 Javascript
Python入门及进阶笔记 Python 内置函数小结
2014/08/09 Python
Python复制目录结构脚本代码分享
2015/03/06 Python
python+selenium实现登录账户后自动点击的示例
2017/12/22 Python
python将文本中的空格替换为换行的方法
2018/03/19 Python
Numpy 改变数组维度的几种方法小结
2018/08/02 Python
对Python 语音识别框架详解
2018/12/24 Python
详解Python基础random模块随机数的生成
2019/03/23 Python
Django 缓存配置Redis使用详解
2019/07/23 Python
Sneaker Studio捷克:购买运动鞋
2018/07/08 全球购物
自我鉴定注意事项
2014/01/19 职场文书
合伙开公司协议书范本
2014/10/28 职场文书
事业单位工作人员年度考核个人总结
2015/02/12 职场文书
2015年绩效考核工作总结
2015/05/23 职场文书
闪闪的红星观后感
2015/06/08 职场文书
追悼会悼词大全
2015/06/23 职场文书
结婚典礼致辞
2015/07/28 职场文书
只需要100行Python代码就可以实现的贪吃蛇小游戏
2021/05/27 Python