全面了解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 相关文章推荐
用js实现的一个Flash滚动轮换显示图片代码生成器
Mar 14 Javascript
js取滚动条的尺寸的函数代码
Nov 30 Javascript
Js从头学起(基本数据类型和引用类型的参数传递详细分析)
Feb 16 Javascript
javascript禁制后退键(Backspace)实例代码
Nov 15 Javascript
在JavaScript中处理时间之setMinutes()方法的使用
Jun 11 Javascript
基于jQuery实现复选框是否选中进行答题提示
Dec 10 Javascript
深入理解javascript中的 “this”
Jan 17 Javascript
Three.js实现绘制字体模型示例代码
Sep 26 Javascript
微信小程序中setInterval的使用方法
Sep 29 Javascript
vue-cli3.0+element-ui上传组件el-upload的使用
Dec 03 Javascript
js中arguments对象的深入理解
May 14 Javascript
详解Vuex下Store的模块化拆分实践
Jul 31 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
php获取CSS文件中图片地址并下载到本地的方法
2014/12/02 PHP
PHP基于mssql扩展远程连接MSSQL的简单实现方法
2016/10/08 PHP
PHP实现的MD5结合RSA签名算法实例
2017/10/07 PHP
php5.x禁用eval的操作方法
2018/10/19 PHP
Laravel框架搜索分页功能示例
2019/02/01 PHP
laravel excel 上传文件保存到本地服务器功能
2019/11/14 PHP
锋利的jQuery 要点归纳(三) jQuery中的事件和动画(上:事件篇)
2010/03/24 Javascript
JavaScript 以对象为索引的关联数组
2010/05/19 Javascript
WEB高性能开发之疯狂的HTML压缩
2010/06/19 Javascript
javascript中对Attr(dom中属性)的操作示例讲解
2013/12/02 Javascript
基于编写jQuery的无缝滚动插件
2014/08/02 Javascript
angularJS 中$attrs方法使用指南
2015/02/09 Javascript
JQuery页面地址处理插件jqURL详解
2015/05/03 Javascript
windows下安装nodejs及框架express
2015/08/07 NodeJs
避免jQuery名字冲突 noConflict()方法
2016/07/30 Javascript
JavaScript遍历Json串浏览器输出的结果不统一问题
2016/11/03 Javascript
jQuery 出现Cannot read property ‘msie’ of undefined错误的解决方法
2016/11/23 Javascript
php 修改密码实现代码
2017/05/24 Javascript
JS获取当前时间的实例代码(昨天、今天、明天)
2018/11/13 Javascript
element ui table(表格)实现点击一行展开功能
2018/12/04 Javascript
详解小程序如何避免多次点击,重复触发事件
2019/04/08 Javascript
jquery实现加载更多"转圈圈"效果(示例代码)
2020/11/09 jQuery
Vue 3.0中jsx语法的使用
2020/11/13 Javascript
[01:16:01]VGJ.S vs Mski Supermajor小组赛C组 BO3 第一场 6.3
2018/06/04 DOTA
浅谈python for循环的巧妙运用(迭代、列表生成式)
2017/09/26 Python
Python Scrapy框架第一个入门程序示例
2020/02/05 Python
解决django无法访问本地static文件(js,css,img)网页里js,cs都加载不了
2020/04/07 Python
keras之权重初始化方式
2020/05/21 Python
详解如何解决canvas图片getImageData,toDataURL跨域问题
2018/09/17 HTML / CSS
一道SQL面试题
2012/12/31 面试题
.net面试题
2016/09/17 面试题
教师一岗双责责任书
2014/04/16 职场文书
校园安全演讲稿
2014/05/09 职场文书
乡镇干部个人对照检查材料(群众路线)
2014/09/26 职场文书
党员教师个人对照检查材料(群众路线)
2014/09/26 职场文书
HttpClient实现文件上传功能
2022/08/14 Java/Android