JavaScript之对象

在ES6出现之前,JavaScript不能真正被称为 面向对象的编程语言,因为 class 仅仅作为其保留字而非关键字,而ES6之后,引入了class,使程序员可以用自己更加熟悉的方式创建对象;

至于ES6和ES5有什么区别,应该就是上面提到的可以让程序员更爽地coding,而程序员爽了,根据 工作量守恒定律,总有某个事物要干更多的活,没错,就是计算机。

为了兼容某些不支持ES6的浏览器,我们可以引入 Babel 库将ES6代码 “编译” 成ES5之后执行,而对于支持ES6的浏览器,在其JavaScript引擎中会自动进行“编译”操作;

所以,总体上看,ES6和ES5在功能上是等效的,即用ES6能完成的任务,用ES5必然能够完成,只是在语法上,ES6提供了更多的 语法糖,让程序员尝到甜头;所以为了更好的理解JavaScript对象,我们回归初心,从ES5中窥视JavaScript所创建的对象世界;

对象的创建

对象有两个基本元素:属性和方法;

属性用于存储数据,方法用于存储代码;接下来,我们从简单到复杂,来理解JavaScript创建对象的演变史。

创建Object对象并赋值时代

我们可以通过 new Object 创建Object,并为其赋属性和方法来创建对象:

// 代码段 1
var p = new Object();
p.name = "x";
p.age = 20;
p.say = function(){
  console.log("My name is",this.name,",this year is",this.age);
}

这样我们就得到一个含有2个属性,1个方法的对象;

通过执行 p.say() 得到输出 : My name is x ,this year is 20 ;

但是,这些几行代码非常松散,每一行代码都像一条单独的语句,为了体现这些属性和方法是一个整体,而不是一个个独立的存在,我们需要进入下一个时代;

字面量赋值时代

我们通过给一个变量赋一个字面量,即可创建一个对象:

// 代码段 2
var p = {
  name : "x" ,
  age : 20 , 
  say : function(){
    console.log("My name is",this.name,",this year is",this.age);
  }
}

这样,我们就创建好了一个对象,这个对象有了自己的属性和方法;

通过 console.log(typeof p) ,输出为object可知, p 的类型是一个对象;

代码段1和代码段2相比,代码段2的结构更为合理,所有的属性和方法都用大括号包含,更利于阅读,也体现了整体性,但功能上是等效的;

不过我们又发现,每次要创建一个相似的对象,都需要写一遍属性名,非常地不优雅,所以,我们又要进入下一个时代;

工厂模式时代

我们可以通过调用一个函数创建我们需要的对象,并返回该对象,从而使代码可重用,这个函数就是传说中的 工厂,代码如下:

// 代码 3
function createPerson(name,age){
  var t = new Object();
  t.name = name;
  t.age = age;
  t.say = function(){
    console.log("My name is",this.name,",this year is",this.age);
  }
  return t;
}
var p = createPerson("x",20);

代码3是将代码1变为了工厂模式;

下面再将代码2也变为工厂模式:

function createPerson(name,age){
  return {
    name:name,
    age : age,
    say = function(){
      console.log("My name is",this.name,",this year is",this.age);
    }
  };
}
var p = createPerson("x",20);
```
工厂模式的代码要比前面两个时代的代码优雅,我们只需要调用一个函数即可获得我们想要的对象;

但是,我们又发现了一个新的问题(别问我为什么总是能发现新问题,因为就是有一双善于观察的眼睛,手动傲娇 ^_^):

通过上述3中方式创建的对象在使用 `typeof` 时,返回的都是 `object`,而通过 `实例 instanceof 类` 只有在类为 `Object`时,才返回true,就是说,上面3中方法创建的对象都是无差别的对象,我们不能分辨出它们的类型;

这就麻烦了,比如我们有这么一个函数:
```javascript
function seeDoctor( o ){
  if(o是人){
    请人医治疗
  }
  if(o是动物){
    请兽医治疗
  }
}
```
那我们创建的对象因为不能判断其是人是兽,将不能选择适合的治疗方案;

要解决这个问题,有两种思路:
- 给对象添加信息,即为每一个对象添加一个属性 `type` ,用于指明其类型;
- 让js解释器能够判断其类型;

第一种方式比较 *丑陋*,我们需要管理更多的数据,但比较容易理解;第二种是更优雅的方法,也推动我们进入下一个时代;

### 构造函数时代

构造函数时代的主要任务是让创建的对象自带类别说明属性,即通过`instanceof` 就能判断出其所属的类:
```javascript
// 类是一个函数,约定:
// 普通函数第一个字母小写,类函数第一个字母大写
function Person(name,age){
  this.name = name;
  this.age = age;
  this.say = function(){
    console.log("My name is",this.name,",this year is",this.age);
  }
}
function Animal(){}
var p = new Person("x",20) ;
```
注意,创建对象时,必须使用关键字 **new** 。

此时,我们通过 `p instanceof Person`,返回的结果为 `true`,而通过 `p instanceof Animal` ,返回结果为 `false`,从而使对象实例自带类型属性;

完美! But,又双叒叕发现了不足,我们用上述各种函数创建两个对象:
```javascript
var p1 = new Person("x",20);
var p2 = new Person("y",21);
console.log(p1.say==p2.say) ; // 输出的是false
```
我们发现:虽然 *say* 函数的代码相同,但两个对象实例的 *say* 居然指向不同的代码块,如果我们有100个实例,相同的代码块就需要有100份,极大的内存浪费,这是我们所不能忍受的,因此迫切希望下一个时代的到来!

### 构造函数+原型时代

原型就是所有对象实例所共享的一个 **对象**,这个对象中的属性就是共享属性(在c++中称为静态变量),方法就是共享方法; 

```javascript
function Person(name,age){
  this.name = name;
  this.age = age;
}
Person.prototype.say = function(){
  console.log("My name is",this.name,",this year is",this.age);
}
var p = new Person("x",20);
```
上述代码创建的对象实例 p 有自己的属性name和age,以及共享的方法say;通过 `p.say()` 即可打印出:*My name is x ,this year is 20* ;

执行 `p.say()` 的时候,p先搜索其自身是否有方法 `say`,如果有,就执行,如果没有,就搜索其原型对象是否有 `say` 方法,如果还是没有,就搜索其原型对象的原型对象是否有say属性(即沿着原型链搜索say方法,这也是继承的实现机制),如果原型链上都没有say方法,就抛出错误,否则,执行搜索到的方法。

p通过属性`p.__proto__` 指向原型对象 `Person.prototype` , 从而获取原型上的所有属性和方法;

如果p上也定义一个方法 `say`:
```javascript
p.say = function(){console.log("Hello,world");}
```
则该方法将会 **覆盖** 原型上的say方法,即调用 `p.say` 输出的将是 *Hello,world* ,而如果通过 `delete p.say` 删除掉 `say` 属性,则调用 `p.say` 时,执行的代码又是原型上的 say 代码;

总结:原型就是一个类所创建的所有对象实例共享的一个对象;

但是,原型对象的定义和构造函数分开了,这又使结构不太优美,所以,我们又得进入下一个时代;

### 构造原型时代

为了解决原型定义和构造函数分离的问题,我们决定将原型定义放到构造函数中,就出现了以下代码:

```javascript
function Person(name,age){
  this.name = name;
  this.age = age;
  Person.prototype.say = function(){
    console.log("My name is",this.name,",this year is",this.age);
  }
}
var p = new Person("x",20);
```
OK,完成了原型定义和构造函数的合并,结构也变得更加优美了,但是,又出现了一个问题:

每次执行创建 Person 对象实例的时候,都要重新定义一遍 `Person.prototype.say` 方法,虽然这不会增加内存泄漏(以前定义的say代码由于没有被引用,内存块将会被自动回收),但却增加了cpu的工作量,所以我们需要进入下一个时代;

### 优化构造原型时代

为了避免 `Person.prototype.say` 函数的重复定义,我们可以先判断该函数是否已定义,如果没有定义,再对其进行定义:

```javascript
function Person(name,age){
  this.name = name;
  this.age = age;
  if(typeof(Person.prototype.say)=="undefined"){
    Person.prototype.say = function(){
      console.log("My name is",this.name,",this year is",this.age);
    } ;
  }
}
var p = new Person("x",20);
```
通过以上7个时代的迭代,我们终于在 JavaScript中创建了一个基本上符合我们要求的对象; 

完!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,123评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,031评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,723评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,357评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,412评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,760评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,904评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,672评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,118评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,456评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,599评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,264评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,857评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,731评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,956评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,286评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,465评论 2 348

推荐阅读更多精彩内容

  • 前言 这是读书笔记第二篇,看完之后突然发现自己对js的内置的一些东西还是了解的不够全面,很多方法见都没见过,啥用都...
    不止前端阅读 186评论 0 0
  • 中午,有个同事问我们需不需要买鞋,她说她领取了一家鞋店的优惠券,有很多张,都是满100减100的,优惠力度特别的大...
    辰苓阅读 1,109评论 0 2
  • copy和mutablecopy 源于对数据的复制需求,对于对象类型的数据来说,区别于直接持有这个数据对象的方式,...
    纵横而乐阅读 398评论 0 1
  • 职位描述 【岗位职责】 1、对房地产市场数据进行日常收集归类统计;及时跟踪和研究国家宏观经济政策的走向,对国家重大...
    没头没脑傻开心阅读 473评论 0 2
  • 小洋洋出生后,我与宝爸基本没有二人世界啦,有的都是三人世界。但这个周末的三人世界有点特别(∩_∩)。 上篇更文中宝...
    李梅树阅读 622评论 1 3