【大树云微课堂】-浅谈 JavaScript 中的 Object(对象)

1. 对象的定义
?向对象的语?都有一个标志,即类。

定义:对象是 JavaScript 的一个基本数据类型,是?种复合值,它将很多值(原始值或者其他对象)聚合在一起,可通过名字访问这些值。即属性的无序集合。

例:上帝根据?己的形象造男造女,这里的上帝便是类,这里的男和女 便是对象。
● 在对象中,每一个属性和?法都有一个名字,?每个名字都映射到一个值,即 ECMAScript 中的对象?非就是?组名值对,其中值可以是数据或者函数。

2. 对象的属性
在 JavaScript 中,对象的属性分为两种类型:数据属性和访问?属性。

2.1 数据属性
1. 数据属性:包含的是?个数据值的位置,在这可以对数据值进行读写。
2. 数据属性包含四个特性,分别是:


例:


2.2 访问器?属性
1. 访问器属性:这个属性不包含数据值,包含的是一对 get 和 set 方法,在读写访问?属性时,就是通过这两个方法来进行操作处理。
2. 访问?属性包含四个特性,分别是:



例:


3. 创建对象的?法
1. 最简单粗暴:对象字?量




2. ?厂模式



3. 构造函数模式



● 创建 Person 实例,必须使? new 操作符,这种?式实际上经历了以下四个步骤:
     1. 创建?个新对象
     2. 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象)
     3. 执?构造函数中的代码(为这个新对象添加属性)
     4. 返回新对象
缺点:每个?法都要在每个实例上重新创建一遍,所有实例内部的 Function 都不是同一个 Function
(注:因为在 JS 中,?切皆对象,联想到函数声明,每定义?个函数也就是实例化?个
对象,逻辑?角度是等价的):



4. 原型模式



这样就不不必在构造函数中定义对象实例例的信息,?而是将这些信息直接添加到原型对象中



● ?论什么时候:
     1. 只要创建了函数,就会根据函数特定的规划为函数创建一个 prototype 属性,这个属性指向函数的原型对象
     2. 原型对象?动获得?个 constructor 属性,这个属性包含一个指向 prototype 属性所在函数的指针

但是,为什么说这样的方式可以避免出现像构造函数那样的问题呢?

原来是这样:在调用 person1.sayName() 的时候,解析?首先会问:
     1. “实例 person1 有 sayName 属性吗?” 答:“没有。"
     2. 然后,它继续搜索,再问 :“ person1 的原型有 sayName 属性吗? ”答:“有。”
     3. 于是,它就读取那个保存在原型对象中的函数。当我们调? person2.sayName() 时,将会重现相同的搜索过程,得到相同的结果。

         ?这正是多个对象实例共享原型所保存的属性和方法的基本原理

对 JS 对象的理解好像?深刻了,尝试以下代码:



所以:有人说,对 Person.prototype 的理解是:它是函数的原型对象,现在看来,这种理解是不够准确的。
其实:
● prototype 只是每个函数在被创建时?带的一个属性,这个属性指向函数的原型对象
● 从?可以使用 person1.constructor 获取原型对象中的 constructor 属性
● ? construtor 指向的是 Person
● 所以 person1.constructor.prototype 也不难理解
(好吧,刚开始真的有点绕,多读几次其实还好)

● 当为对象实例添加一个属性时,这个属性就会屏蔽原型对象中保存的同名属性;
● 换句话说,添加这个属性只会阻止我们访问原型中的那个属性,但不会修改那个属性,举例如下





有个?法可以判断一个属性是存在于实例中,还是存在于原型中。



5. 原型模式进阶



原型模式的缺点:
如果原型对象上面的属性所对应的值是引用类型,那么问题就来了。
众所周知,对于 JS 的数据类型分为两类:基本类型引用类型,我所理解的他们的区别主要是存储的空间不同:
基本数据类型存在于栈内存,键值对的方式存储
对于引用数据类型,栈内存中存的是键和引用地址,而引用地址指向的是堆内存中该对象所存储的地方
所以:



因为它们的引用地址指向的是同?个堆内存对象(数组),所以,每个实例一般都是有专属于?己的属性。

● 书中是这样说的:
     假如我们的初衷就是像这样: 在所有实例中共享一个数组,那么对这个结果我没有话可说。
     可是,实例一般都是要有属于?己的全部属性的。?这个问题正是我们很少看到有人单独使?原型模式的原因所在。

6. 组合使用构造函数模式和原型模式



实例所有的属性都?构造函数定义,所有的?法以及 constructor 属性都早原型对象中定义。
都这样做的好处就是确保每个实例的属性都是?己独立的,但是共享了对?方法的引用,最大限度的节省了内存空间。

7. 动态原型模式



困惑解决:
学习了组合与动态原型模式之后,突然有个问题困惑了,直接上代码:



论封装性,代码?不是更好吗?为什么都是 prototype 属性的重写,代码?和代码?的结果却不一样?
我想到了 new 关键字做了什么:创建对象,指针指向,执行函数,返回对象,我应该是忽略了执行函数这一步。
代码一中,每次实例化对象, prototype 属性都进行了重写,重写了两次,所以改变了现有实例与原型之间的联系。
?代码二中,虽然同样是两次实例化,但不同的是全局代码也就是给 Person 的原型定义只执?了一次,所以 sayName 方法的引?指向的是同一个堆内存对象。

● 书中是这样写的:
     使?动态原型模式时,不能使用对象字?量重写原型。
     前面已经解释过了,如果在已经创建了实例的情况下重写原型,那么就会切断现有实例与新原型之间的联系。
     所以,之所有会有以上困惑,是因为对 new 关键字做了什么把握不清。
     还有两种创建对象的模式,寄?构造函数模式 和 稳妥构造函数模式,大树云项目中目前还没有用到过,这里就不做赘述。

● 本?代码片段参考《JavaScript 高级程序设计(第三版)》第六章
     了解以下知识点或许对本文的理解更有帮助
     1. JavaScript new ?法做了什么
     2. JavaScript 基本数据类型和引?数据类型
     3. 栈内存与堆内存

更多资讯,请关注?树云系列公众号:


★博文内容均由个人提供,与平台无关,如有违法或侵权,请与网站管理员联系。

★博文作者未开放评论功能