js中对象与对象创建方法的各种方法


Posted in Javascript onFebruary 27, 2019

前言

不管是哪门语言,千变万化不离其宗,深入理解其本质,方能应用自如。对应到js,闭包,原型,函数,对象等是需要花费大功夫思考、理解的。

这一次我们来说一说在JavaScript中经常会用到的一个复杂基本类型,对象,先从对象的属性讲起,再讲对象的创建方法,基本涵盖了创建对象的各种方法,大家一起学习呀~

一、对象

要掌握对象的使用及继承,首先当然需要先理解它,接下来,将会对对象的属性类型进行一个整理

    1、什么是对象

    对象其实是无序属性的集合,其属性可以包含基本值,对象或者函数,比如像下面这个例子就是一个person对象啦

var person = {
 name: "NIcholas",
 age: 29,
 sayName: function() {
 console.log(this.name);
 }
}

    从上面的例子我们可以看到,对象可以是由属性和其相应的值构成,对象中可以包含函数,也可以包含其它对象

    2、属性类型

    在JavaScript中,其实有两种属性,包括数据属性和访问器属性

    (1)数据属性

    数据属性包含一个数据值的位置,在这个位置可以读取和写入值,一般来说,有4个描述其行为的特性:

    a、[[Configurable]]:表示能否通过delete删除属性从而重新定义属性,默认值为true

    b、[[Enumerable]]:表示能否通过for-in循环返回属性,默认值为true

    c、[[Writable]]:表示能否修改属性的值,默认值为true

    d、[[Value]]:包含这个属性的数据值,默认值为undefined

    一般来说,数据属性都有自己的默认值,那么如果我们要修改数据属性默认的特性,应该怎么办呢?这个时候就需要用到Object,defineProperty()方法啦,这个方法接收三个参数:属性所在的对象,属性的名字和一个描述对象,来看下面的例子

var person = {};
Object.defineProperty(person, "name", {
 writable: false,
 value: "Nicholas"
});

console.log(person.name); // Nicholas
// 重新赋值
person.name = "Greg";
console.log(person.name); // Nicholas

    从上面的例子我们可以看到,因为设置了person对象的name属性为不可修改,因此无论你在后面怎么修改person的name属性的值,name属性的值都不会发生改变

    (2)访问器属性

    访问器属性不包含数据值,它们包含一对儿getter和setter函数,在读取访问器属性时,会调用getter函数,这个函数负责返回有效的值,在写入访问器属性时,会调用setter函数并传入新值,这个函数负责决定如何处理数据,访问器属性有以下4个特性

    a、[[Configurable]]:表示能否通过delete删除属性从而重新定义属性,默认值为true

    b、[[Enumerable]]:表示能否通过for-in循环返回属性,默认值为true

    c、[[Get]]:在读取属性时调用的函数,默认值为undefined

    d、[[Set]]:在写入属性时调用的函数,默认值为undefined

    访问器属性不能直接定义,必须调用Object.definedProperty()来定义的,来看下面的例子

var book = {
 _year:2004,
 edition:1
}

Object.defineProperty(book, "year", {
 get: function() {
 return this._year;
 },
 set: function(newValue) {
 this._year = newValue;
 }
});

    这里要说明一下,book对象中_year前面的下划线是一种常用的记号,用于表示只能通过对象方法访问的属性,还有呀,大家不要小看了Object.definedProperty()这个方法,这个方法可是很强大呀,像vue的双向数据绑定,其实就是用到了这个方法去实现的

    (3)读取属性的特性

    既然JavaScript有数据属性和访问器属性,那么我们怎样才能读取它们呀,这个时候就需要用到Object.getOwnPropertyDescriptor()方法了,这个方法可以取得给定属性的描述符,接收两个参数,分别是属性所在的对象和要读取其描述符的属性名称

二、对象的创建

    在了解了对象之后,接下来我们就需要说下怎么创建对象了,最简单的方法,当然就是使用前面说的对象字面量的方法去创建啦,但是如果我们需要创建好多个对象,用前面的方法就不行了,我们需要用到其它更加简便的方法,帮助我们创建出多个对象

    1、工厂模式

    这种模式其实是一个设计模式,抽象了创建具体对象的过程,主要是通过在函数内部创建一个对象,为其添加属性和方法,并将对象返回,从而实现创建多个对象的目的,来看下面的例子

function createPerson(name, age) {
 var o = new Object();
 o.name = name;
 o.age = age;
 o.sayName = function() {
  console.log(this.name);
 };
 return o;
}

var person1 = createPerson("Nicholas", 29);
var person2 = createPerson("Greg", 27);

    优点:能够解决创建多个对象的问题,兼容各个浏览器

    缺点:没有解决对象识别的问题,不能知道一个对象的类型

    2、构造函数模式

    这种模式主要通过创建自定义的构造函数,从而定义自定义对象类型的属性和方法,来看下面例子

function Person(name, age) {
 this.name = name;
 this.age = age;
 this.sayName = function() {
  console.log(this.name);
 }
}

var person1 = new Person("Nicholas", 29);
var person2 = new Person("Greg", 27);

    这里要说明一下,构造函数需要以一个大写字母开头,而非构造函数应该以一个小写字母开头,这个主要是为了区别构造函数和其它函数,构造函数其实本身也是函数,只是用来创建对象而已

    其中,要创建Person的新实例,需要使用new操作符,其实这里会经过4个步骤,首先,将会创建一个新对象,接着会将构造函数的作用域赋给新对象,this指向了这个对象,接着会执行构造函数中的代码,为这个新对象添加属性,最后会返回新对象

    优点:可以创建多个对象,解决对象的识别问题

    缺点:每个实例都会创建不同的function实例,而其实创建完成同样任务的function实例是很没有必要的

    这里还是要说明一下,对象类型的检测需要用到instanceof操作符,比如像上面的例子可以用下面的方法检测

console.log(person1 instanceof Person); // true
console.log(person2 instanceof Person); // true

    使用构造函数模式可以解决对象的识别问题,而这也是工厂模式无法办到的

    3、原型模式

    我们都知道,每个函数都有一个prototype属性,这个属性是一个指针,指向一个对象,而这个对象就是原型对象,包含了所有实例共享的属性和方法,如果我们要创建的对象需要共享属性和方法,就可以使用这种方法创建

function Person() {
}

Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.sayName = function() {
 console.log(this.name);
}

var person1 = new Person();
var person2 = new Person();
person1.sayName(); // Nicholas
person2.sayName(); // Nicholas

    优点:不用为构造函数传递参数,可以创建多个相同的对象

    缺点:原型中的属性被很多实例共享,当属性为包含引用类型值的属性时,修改一个实例中属性的值,另一个实例中的属性的值也会改变

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

    通过前面的分析,我们应该可以看到构造函数模式和原型模式的优点和缺点啦,构造函数可以创建多个不同属性值的对象,原型模式可以用于定义方法和共享的属性,我们可以将这两种模式结合起来,这种模式现在是使用最广泛的一种模式啦

function Person(name, age) {
 this.name = name;
 this.age = age;
}

Person.prototype = {
 constructor: Person,
 sayName: function() {
  console.log(this.name);
 }
}

var person1 = new Person("Nicholas", 29);
var person2 = new Person("Greg", 27);

person1.sayName(); // Nicholas
person2.sayName(); // Greg
person1.sayName === person2.sayName; // true

    从上面的例子我们可以看到,使用这种模式创建对象,每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用,最大限度地节省了内存,另外,这种混成模式还支持向构造函数传递参数,可谓是集两种模式之长 

    5、动态原型模式

    这种模式主要是将所有信息都封装在了构造函数里,因为在组合构造函数模式和原型模式中,构造函数和原型模式是独立的,有些开发人员会感到很困惑,因此,这种模式也是为了解决这个问题,通过在构造函数中初始化初始化原型,又保持了同时使用构造函数和原型的优点,换句话说,就是可以通过在构造函数中,检查某个应该存在的方法是否有效,来决定是否需要初始化原型

function Person(name, age) {
 this.name = name;
 this.age = age;
 if(typeof this.sayName != "function") {
  Person.prototype.sayName = function() {
   console.log(this.name);
  }
 }
}

var person1 = new Person("Nicholas", 29);
person1.sayName(); // Nicholas

    这里要说明一下,在if语句中的代码,只有在首次调用构造函数时才会执行,之后原型已经得到初始化,不需要再做什么修改了

    6、寄生构造函数模式

    这种模式其实和工厂模式很像,除了使用new操作符并把使用的包装函数叫做构造函数之外,和工厂模式可以说是一模一样的,这种模式的基本思想是创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的对象

function Person(name, age) {
 var o = new Object();
 o.name = name;
 o.age = age;
 o.sayName = function() {
  console.log(this.name);
 }
 return o;
}

var person1 = new Person("Nicholas", 29);
person1.sayName(); // Nicholas

    寄生构造函数模式和工厂模式真的是很像,那么既然有了工厂模式,为什么还要有寄生构造函数模式呢?其实这个模式主要是用来给js原生的构造函数定义一些新的方法,我们可以看下面这个例子

function SpecialArray() {
 var values = new Array();
 values.push.apply(values, argumens);
 values.toPipedString = function() {
  return this.join("|");
 }
 return values;
}

var colors = new SpecialArray("red","blue");
console.log(colors.toPipedString()); // red|blue

    从上面这个例子我们可以看到,我们在构造函数里面创建了一个新的数组,然后通过push方法初始化这个数组,并且又给数组的实例添加了一个toPipedString方法,并且将所创建的数组返回,因此呢,当我们通过new创建了SpecialArray实例时,其实就得到增加了新方法的数组实例啦,就可以在这个实例上使用我们添加的新的方法toPipedString

    7、稳妥构造函数模式

    在说这种模式之前,要先说一下稳妥对象,稳妥对象就是指没有公共属性,而且其方法也不引用this的对象,稳妥对象最适合在一些安全的环境中,或者防止数据被其他应用程序改动时使用,稳妥构造函数模式遵循与寄生构造函数类似的模式,但是还是有下面的不同,第一个是新创建的对象实例方法不引用this,第二个是不使用new操作符调用构造函数

function Person(name, age) {
 var o = new Object();
 _name = name;
 _age = age;
 o.sayName = function() {
  return _name;
 }
 return o;
}

var person1 = new Person("Nicholas", 29);
person1.sayName(); // Nicholas

    在上面这个例子中,我们在构造函数中创建了一个对象后,可以继续添加一些私有的变量和函数,要修改这些私有的变量和函数,只能通过创建的对象的方法进行访问,这里在对象上创建的方法其实可以看作就是公有方法,比如说这里的_name和_age就是私有变量,而对象o的sayName方法就是访问私有变量的公有方法了,这里除了调用sayName()方法外,没有其它方法可以访问其数据成员

    8、es6中创建对象的方法

    最后要来说下es6中创建对象新增的方法啦,在es6中,引入了 Class(类)这个概念,作为对象的模板,通过class关键字,可以定义类,基本上,ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已

class Person {
 constructor(name, age) {
  this.name = name;
  this.age = age;
 }
 sayName() {
  console.log(this.name);
 }
}

var person1 = new Person("Nicholas", 29);
person1.sayName(); // Nicholas

    上面这个例子其实就是组合构造函数模式和原型模式的改写,其中,constructor属性直接指向类本身,该方法会默认返回实例对象,在里面定义的属性和方法都是实例本身具有的方法,不是其它实例共享的,而像sayName方法就是定义在原型上的方法了,是所有实例一起共享的

    好啦,今天就介绍到这里了,不知道大家对对象和对象的创建是否有了一个比较详细的了解了呢

总结

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

Javascript 相关文章推荐
CSS鼠标响应事件经过、移动、点击示例介绍
Sep 04 Javascript
js实现的复制兼容chrome和IE
Apr 03 Javascript
Javascript前端UI框架Kit使用指南之kitjs的对话框组件
Nov 28 Javascript
js实现简单选项卡与自动切换效果的方法
Apr 10 Javascript
JavaScript基础篇(3)之Object、Function等引用类型
Nov 30 Javascript
jQuery实现页面评论栏中访客信息自动填写功能的方法
May 23 Javascript
基于JS模仿windows文件按名称排序效果
Jun 29 Javascript
AngularJS Ajax详解及示例代码
Aug 17 Javascript
EasyUI创建对话框的两种方式
Aug 23 Javascript
Angularjs中controller的三种写法分享
Sep 21 Javascript
Kendo Grid editing 自定义验证报错提示的解决方法
Nov 18 Javascript
JavaScript高阶教程之“==”隐藏下的类型转换
Apr 11 Javascript
vue router带参数页面刷新或回退参数消失的解决方法
Feb 27 #Javascript
react的滑动图片验证码组件的示例代码
Feb 27 #Javascript
原生JS forEach()和map()遍历的区别、兼容写法及jQuery $.each、$.map遍历操作
Feb 27 #jQuery
js/jquery遍历对象和数组的方法分析【forEach,map与each方法】
Feb 27 #jQuery
spring+angular实现导出excel的实现代码
Feb 27 #Javascript
react native 原生模块桥接的简单说明小结
Feb 26 #Javascript
JavaScript实现获取两个排序数组的中位数算法示例
Feb 26 #Javascript
You might like
PHP开发框架kohana3 自定义路由设置示例
2014/07/14 PHP
thinkPHP中分页用法实例分析
2015/12/26 PHP
php操作mongodb封装类与用法实例
2018/09/01 PHP
JavaScript与DOM组合动态创建表格实例
2012/12/23 Javascript
jquery验证手机号码、邮箱格式是否正确示例代码
2013/07/28 Javascript
禁用Tab键JS代码兼容Firefox和IE
2014/04/18 Javascript
javascript模拟评分控件实现方法
2015/05/13 Javascript
浅谈javascript中基本包装类型
2015/06/03 Javascript
详解JavaScript中的forEach()方法的使用
2015/06/08 Javascript
Jquery鼠标放上去显示全名的实现方法
2017/02/06 Javascript
每个 JavaScript 工程师都应懂的33个概念
2018/10/22 Javascript
如何手动实现es5中的bind方法详解
2018/12/07 Javascript
mockjs+vue页面直接展示数据的方法
2018/12/19 Javascript
深入剖析JavaScript instanceof 运算符
2019/06/14 Javascript
webpack4.0+vue2.0利用批处理生成前端单页或多页应用的方法
2019/06/28 Javascript
js与jquery获取input输入框中的值实例讲解
2020/02/27 jQuery
原生js实现点击按钮复制内容到剪切板
2020/11/19 Javascript
vue3使用vue-count-to组件的实现
2020/12/25 Vue.js
[52:40]完美世界DOTA2联赛PWL S2 Magma vs GXR 第一场 11.29
2020/12/02 DOTA
[51:06]DOTA2-DPC中国联赛 正赛 Elephant vs Aster BO3 第二场 1月26日
2021/03/11 DOTA
基于Python-turtle库绘制路飞的草帽骷髅旗、美国队长的盾牌、高达的源码
2021/02/18 Python
马来西亚领先的在线礼品店:Giftr
2018/08/23 全球购物
C#软件工程师英语面试题
2015/06/07 面试题
linux系统都有哪些运行级别
2016/03/26 面试题
质检部职责
2013/12/28 职场文书
大学生职业生涯规划书汇总
2014/03/20 职场文书
建筑院校毕业生求职信
2014/06/13 职场文书
专题组织生活会方案
2014/06/15 职场文书
舞蹈兴趣小组活动总结
2014/07/07 职场文书
党员学习中共十八大报告思想汇报
2014/09/15 职场文书
《悬崖边的树》读后感2篇
2019/12/02 职场文书
利用Nginx代理如何解决前端跨域问题详析
2021/04/02 Servers
CSS几步实现赛博朋克2077风格视觉效果
2021/06/16 HTML / CSS
Java spring单点登录系统
2021/09/04 Java/Android
使用Docker容器部署rocketmq单机的全过程
2022/04/03 Servers
Spring Boot 实现 WebSocket
2022/04/30 Java/Android