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 相关文章推荐
对table和ul实现js分页示例分享
Feb 24 Javascript
jquery获取颜色在ie和ff下的区别示例介绍
Mar 28 Javascript
JavaScript中的原型和继承详解(图文)
Jul 18 Javascript
JS设置网页图片vspace和hspace属性的方法
Apr 01 Javascript
javascript中Function类型详解
Apr 28 Javascript
JavaScript获取两个数组交集的方法
Jun 09 Javascript
javascript 常见功能汇总
Jun 11 Javascript
基于angular实现三级联动的生日插件
May 12 Javascript
深入理解Vue 的条件渲染和列表渲染
Sep 01 Javascript
Angular4实现图片上传预览路径不安全的问题解决
Dec 25 Javascript
关于Vue Router中路由守卫的应用及在全局导航守卫中检查元字段的方法
Dec 09 Javascript
Vue Router的手写实现方法实现
Mar 02 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加入ftp扩展的解决方法
2013/02/07 PHP
php建立Ftp连接的方法
2015/03/07 PHP
thinkPHP中验证码的简单使用方法
2015/12/26 PHP
phpmyadmin在宝塔面板里进不去的解决方案
2020/07/06 PHP
PHP字符串和十六进制如何实现互相转换
2020/07/16 PHP
使Ext的Template可以解析二层的json数据的方法
2007/12/22 Javascript
js DataSet数据源处理代码
2010/03/29 Javascript
js函数的引用, 关于内存的开销
2012/09/17 Javascript
javascript控制在光标位置插入文字适合表情的插入
2014/06/09 Javascript
JS转换HTML转义符的方法
2016/08/24 Javascript
基于jQuery的checkbox全选问题分析
2016/11/18 Javascript
js微信扫描二维码登录网站技术原理
2016/12/01 Javascript
利用VUE框架,实现列表分页功能示例代码
2017/01/12 Javascript
Angular.JS中的this指向详解
2017/05/17 Javascript
微信小程序之电影影评小程序制作代码
2017/08/03 Javascript
详解layui中的树形关于取值传值问题
2018/01/16 Javascript
Vue 中可以定义组件模版的几种方式
2019/08/06 Javascript
JavaScript Canvas编写炫彩的网页时钟
2019/10/16 Javascript
简单了解前端渐进式框架VUE
2020/07/20 Javascript
vue 实现setInterval 创建和销毁实例
2020/07/21 Javascript
[01:06:30]DOTA2-DPC中国联赛定级赛 Phoenix vs DLG BO3第二场 1月9日
2021/03/11 DOTA
python中wx将图标显示在右下角的脚本代码
2013/03/08 Python
numpy中的ndarray方法和属性详解
2019/05/27 Python
如何使用Python标准库进行性能测试
2019/06/25 Python
Python基于模块Paramiko实现SSHv2协议
2020/04/28 Python
Big Green Smile法国:领先的英国有机和天然产品在线商店
2021/01/02 全球购物
如何安装ruby on rails
2014/02/09 面试题
晚归检讨书
2014/02/19 职场文书
公司新年寄语
2014/04/04 职场文书
班级文化标语
2014/06/23 职场文书
新闻发布会活动策划方案
2014/09/15 职场文书
秋季校运会广播稿100字
2014/09/18 职场文书
2014国庆黄金周超市促销活动方案
2014/09/21 职场文书
西柏坡导游词
2015/02/05 职场文书
2015年乡镇信访工作总结
2015/04/07 职场文书
Flutter Navigator 实现路由传递参数
2022/04/22 Java/Android