js中创建对象的几种方式


Posted in Javascript onFebruary 05, 2017

前言

不管是哪门语言,千变万化不离其宗,深入理解其本质,方能应用自如。对应到js,闭包,原型,函数,对象等是需要花费大功夫思考、理解的。本文穿插了js原型和函数的相关知识,讨论了批量创建对象的几种方式以及它们的优缺点。

正文

说起创建对象,最容易想到的便是通过对象字面量方式直接定义一个对象吧,但这种方式只能创建少量,单独且相互间无联系的对象。若要批量创建对象,该如何?

工厂模式

工厂模式非常直观,将创建对象的过程抽象为一个函数,用函数封装以特定接口创建对象的细节。如下所示:

function createStudent(name,sex,grade){       
 var o = new Object();
 o.name = name;
 o.sex = sex;
 o.grade = grade;

 o.sayName = function(){
 console.log(this.name);
 }
 return o;
}
var s1 = createStudent('Claiyre','famale',1);

通俗地讲,工厂模式就是将创建对象的语句放在一个函数里,通过传入参数来创建特定对象,最后返回创建的对象。

工厂模式虽然可以创建多个相似的对象,但却不能解决对象标识的问题,即怎样知道一个对象的类型。构造函数模式应运而生。

构造函数模式

构造函数模式是java语言创建对象的通用方式。两种语言用构造函数创建对象的方式略有不同,注意区别。

在JavaScript中没有类的概念,函数即为一等公民,因此,不必显式声明某个类,直接创建构造函数即可,类的方法和属性在构造函数中(或原型对象上)处理。构造函数模式的示例代码如下:

function Student(name,sex,grade){       
 this.name = name;
 this.sex = sex;
 this.grade = grade;
 this.sayName = function(){
 console.log(this.name);
 }
}
var s2 = new Student('孙悟空','male',2);

细心的朋友一定发现了构造函数的函数名首字母是大写的,而普通函数首字母则是小写,这是众多OO语言约定俗成的规定,虽然大多数情况下不大写也不会报错,但是为了代码的规范性和可读性,还是应该将构造函数的首字母大写,与普通函数区别开。

与工厂模式相比,用构造模式创建对象有以下几点不同:

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

此外,还应注意到要创建Student的实例,必须要使用new操作符,创建的实例对象将有一个constructor(构造器)属性,指向Person构造函数。调用构造函数创建对象经过了以下几个过程:

  • 创建一个新对象
  • 将构造函数的作用域赋给新对象(因此this就指向了这个新对象)
  • 执行构造函数中的代码
  • 返回新对象(不需要显式返回)

构造函数虽好用,但也不是没有缺点。使用构造函数的主要问题是:每个方法都要在每个实例上创建一遍。在ECMAScript中,函数即对象,因此每定义一个函数,也就是实例化了一个对象。下面的例子证明了这个缺点。

var s3 = new Student('唐僧','male',3);
var s4 = new Student('白骨精','female',4);
s3.sayName();
s4.sayName();
console.log(s3.sayName == s4.sayName);

运行结果:

js中创建对象的几种方式

也就是说通过构造函数实例化的多个对象的方法,是多个不同的方法,但它们内部的代码以及实现的功能是相同的,这就造成了一定的资源浪费。

幸运的是,这个问题可以用原型模式来解决。

原型模式

js中,每个函数都有一个prototype属性,它是一个指针,指向一个对象,叫做原型对象,原型对象包含了可以由特定类型的所有实例对象共享的属性和方法。此外,这个对象有一个与生自来的属性constructor,指向创建对象的构造方法。

使用原型模式可以让所有的实例共享原型对象中的属性和方法,也就是说,不必再构造函数中定义对象实例的信息。用代码表示如下:

function Student_1(){
}
Student_1.prototype.name = 'Claiyre';
Student_1.prototype.sex = 'female';
Student_1.prototype.class = 5;
Student_1.prototype.sayName = function (){
 console.log(this.name);
}
var s5 = new Student_1();       s5.sayName(); //Claiyre
var s6 = new Student_1();
s6.sayName(); //Claiyre

一张图胜过千言万语,下图清楚地阐释了各个对象和原型对象间的关系:

js中创建对象的几种方式

了解过原型后,可以继续在实例对象上增添属性或方法:

s6.name = 'John';      
s6.sayName(); //John

当要读取某个对象的属性时,都会执行一次搜索,搜索首先从对象实例本身开始,如果在实例中找到了这个属性,则搜索结束,返回实例属性的值;若实例上没有找到,则继续向对象的原型对象延伸,搜索对象的原型对象,若在原型对象上找到了,则返回原型上相应属性的值,若没有找到,则返回undefined。因此,实例对象属性会覆盖原型对象上的同名属性,所以上面第二行代码输出的是John。

  • Object.getPrototypeOf(object)方法返回参数对象的原型对象。
  • Object.keys(object)方法返回对象上课枚举的实例属性。

原型中的所有属性都是被所有实例所共享的,这种共享对于函数来说非常合适,对于包含基本值的属性也说的过去(实例属性会覆盖原型同名属性),但对于那些包含引用类型的属性,可有大麻烦了

Student_1.prototype.friends = ['aa','bb'];
console.log('s6的朋友' + s6.friends);
s5.friends.push('cc');
console.log('s5的朋友' + s5.friends);
console.log('s6的朋友' + s6.friends);

运行结果:

js中创建对象的几种方式

问题来了,我们只想改变s5的朋友列表,但由于原型模式的共享本质,s6的朋友列表也随之改变了。

因此,很少单独使用原型模式。

组合使用构造函数和原型模式

构造函数模式用于定义实例属性,原型模式则用于定义方法和共享的属性。这种混合模式不仅支持向构造函数传入参数,还最大限度地节约了内存,可谓是集两模式之长。示例代码如下:

function Student(name,sex,grade){       
 this.name = name;
 this.sex = sex;
 this.grade = grade;
}
Student.prototype.sayName = function(){
 console.log(this.name);
}
Student.prototype.school = 'Joooh school';

其他模式

除了以上几种常见的模式外,批量创建对象的方式还有

  • 动态原型模式:仅在第一次调用构造函数时,将方法赋给原型对象的相应属性,其他示例的处理方式同构造函数模式
  • 寄生构造函数模式:仅仅封装创建对象的代码,然后再返回新创建的对象,仍使用new操作符调用
  • 稳妥构造函数模式:没有公共属性,只有私有变量和方法,以及一些get/set方法,用以处理私有变量。

结语

每种模式都有各自的优缺点,具体要使用哪种,还需结合实际场景,深入理解,灵活运用。

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持三水点靠木!

Javascript 相关文章推荐
JavaScript While 循环基础教程
Apr 05 Javascript
JS 强制设为首页的代码
Jan 31 Javascript
在html页面上拖放移动标签
Jan 08 Javascript
jQuery操作 input type=checkbox的实现代码
Jun 14 Javascript
jQuery :first选择器使用介绍
Aug 09 Javascript
微信小程序 生命周期和页面的生命周期详细介绍
Jan 19 Javascript
微信小程序实现添加手机联系人功能示例
Nov 30 Javascript
解决vue2中使用axios http请求出现的问题
Mar 05 Javascript
npm全局模块卸载及默认安装目录修改方法
May 15 Javascript
JavaScript实现构造json数组的方法分析
Aug 17 Javascript
vue中使用heatmapjs的示例代码(结合百度地图)
Sep 05 Javascript
JavaScript实现切换多张图片
Jan 27 Javascript
js实现增加数字显示的环形进度条效果
Feb 05 #Javascript
JS实现复制内容到剪贴板功能
Feb 05 #Javascript
js实现适合新闻类图片的轮播效果
Feb 05 #Javascript
BootStrap table删除指定行的注意事项(笔记整理)
Feb 05 #Javascript
JS前端开发判断是否是手机端并跳转操作(小结)
Feb 05 #Javascript
thinkjs之页面跳转同步异步操作
Feb 05 #Javascript
js实现5秒倒计时重新发送短信功能
Feb 05 #Javascript
You might like
使用php实现网站验证码功能【推荐】
2017/02/09 PHP
PHP函数按引用传递参数及函数可选参数用法示例
2018/06/04 PHP
jQuery学习笔记 操作jQuery对象 属性处理
2012/09/19 Javascript
json原理分析及实例介绍
2012/11/29 Javascript
js获取元素外链样式的方法
2015/01/27 Javascript
JavaScript实现将数组中所有元素连接成一个字符串的方法
2015/04/06 Javascript
jQuery密码强度检测插件passwordStrength用法实例分析
2015/10/30 Javascript
jQuery解决$符号命名冲突
2016/06/18 Javascript
AngularJS中的API(接口)简单实现
2016/07/28 Javascript
老生常谈JQuery data方法的使用
2016/09/09 Javascript
js实现html table 行,列锁定的简单实例
2016/10/13 Javascript
canvas实现绘制吃豆鱼效果
2017/01/12 Javascript
原生js实现放大镜
2017/02/20 Javascript
老生常谈JavaScript面向对象基础与this指向问题
2017/10/16 Javascript
详解vue-admin和后端(flask)分离结合的例子
2018/02/12 Javascript
Vue2.0实现组件数据的双向绑定问题
2018/03/06 Javascript
常用的 JS 排序算法 整理版
2018/04/05 Javascript
JS中实现隐藏部分姓名或者电话号码的代码
2018/07/17 Javascript
LayerClose弹窗关闭刷新方法
2018/08/17 Javascript
Jquery动态列功能完整实例
2019/08/30 jQuery
简述Vue中容易被忽视的知识点
2019/12/09 Javascript
Python 装饰器深入理解
2017/03/16 Python
基于python中theano库的线性回归
2018/08/31 Python
不知道这5种下划线的含义,你就不算真的会Python!
2018/10/09 Python
Python实现二叉搜索树BST的方法示例
2019/07/30 Python
利用Opencv实现图片的油画特效实例
2021/02/28 Python
纯CSS3实现Material Design效果
2017/03/09 HTML / CSS
将时尚融入珠宝:Adornmonde
2019/10/17 全球购物
美国沃尔玛网上超市:Walmart
2020/08/14 全球购物
在weblogic中发布ejb需涉及到哪些配置文件
2012/01/17 面试题
应届毕业生求职信
2014/05/26 职场文书
动漫设计与制作专业推荐信
2014/07/07 职场文书
2016年优秀团员事迹材料
2016/02/25 职场文书
PyCharm配置KBEngine快速处理代码提示冲突、配置命令问题
2021/04/03 Python
Python机器学习之逻辑回归
2021/05/11 Python
微信小程序实现聊天室功能
2021/06/14 Javascript