全面了解JavaScript对象进阶


Posted in Javascript onJuly 19, 2016

要了解JavaScript对象,我们可以从对象创建、属性操作、对象方法这几个方面入手。概括起来,包括以下几模块:  

1.创建对象

1.1 对象直接量

对象直接量是创建对象最简单的方式,由若干名/值对组成映射表:

var point = {x: 0, y: 0 };

属性名也没有什么限制,可以是js的关键字或者任意字符串,如果是这两种情况,属性需要用双引号引起来:

var empty = {};
va point = {x: 0, y: 0 };
var book = {
"main title": "Javascript",
"sub-title": "The definitive Guide",
"for": "all audience",
author: {
firstName: "Davide",
lastName: "Flanagan"
}
};

对象直接量创建对象十分简单,但一般都不会这样使用。代码可复用性低,如果想要在其他地方使用该对象并且属性值不一样,那这么办?是不是又得重新创建一份代码?

1.2 通过new创建对象

通过new创建对象之前,先要创建一个函数,new把这个函数当做构造函数(constructor)。例如通过new创建一个Person对象:

function Person(){
//构造函数
}

var person = new Person();

Javscript语言核心中的原始类型都包含内置构造函数:

var a = new Array();
var d = new Date();
var r = new RegExp(“js”);

1.3 Object.create()

在了解Object的create方法之前,我们想看看什么是原型。每一个Javascript对象(null除外)都和另一个对象相关联。“另一个”对象就是我们所说的原型。每一个对象都从原型继承属性。

所有通过对象直接量创建的对象都具有同一个原型对象Object.prototype。关键字new和构造函数创建的对象原型就是构造函数的prototype属性的值。通过new Array()创建对象的原型为Array.prototype,通过new Date()创建的对象原型为Date.prototype。原型暂介绍到这里。

Object.create方法包含两个参数,第一个参数是对象的原型,第二个参数可选,用于描述对象属性。使用很简单,只需传入所需的原型对象即可:

var o1 = Object.create({x: 1, y: 2 }); //原型为Object.prototype

如果想创建一个没有原型的对象,可通过传入null作为参数。这样创建的对象不会继承任何属性,也没有像toString这样的方法:

var o2 = Object.create(null); //没有原型

如果想创建一个普通的空对象,直接传入Object.prototype:

var o3 = Object.create(Object.prototype);

如果是自定义的对象,和创建空对象一样。直接传入对象名.prototype:

function Person(){
}
var o4 = Object.create(Person.prototype);

2.属性管理

2.1 属性查询和设置

对象的属性可通过点(.)或方括号([])运算符获取。如果使用点获取属性,属性名必须是简单的表示符。不能是保留字,比如,o.for或者o.class。

ar author = book.author; //正确
var name = author.surname; //正确
var title = book[“main title”]; //正确
var className = book.class; //错误

object[“property”]这种语法看起来更像数组,只是这个数组的元素是通过字符串索引而不是数字索引。这种数组就是我们所说的关联数组,也称为散列、映射或字典。Javascript对象都是关联数组。

既然对象是关联数组,那么Javascript也为我们提供了属性的遍历方式for/in。下面的例子利用for/in计算portfolio的总计值:  

function getvalue(portfolio){
  var total = 0.0;
  for(stock in portolio){
    var shares = portolio[stock];
    var price = getquote(stock);
    total += shares * price;
  }

  return total;
}

继承:Javascript对象具有自有属性(own property),也有一些属性是从原型对象继承而来。我们先看看一个实现继承功能的函数inherit:

function inherit(p){
  if (p == null) throw TypeError(); //p是一个对象,大不能是null
  if(Object.create){
    return Object.create(p); //直接使用Object.create方法
  }
  var t = typeof p;
  if(t !== "object" && t !== "function") throw TypeError();
  function f() {};
  f.prototype = p; //将其原型属性设置为p
  return new f();
}

假设要查询对象o的属性x,如果o中不存在x,将会继续在o的原型对象中查询属性x。如果原型对象中也没有x,但这个原型对象也有原型,那么继续在这个原型对象的原型上执行查询,直到找到x或者查询到一个原型为null的对象为止。

var o = {}; //o从Object.prototype继承对象属性
o.x = 1; //给o定义x属性
var p = inherit(o); //p继承o和Object.prototype
p.y = 2; //p定义属性y
var q = inherit(p); //q继承p、o和Object.prototype
q.z = 3; //给q定义属性z
var s = q.toString(); //toString继承自Object.prototype
q.x + q.y // => 3:x和y分别继承自o和p

2.2 删除属性

delete运算符可以删除对象的属性:

delete book.author;
delete book[“main title”];

delete只能删除自有属性,不能删除继承属性。要删除继承属性,必须从定义这个属性的原型对象上删除它,而且这会影响到所有的继承自这个原型的对象。删除成功会返回true。

ar o = {x: 1};
delete o.x; //删除x,返回true
delete o.x; //x已经不存在了,什么都没做,返回true。
delete o.toString; //什么都没做,返回true。
delete不能删除可配置型为false的属性。某些内置对象的属性是不可配置的,比如通过变量声明和函数声明创建的全局对象的属性:
delete Object.prototype //不能删除,属性是不可配置的
var x = 1;
delete this.x; //不能删除这个属性
function f() {}
delete this.f; //不能删除全局函数

2.3 检测属性

判断某个属性是否存在于某个对象中,可通过in运算符、hasOwnProperty()和propetyIsEnumerable()方法来检测。

in运算符:运算符左侧是属性名,右侧是对象。如果对象的自有属性或者继承属性包含属性则返回true:

var o = {x: 1};
"x" in o; //true:x是o的属性
"y" in o; //false:y不是o的属性
"toString" in o; //true:o继承toString属性

hasOwnProperty()方法:检测给定的名字是否是对象的自有属性。对于继承属性它将返回false:

var o = {x: 1};
o.hasOwnProperty("x"); //true:o有一个自由属性x
o.hasOwnProperty("y"); //false:o中不存在属性y
o.hasOenProperty("toString"); //false:toString是继承属性

propertyIsEnumerable()方法:是hasOwnProperty的增强版,只有检测到自有属性并且这个属性是可枚举行为true时才返回true:

var o = inherit({y: 2});
o.x = 1;
o.propertyIsEnumerable("x"); //true: o有一个可枚举属的自有属性x
o.propertyIsEnumerable("y"); //false:y是继承来的
Object.prototype.propertyIsEnumerable("toString"); //false:不可枚举

2.4 枚举属性

通常使用for/in循环遍历对象属性,遍历的属性包括自有属性和继承属性。对象继承的内置方法是不可枚举的,但在代码中给对象添加的属性都是可枚举的。例如:

var o = {x: 1, y: 2, z: 3}; //三个可枚举的自有属性
o.propertyIsEnumeable("toString"); //false,不可枚举
for (p in o) //遍历属性
  console.log(p); //输出x、y和z,不会输出toString

有时候我们只想遍历自有属性,并且属性不为函数:

for(p in o){
  if(!o.hasOwnProperty(p)) continue;
  if(typeof o[p] === "function") continue;
}

我们可通过枚举遍历功能实现可枚举属性的复制:

/*
* 把p中的可枚举属性复制到o中,并返回o
* 如果o和p含同名属性,则覆盖o中的属性
* 这个函数并不处理getter和setter以及复制属性
*/
function extend(o, p){
  for(prop in p){ //遍历p中的所有属性
    o[prop] = p[prop]; //将属性添加到o中
  }
  return o;
}

ES5定义了两个用以枚举属性名称的函数。第一个是Object.keys(),返回由对象中可枚举属自有属性名称组成的数组。第二个枚举函数是Object.getOwnPropertyNames(),和Object.keys()类似,它返回对象的所有自有属性,而不仅仅是可枚举属性。

3.属性封装

3.1 属性getter和setter 

对象属性由名字、值和一组特性(attribute)构成的。在ES5中,属性值可以用一个或两个方法替代,这两个方法就是getter和setter。由getter和setter定义的属性称做“存取器属性”,它不同于“数据属性”,数据属性只有一个简单的值。

和数据属性不同,存取器属性不具有可写性(writeable atribute)。如果属性同时具有getter和setter方法,那么它是一个读/写属性。如果它只有getter方法,那么它是一个只读属性,如果它只有setter方法,那么它是一个只写属性。读取只写属性总是返回undefined。

存取器属性定义语法也比较简单,函数定义没有使用function关键字,而是使用get或set:

var o = {
  //普通的数据属性
  data_prop: 1,
  //存取器属性都是成对定义的函数
  get accessor_prop(){/* 这里是函数体 */},
  set accessor_prop(value){}
};

思考下面这个表示2D笛卡尔点坐标的对象。它有两个普通属性x和y分别表示x坐标和y坐标,它还有两个等价的存取器属性用来表示点的极坐标:

var p = {
  //x和y是普通的可读写数据属性
  x: 1.0,
  y: 1.0,
  //r是可读写的存取器属性,它有getter和setter
  get r(){return Math.sqrt(this.x * this.x + this.y * this.y); },
  set r(newValue){
  var oldValue = Math.sqrt(this.x * this.x + this.y * this);
  var ratio = newValue / oldValue;
  this.x *= ratio;
  this.y *= ratio;
  },
  //theta是只读存取器属性,只有getter方法
  get theta() { return Math.atan2(this.y, this.x); }
};

和数据属性一样,存取器属性是可以继承的,因此可以将上述代码中的p对象当做另一个“点”的原型。可以给性对象定义它的x和y属性,但r和theta属性继承而来:

var q = inherit(p);
q.x = 1, q.y = 1;
console.log(q.r);
cosole.log(q.theta);

3.2 属性特性

我们可以将存取器属性的getter和setter方法看成属性的特性。按照这个逻辑,我们也可把属性的值同样看着属性的特性。因此,可以认为一个属性包含一个名字和4个特性。

数字属性的4个特性分别是它的值(value)、可写性(writeable)、可枚举性(enumerable)和可配置型(configurable)。

存取器属性不具有值(value)特性和可写性,因此包含:读取(get)、写入(set)、可枚举性、可配置性。

ES5定义了一个名为“属性描述符”的对象,这个对象代表那4个特性。数据属性的描述符对象的属性有value、writable、enumerable和configurable。存取器属性的描述符对象则用get属性和set属性代替value和writable。其中writable、enumerable、configurable都是布尔值,get属性和set属性是函数值。

通过调用Object.getOwnPropertyDescriptor()可以获取某个对象特定属性的属性描述符:

//返回{value: 1, writable: true, enumerable: true, configurable: true}
Object.getOwnProeprtyDescriptor({x: 1},"x");
//查询上文中定义的random对象的octet属性
//返回{get: /*func */, set: undefined, enumerable: true, configurable: true}
Object.getOwnPropertyDesciptor(random, "octet");

//对于继承属性和不存在属性,返回undefined
Object.getOwnPropertyDesciptor({}, "x");
Object.getOwnPropertyDesciptor({}, "toString");

从函数名就可以看出,Object.getOwnPropertyDesciptor()只能得到自有属性的描述符。要想获得继承属性的特性,需要遍历原型链(Object.getPrototypeOf())。

想要设置属性的特性,或者让新建属性具有某些特性,则需要调用Object.defineProperty(),包含三个参数:对象、属性名、属性描述符对象:

// 属性是存在的,但不可枚举
o.x; //=> 1
Object.keys(o) //=> []
//现在对属性x做修改,让它变成只读
Object.defineProperty(o, "x", {writable: true });
//视图更改这个属性的值
o.x = 2; //操作失败但不报错,而在严格模式中抛出类型错误异常
//属性依然是可配置的,因此可通过这种方式对它进行修改:
Object.defineProperty(o, "x", {value: 2 });
o.x //=> 2
//现在将x从数据属性修改为存取器属性
Object.defineProperty(o, "x", { get: function() {return 0;} });
o.x // => 0

 如果要同时修改或创建多个属性,则需要使用Object.defineProperties()。第一个参数是要修改的对象,第二个参数㐊一个映射表。例如:

var p = Object.defineProperties({}, {
  x: { value: 1, writable: true, enumerable: true, configurable: true},
  y: { value: 2, writable: true, enumerable: true, configurable: true},
  r: {
    get: function(){ return Math.sqrt(this.x * this.x + this.y * this.y); },
    enumerable: true,
    configurable: true
  }
});

getter和setter的老式API: 在ES5采纳之前,大多数Javascript的实现已经可以支持对象直接量语法中get和set写法。这些实现提供了非标准的老式API用来查询和设置getter和setter。这些API由四个方法组成,所有对象都拥有这些方法。

__lookupGetter__()和__lookupSetter__()用以返回一个命名属性的getter和setter方法。

__defineGetter__()和__defineSetter__()用以定义getter和setter,第一个参数是属性名字,第二个参数是getter和setter方法。  

var o = {};
o.__defineGetter__("x", function(){return 0;});
o.__defineSetter__("y", function(value){console.log("set value:" + value);});

4.对象的三个属性

每一个对象都有与之相关的原型(prototype)、类(class)、可扩展性(extensible attribute)。接下来讲述这些属性有什么作用。

4.1 原型属性

对象的原型属性是用来继承属性的,我们经常把“o的原型属性”直接叫做“o的原型”。在之前“创建对象”介绍了三种方式创建对象。通过对象直接量创建的对象使用Object.prototype作为它们的原型。通过new创建的对象使用构造函数的prototype属性作为它们的原型。通过Object.create()创建的对象使用第一个参数作为它们的原型。

在ES5中,可通过Object.getPrototypeOf()查询对象原型。在ES3中,没有与之等价的函数,而是使用表达式o.constructor.prototype检查对象的原型。

要想检测一个对象是否是另一个对象的原型(或处于原型链中),使用isPrototypeOf()方法。例如,可以通过p.isPrototypeOf(o)来检测p是否是o的原型:

var p = {x: 1}; //定义一个原型对象
var o = Object.create(p); //使用这个原型创建一个对象
p.isPrototypeOf(o); //=> true,o继承自p
Object.prototype.isPrototypeOf(o) //=> true, p继承自Object.prototype

Mozilla实现的Javascript对外暴露了一个专门命名为__proto__属性,用以直接查询/设置对象原型。但IE和Opera不支持__proto__属性,所以不建议直接使用__proto__属性。

4.2 类属性

对象的类属性是一个字符串,用以表示对象的类型信息。ES3和ES5都为提供设置这个属性的方法,只有一种间接方式查询它。默认的toString()方法返回这种格式的字符串:[object class]。

可通过调用toString()方法,然后提取已返回字符串的第八个到倒数第二个位置之间的字符。但有个麻烦是,很多对象继承的toString()方法重写了,为了能够调用正确的toString()版本,必须间接调用Function.call()方法。下面例子的classof函数可返回任意对象的类:

function classof(o){
  if(o === null) return "Null";
  if(o === undefined) return "Undefined";
  return Object.prototype.toString.call(o).slice(8, -1);
}

4.3 可扩展性

对象的可扩展性用以表示是否可以给对象添加新属性。所有内置对象和自定义对象都是显式可扩展的。在ES5中,可将对象转换为不可扩展的。

Object.seal()方法除了能够将对象设置为不可扩展的,还可以将对象的所有自有属性都设置为不可配置的。也就是说,不能给对象添加新属性,而且已有属性也不能删除和配置。

Object.isSealed()方法用来检测对象是否封闭。

Object.freeze()方法将更严格的锁定对象,除了拥有Object.seal()方法的功能外,还可以将自有的所有数据属性设置为只读(如果对象的存取器属性有setter方法,存取器属性不受影响, 仍可以通过给属性赋值调用它们)。

Object.isFrozen()用来检测对象是否冻结。

5.序列化对象

对象序列化是指将对象的状态转换为字符串,也可以将字符串还原为对象。ES5提供了内置函数JSON.stringify()和JSON.parse()用来序列化和还原Javascript对象。这些方法都使用JSON作为数据交换格式。例如:

o = {x: 1, y: {z: [false, null, ""]}}; //定义一个测试对象
s = JSON.stringify(o); //{"x":1,"y":{"z":[false,null,""]}}
p = JSON.parse(s); //p是o的深拷贝

JSON的语法是Javscript语法的子集,它并不能表示Javascript里的所有值。支持对象、数组、字符串、无穷大数字、true、false和null,并且它们可以序列化和还原。NaN、Inifinity和-Inifinity序列化结果都是null。函数、RegExp、Error对象和undefined值不能序列化和还原。

这里在附加说一下对象的方法:

toString()方法:它将返回一个表示调用这个方法的对象值的字符串。很多对象都重写了toString()方法,比如Array.toString()、Date.toString()以及Function.toStrring()。

toJSON()方法:Object.prototype实际上没有定义toJSON()方法,但由于需要执行序列化的对象来说,JSON.stringify()方法会调用toJSON()方法。如果在带序列化的对象中存在这个方法,则调用它。

valueOf()方法:valueOf()方法和toString()方法非常相似,但往往Javascript需要将对象转换为某种原始值而非字符串的时候才调用它,尤其是转换为数字的时候。有些内置类自定义了valueOf()方法,比如,Date.valueOf()。

以上这篇全面了解JavaScript对象进阶就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
javascritp实现input输入框相关限制用法
Jun 29 Javascript
JavaScript学习笔记(一) js基本语法
Oct 25 Javascript
js有关元素内容操作小结
Dec 20 Javascript
js实现点击切换TAB标签实例
Aug 21 Javascript
深入解析JavaScript中的立即执行函数
May 21 Javascript
js实现倒计时关键代码
May 05 Javascript
微信小程序使用request网络请求操作实例
Dec 15 Javascript
浅谈针对Vue相同路由不同参数的刷新问题
Sep 29 Javascript
VUE 配置vue-devtools调试工具及安装方法
Sep 30 Javascript
laydate只显示时分 不显示秒的功能实现方法
Sep 28 Javascript
微信小程序 SOTER 生物认证DEMO 指纹识别功能
Dec 13 Javascript
微信小程序实现音频文件播放进度的实例代码
Mar 02 Javascript
EasyUI中在表单提交之前进行验证
Jul 19 #Javascript
jQuery EasyUI提交表单验证
Jul 19 #Javascript
js 动态添加元素(div、li、img等)及设置属性的方法
Jul 19 #Javascript
JS把内容动态插入到DIV的实现方法
Jul 19 #Javascript
JS动态给对象添加事件的简单方法
Jul 19 #Javascript
老生常谈js动态添加事件--- 事件委托
Jul 19 #Javascript
Bootstrap表单布局
Jul 19 #Javascript
You might like
Codeigniter+PHPExcel实现导出数据到Excel文件
2014/06/12 PHP
php使用simplexml_load_file加载XML文件并显示XML的方法
2015/03/19 PHP
php注册和登录界面的实现案例(推荐)
2016/10/24 PHP
yii2.0整合阿里云oss删除单个文件的方法
2017/09/19 PHP
Laravel Intervention/image图片处理扩展包的安装、使用与可能遇到的坑详解
2017/11/14 PHP
理解JavaScript的prototype属性
2012/02/11 Javascript
JS中的prototype与面向对象的实例讲解
2013/05/22 Javascript
jQuery:delegate中select()不起作用的解决方法(实例讲解)
2014/01/26 Javascript
Javascript URI 解析介绍
2015/03/15 Javascript
原生JavaScript实现Tooltip浮动提示框特效
2017/03/07 Javascript
JavaScript实现隐藏省略文字效果的方法
2017/04/27 Javascript
NodeJS实现图片上传代码(Express)
2017/06/30 NodeJs
JavaScript 用fetch 实现异步下载文件功能
2017/07/21 Javascript
浅谈Angular2 模块懒加载的方法
2017/10/04 Javascript
详解最新vue-cli 2.9.1的webpack存在问题
2017/12/16 Javascript
在vue项目中使用Nprogress.js进度条的方法
2018/01/31 Javascript
layui 设置table 行的高度方法
2018/08/17 Javascript
vue中如何去掉空格的方法实现
2018/11/09 Javascript
[01:00:26]Ti4主赛事胜者组第一天 EG vs NEWBEE 1
2014/07/19 DOTA
详解Python使用simplejson模块解析JSON的方法
2016/03/24 Python
python绘制条形图方法代码详解
2017/12/19 Python
Python拼接字符串的7种方法总结
2018/11/01 Python
python装饰器练习题及答案
2019/11/01 Python
python GUI库图形界面开发之PyQt5中QMainWindow, QWidget以及QDialog的区别和选择
2020/02/26 Python
pip已经安装好第三方库但pycharm中import时还是标红的解决方案
2020/10/09 Python
M1芯片安装python3.9.1的实现
2021/02/02 Python
购买瑞典当代设计的腕表和太阳眼镜:TRIWA
2016/10/30 全球购物
绘画设计学生的个人自我评价
2013/09/20 职场文书
《藏戏》教学反思
2014/02/11 职场文书
《中国梦我的梦》大学生演讲稿
2014/08/20 职场文书
工伤死亡理赔协议书
2014/10/20 职场文书
2014年反腐倡廉工作总结
2014/12/05 职场文书
如何使用CocosCreator对象池
2021/04/14 Javascript
教你怎么用Python处理excel实现自动化办公
2021/04/30 Python
Redis实现订单自动过期功能的示例代码
2021/05/08 Redis
Spring Data JPA框架的核心概念和Repository接口
2022/04/28 Java/Android