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 相关文章推荐
文字幻灯片
Jun 26 Javascript
javascript动态加载二
Aug 22 Javascript
JS 实现获取打开一个界面中输入的值
Mar 19 Javascript
js弹出层包含flash 不能关闭隐藏的2种处理方法
Jun 17 Javascript
javascript 面向对象封装与继承
Nov 27 Javascript
JS实现带提示的星级评分效果完整实例
Oct 30 Javascript
Angular.js中下拉框实现渲染html的方法
Jun 18 Javascript
JS获取input[file]的值并显示在页面的实现方法
Mar 09 Javascript
详解Vue基于 Nuxt.js 实现服务端渲染(SSR)
Apr 05 Javascript
react在安卓中输入框被手机键盘遮挡问题的解决方法
Sep 03 Javascript
js监听html页面的上下滚动事件方法
Sep 11 Javascript
微信小程序基于picker实现级联菜单
Feb 15 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下实现折线图效果的代码
2007/04/28 PHP
PHP 日常开发小技巧
2009/09/23 PHP
PHP获取当前所在目录位置的方法
2014/11/26 PHP
PHP CURL中传递cookie的方法步骤
2019/05/09 PHP
javascript CSS画图之基础篇
2009/07/29 Javascript
通过下拉框的值来确定输入框是否可以为空的代码
2011/10/18 Javascript
如何用JavaScript定义一个类
2014/09/12 Javascript
jQuery多级弹出菜单插件ZoneMenu
2014/12/18 Javascript
jQuery 3.0十大新特性最终版发布
2016/07/14 Javascript
JavaScript实现审核流程状态的动态显示进度条
2017/03/15 Javascript
Javascript中的getter和setter初识
2017/08/17 Javascript
js截取字符串功能的实现方法
2017/09/27 Javascript
vue+iview写个弹框的示例代码
2017/12/05 Javascript
BootStrap自定义popover,点击区域隐藏功能的实现
2018/01/23 Javascript
浅析Vue中method与computed的区别
2018/03/06 Javascript
JavaScript中引用vs复制示例详析
2018/12/06 Javascript
Vue项目前后端联调(使用proxyTable实现跨域方式)
2020/07/18 Javascript
[02:51]2018年度DOTA2最佳中单位选手-完美盛典
2018/12/17 DOTA
Python中正则表达式的详细教程
2015/04/30 Python
Pandas DataFrame 取一行数据会得到Series的方法
2018/11/10 Python
pycharm运行scrapy过程图解
2019/11/22 Python
python通过nmap扫描在线设备并尝试AAA登录(实例代码)
2019/12/30 Python
从训练好的tensorflow模型中打印训练变量实例
2020/01/20 Python
Python常用GUI框架原理解析汇总
2020/12/07 Python
CSS3 制作绽放的莲花采用效果叠加实现
2013/01/31 HTML / CSS
Ted Baker美国官网:英国时尚品牌
2018/10/29 全球购物
Nike瑞典官方网站:Nike.com (SE)
2018/11/26 全球购物
Michael Kors英国官网:美国奢侈品品牌
2019/11/13 全球购物
社团文化节策划书
2014/02/01 职场文书
合作意向书格式及范文
2014/03/31 职场文书
法人代表授权委托书
2014/04/08 职场文书
2014年销售工作总结与计划
2014/12/01 职场文书
初三毕业评语
2014/12/26 职场文书
2015年发展党员工作总结报告
2015/03/31 职场文书
Pytorch中的学习率衰减及其用法详解
2021/06/05 Python
HTML页面中使两个div并排显示的实现
2022/05/15 HTML / CSS