1.对象

1.1 语法

对象可以通过两种形式定义:声明(文字)形式和构造形式。

对象的文字语法:

var myObj = {
key : value
//...
};

对象的构造语法:

var myObj = new Object();
myObj.key = value;

1.2 类型

对象是JavaScript的基础。在JavaScript中一共有六种主要类型(术语是“语言类型”):

  • string
  • number
  • boolean
  • null
  • undefined
  • object

函数是对象的一个子类型(从技术角度来说就是“可调用的对象”)。

数组也是对象的一种类型,具备一些额外的行为。

JavaScript中还有一些对象子类型,通常被称为内置对象。

  • String
  • Number
  • Boolean
  • Object
  • Function
  • Array
  • Date
  • RegExp
  • Error

在JavaScript中,它们实际上只是一些内置函数,这些内置函数可以当作构造函数来使用。

相关阅读:JavaScript 参考手册

var strPrimitive = "I am a string";
typeof strPrimitive;//"string"
strPrimitive instanceof String;//false

原始值 "I am a string"并不是一个对象,它只是一个字面量,并且是一个不可变的值。如果要在这个字面量上执行一些操作,比如获取长度、访问其中某个字符等,那需要将其转换为String对象。

幸好,在必要时语言会自动把字符串字面量转换成一个String对象,也就是说你并不需要显式创建一个对象。

var strPrimitive = "I am a string";
console.log(strPrimitive.length);//13
console.log(strPrimitive.charAt(3));//"m"

我们可以直接在字符串字面量上访问属性或者方法,因为引擎自动把字面量转换成String对象,所以可以访问属性和方法。

对于数值字面量和布尔字面量也是如此。

nullundefined没有对应的构造形式,它们只有文字形式。

Date只有构造,没有文字形式。

对于Object、Array、Function和RegExp来说,无论使用文字形式还是构造形式,它们都是对象,不是字面量。

Error对象很少在代码中显式创建,一般是在抛出异常时被自动创建。

1.3 内容

对象的内容是由一些存储在特定命名位置的值组成的,我们称之为属性

在引擎内部,这些值的存储方式是多种多样的,一般并不会存在对象容器内部。

存储在对象容器内部的是这些属性的名称,它们就像指针(从技术角度来说就是引用)一样,指向这些值真正的存储位置。

var myObject = {
a : 2
}; myObject.a;//2
myObject["a"];//2

如果要访问myObject中a位置上的值,我们需要使用.操作符或者[]操作符。

.a语法通常被称为“属性访问”, ["a"]语法通常被称为“键访问”。

.操作符要求属性名满足标识符的命名规范,而[".."]语法可以接受任意UTF-8/Unicode字符串作为属性名。

在对象中,属性名永远都是字符串。如果你使用string(字面量)以外的其他值作为属性名,那它首先会被转换为一个字符串。

ES6增加了可计算属性名,可以在文字形式中使用[]包裹一个表达式来当作属性名。

var prefix = "foo";

var myObject = {
[prefix + "bar"] : "hello",
[prefix + "baz"] : "world"
}; myObject["foobar"];//hello
myObject["foobaz"];//world

数组支持[]访问形式。

var myArray = ["foo",42,"bar"];

myArray.length;//3
myArray[0];//"foo"
myArray[2];//"bar"

数组也是对象,所以虽然每个下标都是整数,你仍然可以给数组添加属性:

var myArray = ["foo",42,"bar"];

myArray.baz = "baz";
myArray.length;//3
myArray.baz;//"baz"

你可以把数组当作一个普通的键/值对象来使用,并且不添加任何数值索引。

但最好只用对象来存储键/值对,只用数组来存储数值下标/值对。

注意:如果你试图向数组添加一个属性,但是属性名“看起来”像一个数字,那它会变成一个数值下标。

var myArray = ["foo",42,"bar"];

myArray["3"] = "baz";
myArray.length;//4
myArray[3];//"baz"

复制对象

对于JSON安全的对象来说,有一种巧妙的复制方法:var newObj = JSON.parse(JSON.stringify(someObj))

ES6定义了Object.assign(..)方法来实现浅复制。

属性描述符

从ES5开始,所有的属性都具备了属性描述符。

在创建普通属性时属性描述符会使用默认值,我们也可以使用Object.defineProperty(..)来添加一个新属性或者修改一个已有属性并对特性进行设置。

var myObject = {}
Object.defineProperty(myObject,"a",{
value:2,
writable:true,
configurable:true,
enumerable:true
});
myObject.a;//2
//writable:是否可以修改属性的值
//configurable:是否可以配置
//enumerable:属性的值是否可以被枚举

遍历

var myArray = [1,2,3];
for(var i = 0; i < myArray.length; i++){
console.log(myArray[i]);
}//1 2 3

这实际上并不是在遍历值,而是遍历下标来指向值。

ES5中增加了一些数组的辅助迭代器,包括forEach(..)every(..)some(..)

ES6增加了一种用来遍历数组的for..of循环语法。

var myArray = [1,2,3];
for(var v of myArray){
console.log(v);
}
//1
//2
//3

for..of循环首先会向被访问对象请求一个迭代器对象,然后通过调用迭代器对象的next()方法来遍历所有返回值。

var myArray = [1,2,3];
var it = myArray[Symbol.iterator](); it.next();//{value:1,done:false}
it.next();//{value:2,done:false}
it.next();//{value:3,done:false}
it.next();//{done:true}

我们使用ES6中的符号Symbol.iterator来获取对象的iterator内部属性。

和数组不同,普通的对象没有内置的iterator,所以无法自动完成for..of遍历。

2.原型

2.1 [[Prototype]]

JavaScript中的对象有一个特殊的[[Prototype]]内置属性,其实就是对于其他对象的引用。几乎所有的对象在创建时[[Prototype]]属性都会被赋予一个非空的值。

var myObject = {
a : 2
}; myObject.a;//2

当我们试图引用对象的属性时会触发[[Get]]操作,比如myObject.a。对于默认的[[Get]]操作来说,第一步是检查对象本身是否有这个属性,如果有的话就使用它。但是如果a不在myObject中,就需要使用对象的[[Prototype]]链了。

对于默认的[[Get]]操作来说,如果无法在对象本身找到需要的属性,就会继续访问对象的[[Prototype]]链。

var anotherObject = {
a : 2
}; var myObject = Object.create(anotherObject); myObject.a;//2

现在myObject对象的[[Prototype]]链关联到了anotherObject。显然myObject.a并不存在,但是尽管如此,属性访问仍然成功地找到了值2。但是,如果anotherObject中也找不到a并且[[Prototype]]链不为空的话,就会继续查找下去。

Object.prototype

所有普通的[[Prototype]]链最终都会指向内置的Object.prototype

所有的“普通”对象都把[[Prototype]]链的顶端设置为这个Object.prototype对象,所以它包含JavaScript中许多通用的功能。

属性设置和屏蔽

myObject.foo = "bar"

如果myObject对象中包含名为foo的普通数据访问属性,这条赋值语句只会修改已有的属性值。

如果foo不是直接存在于myObject中,[[Prototype]]链就会被遍历,类似[[Get]]操作。如果原型链上找不到foo,foo就会被直接添加到myObject上。

如果属性名foo既出现在myObject中,也出现在myObject上的[[Prototype]]链上层,那么就会发生屏蔽。myObject中包含的foo属性会屏蔽原型链上层的所有属性,因为myObject.foo总是会选择原型链中最底层的foo属性。

屏蔽比我们想象中更加复杂。

通常来说,应当尽量避免使用屏蔽。

var anotherObject = {
a : 2
}; var myObject = Object.create(anotherObject); anotherObject.a;//2
myObject.a;//2 anotherObject.hasOwnProperty("a");//true
myObject.hasOwnProperty("a");//false myObject.a++;//隐式屏蔽! anotherObject.a;//2
myObject.a;//3 myObject.hasOwnProperty("a");//true

2.2 “类”

所有的函数默认都会拥有一个名为prototype的公有并且不可枚举的属性,它会指向另一个对象。这个对象通常被称为Foo的原型。

Foo.prototype默认有一个公有并且不可枚举的属性.constructor,这个属性引用的是对象关联的函数。

在JavaScript中对于“构造函数”最准确的解释是,所有带new的函数调用。

函数不是构造函数,但是当且仅当使用new时,函数调用会变成“构造函数调用”。

2.3 (原型)继承

function Foo(name){
this.name = name;
} Foo.prototype.myName = function(){
return this.name;
}; function Bar(name,label){
Foo.call(this,name);
this.label = label;
} //我们创建了一个新的Bar.prototype对象并关联到Foo.prototype
Bar.prototype = Object.create(Foo.prototype); Bar.prototype.myLabel = function(){
return this.label;
}; var a = new Bar("a","obj a"); a.myName();//"a"
a.myLabel();//"obj a"

这段代码的核心部分是Bar.prototype = Object.create(Foo.prototype)。调用Object.create(..)会凭空创建一个“新”对象并把新对象内部的[[Prototype]]关联到你指定的对象。即创建一个新的Bar.prototype对象并把它关联到Foo.prototype

在ES6之前,我们通过设置._proto_属性来修改对象的[[Prototype]]关联,但是这个方法并不是标准并且无法兼容所有浏览器。

ES6添加了辅助函数Object.setPrototypeOf(..),可以用标准并且可靠的方法来修改关联。

2.4 对象关联

[[Prototype]]机制是存在于对象中的一个内部链接,它会引用其他对象。

这个链接的作用是:如果在对象上没有找到需要的属性或者方法引用,引擎就会继续在[[Prototype]]关联的对象上进行查找。如果在后者中也没有找到需要的引用就会继续查找它的[[Prototype]],以此类推,这一系列的链接被称为“原型链”。

创建关联

var foo = {
something : function(){
console.log("Tell me something good...");
}
}; var bar = Object.create(foo); bar.something();//Tell me something good...

Object.create(..)会创建一个新对象(bar)并把它关联到我们指定的对象(foo),这样我们就可以充分发挥[[Prototype]]机制的威力(委托)并且避免不必要的麻烦(比如使用new的构造函数调用会生成.prototype.constructor引用)。

3.行为委托

Task = {
setID : function(ID){this.id=ID;},
outputID : function(){console.log(this.id);}
}; //让XYZ委托给Task
XYZ = Object.create(Task); XYZ.prepareTask = function(ID,Label){
this.setID(ID);
this.label = Label;
}; XYZ.outputTaskDetails = function(){
this.outputID();
console.log(this.label);
};

XYZ通过Object.create(..)创建,它的[[Prototype]]委托了Task对象。

这种编码风格称为“对象关联”(OLOO)。

比较思维模型

模仿类的行为

function Foo(who){
this.me = who;
} Foo.prototype.identify = function(){
return "I am " + this.me;
}; function Bar(who){
Foo.call(this,who);
} Bar.prototype = Object.create(Foo.prototype); Bar.prototype.speak = function(){
alert("Hello, " + this.identify() + ".");
}; var b1 = new Bar("b1");
var b2 = new Bar("b2"); b1.speak();
b2.speak();

对象关联风格

Foo = {
init: function(who){
this.me = who
},
identify: function(){
return "I am " + this.me;
}
}; Bar = Object.create(Foo); Bar.speak = function(){
alert("Hello, " + this.identify() + ".");
}; var b1 = Object.create(Bar);
b1.init("b1");
var b2 = Object.create(Bar);
b2.init("b2"); b1.speak();
b2.speak();

参考资料:《你不知道的JavaScript》(上卷) 第二部分 this和对象原型

JS的对象原型的更多相关文章

  1. 关于js的对象原型继承(一)

    javascript中,对象的继承是通过原型去继承. 可以这样理解:js中的对象,包含的除了属性和方法,还有一个最基本的原型__proto__对象.这个原型__proto__指向谁,这个对象就继承谁. ...

  2. 深度剖析前端JavaScript中的原型(JS的对象原型)

          这张图片有点劝退了,哈哈哈~    通过原型机制,JavaScript 中的对象从其他对象继承功能特性:这种继承机制与经典的面向对象编程语言的继承机制不同.本文将探讨这些差别,解释原型链如 ...

  3. 关于js的对象原型继承(二)

    本章讨论使用new一个构造函数来创建一个对象. 前期知识点说明: 1.prototype是函数的一个属性,每个函数都有一个prototype属性.这个属性是一个指针,指向一个对象.它是显示修改对象的原 ...

  4. js 继承 对象方法与原型方法

    js函数式编程确实比很多强语言使用灵活得多,今天抽了点时间玩下类与对象方法调用优先级别,顺便回顾下继承 暂时把原型引用写成继承 先看看简单的两个继承 var Parent = function(){} ...

  5. jquery实现点击展开列表同时隐藏其他列表 js 对象操作 对象原型操作 把一个对象A赋值给另一个对象B 并且对象B 修改 不会影响 A对象

    这篇文章主要介绍了jquery实现点击展开列表同时隐藏其他列表的方法,涉及jquery鼠标事件及节点的遍历与属性操作技巧,具有一定参考借鉴价值,需要的朋友可以参考下 本文实例讲述了jquery实现点击 ...

  6. 关于JS对象原型prototype与继承,ES6的class和extends · kesheng's personal blog

    传统方式:通过function关键字来定义一个对象类型 1234567891011 function People(name) { this.name = name}People.prototype. ...

  7. js 中对象--对象结构(原型链基础解析)

    对于本篇对于如何自定义对象.和对象相关的属性操作不了解的话,可以查我对这两篇博客.了解这两篇可以更容易理解本篇文章 用构造函数创建了一个对象  obj对象的本身创建了两个属性  x=1   ,y=2 ...

  8. 【JS】#001 JS定义对象写法(原型、JSON方式)

    下面主要写两种 JS 定义对象的 常用写法 写法1:[很像面向对象语言中的写法] function zhongxia(age) { this.age = age; } zhongxia.name = ...

  9. JS进阶之原型

    之前有在自己的文章中谈到对象,而说到对象我们就不可避免的要扯到原型,并且原型也是我们必须得理解到位的一个点,那接下来我们就来聊一聊js的原型吧. JS中一切皆为对象,那么原型也是一种对象.所以它有对象 ...

随机推荐

  1. Ubuntu下载磁力链接,torrent,迅雷链接

    用ubuntu下载电影:磁力链接,torrent,迅雷链接 需要软件:Ktorent, Amule 安装软件: sudo apt-get install ktorrent sudo apt-get i ...

  2. Python学习之路目录(收藏整理)

    目录 Python之路[第一篇]:Python简介和入门 Python之路[第二篇]:Python基础(一) Python之路[第三篇]:Python基础(二) Python之路[第四篇]:模块    ...

  3. 【Docker】第二篇 Docker镜像管理

    一.搜索镜像 1.下载一个docker镜像:我们可以通过登陆docker网站搜索自己需要的镜像,可以选择自己所需要的版本,然后通过详情也可以看到:网址:https://hub.docker.com/2 ...

  4. LeetCode 657. Robot Return to Origin (C++)

    题目: There is a robot starting at position (0, 0), the origin, on a 2D plane. Given a sequence of its ...

  5. 奔跑吧DKY——团队Scrum冲刺阶段-Day 7

    今日完成任务 谭鑫:将人物图添加到游戏以及商店界面中,实现商店的选择换装功能 黄宇塘:制作人物图.背景图 赵晓海:阅读所有代码测试所有功能,美化部分界面 方艺雯:为商店界面及关于界面添加必要文字说明 ...

  6. 20162314 Experiment 2 - Tree

    Experiment report of Besti course:<Program Design & Data Structures> Class: 1623 Student N ...

  7. 今日事——Sprint计划会议

    一.   Sprint需求: 解屏提醒部分 界面设计 登录功能 备忘功能 成就系统 二.工作认领: 因有成员请假回家,所以延后认领,目前主要任务是学习如何在andriod平台开发并搭建开发环境. 网上 ...

  8. 利用python进行简单的图像处理:包括打开,显示以及保存图像

    利用python进行简单的图像处理:包括打开,显示以及保存图像 利用PIL处理 PIL(python image library) 是python用于图片处理的package.但目前这个package ...

  9. YFCC 100M数据集分析笔记

    --从YFCC 100M数据集中筛选出Geo信息位于中国的数据集 1.YFCC 100M简介 YFCC 100M数据库是2014年来基于雅虎Flickr的影像数据库.该库由1亿条产生于2004年至20 ...

  10. 项目报错“JavaServer Faces 2.2 can not be installed : One or more constraints”等一系列问题

    在做springmvc+maven项目时,经常遇到如下错误: 解决办法(这里以jdk1.8,web3.0为例): 一:保证build path的jre版本 remove掉旧版本的,add新版本 二:保 ...