【轻聊前端】为什么说一切皆对象?

现今各种框架、工具‘横行’,到处在讲原理和源码,更有跨端技术需要我们去探索,但如果基本功不好,学什么都是事倍功半,效果很不好,花费时间的同时打击自信心。此篇文章,为我所计划的【轻聊前端】系列第三篇,旨在系统地、逻辑性地把原生JavaScript知识分享给大家,帮助各位较为轻松地理清知识体系,更好地理解和记忆,我尽力而为,望不负期待。

前端er们经常看到这么一句话:“JavaScript的一切皆为对象”。

也有说“JavaScript是一门面向对象”的编程语言,还有说“不对,JavaScript不具备正统面向对象的特征,应该说是基于对象。”

谁是谁非?

我们“不是”对象

上篇文章我们讨论了JavaScript的数据类型,列出了这么几种:

Number、String、Null、Undefined、Boolean、Symbol

称为基本类型,它们有自己的类型,不可再分。它们是对象吗?

回答这个问题,得先看两个小问题:

  • 对象长什么样
  • 对象有什么特点

长什么样

第一篇文章里聊编程的时候,我们就定义过对象,长这样:

1
2
3
4
5
6
7
8
let people = {
sex:'男',
age:20,
occupation:'程序员',
eat:function(){
//吃饭
}
}

有什么

它有什么呢?

属性:性别、年龄、职业

方法/能力:吃饭

由此看,基本类型并不是对象,既没有对象的外表,也没有对象的特质

从“值”到“对象”

我们说过,基本类型存储的是值本身,但如果不论什么时候,值都只是值,那么这门语言等于废了一半的武功。

所以,当需要的时候,基本类型的值就会被转化成对应的对象,又称“包装类型”。简单理解,就是被包装成了对象。

就算不懂编程,看过代码的人应该对几个东西眼熟,比如:toString()、parseInt()/parseFloat()等。

如果你到代码里找它们的定义,发现并找不到,哪儿来的呢?

就来源于“包装类型”。

数字、字符串和布尔值对应的包装类型就是“Number、String、Boolean”。

像这样一段代码

1
2
let a = 1;
a.toString();

当运行的时候,会在内部经历三个过程:

  • 创建Number类型的实例
  • 调用实例方法toString()
  • 销毁实例

创建实例的过程可理解为执行了这么一行代码:

1
let a = new Number(1);

使其具备了和对象一样的可访问属性和可调用方法。

值得注意的是第三步也很关键,即销毁实例,虽然在以上代码执行的当时,变量具备了调用方法或者访问属性的能力,但是,在执行完毕之后实例会被立即销毁,恢复它本来“身份”,这就使得你想再给它添加属性或者方法是不会生效的。

1
2
3
4
let a = 1;
a.toString();
a.name = 'a的名字';
console.log(a.name); // undefined

String 和 Boolean 原理同 Number,不再赘述。

除了以上三者,JavaScript中还有几种基本的引用类型,这里一并介绍:

Date(日期)

Date是有很多使用场景的类型,比如:生日、文章发布时间、活动截止时间等等,都要用到日期。

这个类型不需要开发者自己定义,可以直接使用,可以拿到毫秒数,也可以拿到具体的年份,月份,星期,时/分/秒等,十分灵活,开发者按照需要进行格式的转换和组合就好。

RegExp

正则,一般以正则表达式的形式存在,而正则表达式通常被用来检索、替换符合某个规则的文本**。

比如我们最常见的,邮箱、电话号码、身份证号,当用户输入一个有格式要求的信息时,在前端交互上就要对数据进行校验,如果格式不符,就提示用户输入正确格式的内容,如果在提交之后再发现不对,体验就很不好了。

示例:

1
2
^[0-9]*$  //匹配数字  
^[\u4e00-\u9fa5]{0,}$ //匹配汉字

正则表达式有很多符号的属性和规则,能够发挥的作用十分强大,这里不再赘述,读者可以找专门的资料进一步了解。

集合引用类型

介绍完基本引用类型,再看集合引用类型。

Array

数组用来存储一系列同类型的数据,当然,通常我们是这么做的,但JavaScript中的常规数组不受类型限制,允许存储任意类型的数据。

为什么说数组是对象呢?它和上面提到的一样,具有包装类型 Array。

当我们对其进行访问时:

1
2
3
let a = [1,2,3];
a.length // 访问属性 3
a.push(4) //调用方法 [1,2,3,4]

均会调起包装类所具备的属性和方法来为我们服务。

Object

按理说,讲对象应该第一个提到Object,为什么现在才提:

一、它就叫对象。

二、它是用来自定义的对象,本身没有固定的属性和方法,开发者定义了才有。

基于以上两点,它是最好理解的对象类型,就放在后面,当然,关于对象,还会有另一篇文章详细介绍。

除了以上二者,JavaScript中新增了几种集合引用类型。

Map

ES6之前,存储键值对都是用Object,Map是一种新的类型,它在多数场景下和 Object 表现出的特性一致,但仍存在差异。

比如:

1
2
3
4
5
6
let a = new Map();
a.set('name','idea');
a.set('age',18); //使用set添加键值对
a.size //2 使用size获取键值对数量
a.has('name') //true 使用has查看是否具有某个键
a.get('name') //idea 使用get获取某个键的值

还有其他方法不再赘述,但Map和Object还有两点明显不同:

一是键的类型不再有限制,可以是任意类型。

二是它会维护插入的键的顺序。

PS:其他差异及如何选择使用,后续文章再详聊。

Set

Set类型也是ES6后新增的,它跟Map很像,大多数方法和API也是共有的。

但它不是专门用来存储键值对,而是存储值,可以存储任何类型的值。

示例:

1
2
3
4
let a = new Set([1,2,3]);  //初始化
a.add(4) //添加一个值
console.log(a); //{1,2,3,4}
a.add(4) //重复添加的值无效

Set使用add()方法添加值,Set同样会维护值的插入顺序且不允许值有重复,这一点给开发过程中的一些场景提供了很大便利,比如常见的数组去重,往常我们需要专门写个方法处理,现在只需要将数组使用Set方法处理一次就好。

PS:Set其他内容同上略过。

Function

聊完以上几种类型,就是函数了,函数也是对象?虽然我们很少像常规对象那样来用,但它也是对象。

它具备哪些属性和方法呢?

每个函数都至少有两个属性:length和prototype,length表示函数命名参数的个数,prototype则并非函数独有,以上所提及的其他类型的对象也都有,它是保存引用类型所有实例方法的地方,譬如:toString()、valueOf()等。

函数还有三个很有用的方法:call()、apply()、bind(),这三个方法用于将函数调用时的this 指向传给它们的对象。比如:

1
2
3
4
5
6
7
8
9
10
var color = "red;"
let o = {
color:"blue"
}

function getColor(){
console.log(this.color)
}
let objectColor = getColor.call(o);
objectColor(); // "blue"

如果没有call,输出会是 “red”,现在是 “blue”。

除此之外,函数曾经有过一种重要用途——构造函数

1
2
3
4
5
6
7
8
9
10
11
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name);
}
};

var person1 = new Person("tom",18,"teacher");
var person2 = new Person("lili",16,"doctor");

在ES以前的版本当中,这曾是很流行也很经典的一种定义“类”和创建“实例”的模式。
这里的函数和普通函数没什么区别,区别就在于使用了new关键字,从而触发了内部的一些特殊处理,这里点到为止~

其他内置对象

Math

除了Date对象,Math对象是另一个非常有用的内置对象,有很多实用的方法,比如:

  • 求最大、最小值——max()/min()
  • 四舍五入、上下取整——round()/ceil()/floor()
  • 随机数——random()
  • 绝对值——abs()

这些方法为数字处理提供了极大便利。

Window

有些对象在开发时并不会显式调用,有些属性和方法也不属于以上提到的任何一种对象,比如:全局定义的变量和函数,内置方法parseInt()和parseFloat()等。window作为浏览器中全局对象的代理对象就担当了这么一个角色,它们自动归属于window。

同时,很多浏览器API及相关构造函数都以window对象属性的形式暴露出来,以便开发者使用。

常见的有窗口宽高、像素比、滚动、打开窗口等,都是window对象的范畴。

window对象并不孤独,它属于BOM(Browser Object Model)浏览器对象模型这个大的类当中,BOM是很丰富的,这里简单介绍一下其他包含在BOM中的对象:

location对象:可获取所访问的URL的各种信息(协议、域名、端口号等)。

navigator对象:客户端信息(名称、网络、电池、地理位置、语言、多媒体设备等)。

screen对象:屏幕宽高、屏幕朝向等。

history对象:当前窗口用户的导航历史记录。

总结

行文至此,可以告一段落。

为什么说完变量就说对象?这和很多文章的逻辑都不一样,本文并没有意图说清楚所有对象,只是帮助大家理清这么个关系。

你也看到了,就算是基本类型,它们也有对象的表现,在使用过程中会触发对象的行为(访问属性、调用方法),另外有若干内置对象可供使用,如果没有“它们也是对象”的概念,后面很多东西就是说不通的,难免有困惑。

但这就是“一切”了吗?

JavaScript程序的整体是由ES、BOM、DOM文档对象模型(Document Object Model)组成,上文只提了ES和BOM,DOM同样也是对象,不仅如此,跟DOM紧密相关的“事件”也是,包括我们在业务当中会接触到的File,前后端请求用到的XMLHttpRequest等等,它们都是对象。

那么,回到文章开头,说JavaScript是基于对象的编程语言是相对准确的,它整个运行在由多种对象构成的系统里,建立了这样的认识之后,再去看JavaScript的程序,就清晰很多,因为大多的程序都只是在干一件事儿:

创建某个对象的实例,访问或者改变属性,调用方法。

好了,我们下篇文见~