JavaScript对象:面向对象还是基于对象?

  学习JavaScript对象,当然会看看JavaScript标准对基于对象的定义,这个定义的内容是:“语言和宿主的基础设施由对象来提供,并且JavaScript程序即是一系列相互通讯的对象集合”。这听起来多么晦涩,至少我很难从中理解到任何JS对象的关键。

什么是面向对象

  首先,什么是对象呢?由于翻译的原因,中文语境中很难理解“对象”的真正含义。实际上,Object(对象)在英文中,是一切事物的总称,这和面向对象编程的抽象思维有互通之处。而中文里的“对象”却没有这样的普适性,以往的时间里更多的是把它当做一个专业名词来理解。其实,我们需要理解一点,对象不是计算机领域凭空创造出来的概念,它是顺着人类思维模式产生的一种抽象。

人类思维模式
  在幼年期,我们总是先认识到某一个苹果能吃(这里的某一个苹果就是一个对象),继而认识到所有的苹果都可以吃(这里的所有苹果,就是一个类),再到后来我们才能意识到三个苹果和三个梨之间的联系,进而产生数字“3”(值)的概念。

  有了对象的自然定义后,就可以描述编程语言中的对象了。在不同编程语言中,它们的设计者利用不同的语言特性来抽象描述对象,C++,Java等流行语言都是使用“”的方式来描述对象,但JavaScript早年选择了一个更了冷门的方式:原型。JS设计时在“原型运行时”的基础引入new、this等语言特征,使之更像Java。

  如果我们从运行时角度来谈论对象,就是在讨论 JavaScript 实际运行中的模型,这是由于任何代码执行都必定绕不开运行时的对象模型。

JavaScript 对象的特征

参考《面向对象分析与设计》,对象有如下几个特点:

  1. 对象具有唯一标识性:即是完全相同的两个对象,也并非用一个对象。
  2. 对象有状态:对象具有状态,同一对象可能处在不同状态之下。
  3. 对象具有行为:即对象的状态,可能因为它的行为产生变迁。

  我们先来看第一个特征,对象具有唯一标识性。一般而言,各种语言的对象唯一标识性都是用内存地址来体现的, 对象具有唯一标识的内存地址,所以具有唯一的标识。

 var o1 = { a: 1 };
 var o2 = { a: 1 };
 console.log(o1 == o2); // false

o1 和 o2 初看是两个一模一样的对象,但是打印出来的结果是false。

  关于对象的第二个和第三个特征“状态和行为”,不同语言会使用不同的术语来抽象描述它们,比如 C++ 中称它们为“成员变量”和“成员函数”,Java 中则称它们为“属性”和“方法”。而在JS中,将状态和行为统一抽象为“属性”。

  下面这段代码其实就展示了普通属性和函数作为属性的一个例子,其中 o 是对象,d 是一个属性,而函数 f 也是一个属性,尽管写法不太相同,但是对 JavaScript 来说,d 和 f 就是两个普通属性。

  var o = { 
        d: 1,
        f() {
            console.log(this.d);
        }    
    };

  在实现了对象基本特征的基础上, JavaScript 中对象独有的特色是:对象具有高度的动态性,这是因为 JavaScript 赋予了使用者在运行时为对象添改状态和行为的能力。

  譬如,JavaScript 允许运行时向对象添加属性,这就跟绝大多数基于类的、静态的对象设计完全不同。下面这段代码就展示了运行时如何向一个对象添加属性,一开始我定义了一个对象 o,定义完成之后,再添加它的属性 b,这样操作是完全没问题的。

 var o = { a: 1 };
 o.b = 2;
 console.log(o.a, o.b); //1 2

  为了提高抽象能力,JavaScript 的属性被设计成比别的语言更加复杂的形式,它提供了数据属性和访问器属性(getter/setter)两类。对 JavaScript 来说,属性并非只是简单的名称和值,JavaScript 用一组特征(attribute)来描述属性(property)。

数据属性

数据属性具有四个特征:

  1. value:属性值
  2. writable:决定属性能否被赋值
  3. enumerable:决定for in 能否枚举该属性
  4. configurable:决定该属性能否被删除或改变特征值

访问器(getter/setter)属性

它也有四个特征:

  1. getter:函数或undefined,在取属性值时被调用
  2. setter:函数或undefined,在设置属性值时被调用
  3. enumerable:决定for in 能否枚举该属性
  4. configurable:决定该属性能否被删除或改变特征值

  访问器属性使得属性在读和写时执行代码,它允许使用者在写和读属性时,得到完全不同的值,它可以视为一种函数的语法糖。

语法糖(Syntactic sugar)
它意指那些没有给计算机语言添加新功能,而只是对人类来说更“甜蜜”的语法。语法糖往往给程序员提供了更实用的编码方式,有益于更好的编码风格,更易读。不过其并没有给语言添加什么新东西。语法糖是一种便捷的写法,编译器会帮我们做转换;而且可以提高开发编码的效率,在性能上也不会带来损失。

  用于定义属性的代码会产生数据属性,其中writable、enumerable、configurable都默认为true,可以使用内置函数Object.getOwnPropertyDescriptor来查看,如下代码:

var 0 = {a : 1};
o.b = 3;
Object.getOwnPropertyDescriptor(o, "a");	//{value: 1, writable: true, enumerable: true, configurable: true}
Object.getOwnPropertyDescriptor(o, "b");  //{value: 3, writable: true, enumerable: true, configurable: true}

如果想要改变属性的特征,或者定义访问器属性,我们可以使用Object.defineProperty,如下代码:

var o = {a : 1};
Object.defineProperty(o, "b", {value: 1, writable: false, enumerable: false, configurable: false}) 
Object.getOwnPropertyDescriptor(o, "b");  //{value: 1, writable: false, enumerable: false, configurable: false}
o.b = 5;
console.log(o.b);  //1

创建对象是,也可以使用get 和 set 关键字来创建访问器属性,代码如下:

var o = {
     get a(){
        return 1;
    }
};
console.log( o.a );  //1

  访问器属性跟数据属性不同,每次访问属性都会执行 getter 或者 setter 函数。这里我们的 getter 函数返回了 1,所以 o.a 每次都得到 1。所以呢,实际上 JavaScript 对象的运行时是一个“属性的集合”,属性以字符串或者 Symbol 为 key,以数据属性特征值或者访问器属性特征值为 value。

猜你喜欢

转载自blog.csdn.net/bertZuo/article/details/86690891
今日推荐