JS — 对象的基本操作
js面向对象系列教程 — 对象的基本操作
面向对象概述

面向对象(object oriented)简称oo,它是一种编程思维,用于指导我们如何应对各种复杂的开发场景。
这里说的对象(object),意思就是事物,在面向对象的思维中,它将一切都看作是对象,并以对象为切入点去思考问题。
使用面向对象的思维去开发程序,我们首先思考的是这个系统中有哪些对象(事物),它们各自有什么属性(特征),又有什么方法(行为),这样一来,就可以把系统分解为一个一个的对象,然后对每个对象进行单独研究,以降低系统的整体复杂度。
学习面向对象,我们不仅要学习它的技术知识,更重要的是学习这种思考程序的方式,这种思维方式跟我们过去开发程序有很大的区别。在过去,我们完成一个功能的时候,往往思考的是完成该功能的步骤,先做什么,再做什么,如果怎么样,就怎么怎么样……,这种过去的思维模式,我们称之为面向过程。
面向过程并不是错误的,只是它面对复杂的问题时显得有些捉襟见肘。
面向过程和面向对象最大的区别在于,面向过程思考的重心和切入点是事情,面向对象思考的重心和切入点是事物。
在面向对象的世界中,它将一切都看作是对象。这里的对象,包罗万象,它可以是现实世界中看得见摸得着的实体:人、小猫、小狗、飞机、课桌、铅笔、电视等等等等,都可以看作是对象。它也可以是某些领域中的抽象体:订单、价格计算器、dom元素、日期等等等等,它们也被看作是对象。
因此,在oo的世界里,有一句至理名言——一切皆为对象。
javascript语言是支持面向对象开发的语言,本系列教程中,将一步步讲解如何使用它进行面向对象开发,同时会进一步探寻它背后深层次的原理。
注意:本系列教程的重心,将放在面向对象开发的技术层面(即面向对象编程,object oriented programming,oop)。
对于面向对象思想层面(即面向对象设计,object oriented design,ood),本教程不做过多讲解。因为面向对象思想的建立并非一朝一夕,它不是靠读文章读出来的,虽然好的文章可以对你有启发作用,但更多的,这种思想是靠常年累月的代码量练出来的,是靠不断的分析、重构想出来的。
本教程希望的是,你能够通过学习面向对象开发,激发你的思考,引起你对另一种开发方式的关注,逐渐建立起面向对象的思维。
创建对象
对象即事物,一切皆对象。
其实你在之前的开发中,应该不止一次的接触过对象,有些是浏览器自带的对象,比如:dom对象、window对象、document对象等;有些则是你根据功能需要自己创建的,比如:用户对象、学生对象等。
在js中,创建一个对象非常的简单,通过一对{}即可创建一个对象,下面的代码你应该并不会陌生:
const user1 = { loginid: "bangbangji", loginpwd: "123456", name: "棒棒鸡"};const user2 = { loginid: "xiaobaitu", loginpwd: "654321", name: "小白兔"};
上面的代码应该这样理解,创建了两个对象,分别把它们赋值给了变量user1和user2。
为什么要强调这一点呢?因为真正的对象不是user1和user2,而是那一对大括号及其里面的内容{...},在后面讲解对象赋值原理时还会详细讲解这一点。
上面这种创建对象的写法,即使用两个大括号来表示对象{...},叫做对象的字面量表示法。
使用字面量表示法来创建对象的好处是简单易懂,但它不好的地方在于无法应用面向对象开发中的一些高级特性。
下面的代码,展现了另一种创建对象的方式,即使用关键字new来创建对象:
const user1 = new object(); //相当于:const user1 = {};user1.loginid = "bangbangji"; user1.loginpwd = "123456"; user1.name = "棒棒鸡";const user2 = new object(); //相当于:const user2 = {};user2.loginid = "xiaobaitu"; user2.loginpwd = "654321"; user2.name = "小白兔";
这样的代码虽然看似繁琐了很多,但实际上它才是对象原本的创建方式。上面的代码和第一种方式效果完全一致。
实际上,在js语言中,所有的对象都是使用new关键字创建的。即便你使用的是字面量表示法,js引擎最终也会将其转换为这种方式。因此,对象的字面量表示法仅仅是js语言的一个语法糖,要创建对象,最终都是要使用new关键字的。因此,在后文中,每一个示例,我都会用两种方式来实现对象的创建。
语法糖(syntactic sugar),也译为糖衣语法,是指语言层面的一些便捷语法,它仅仅是为开发者提供的一种高效的编码方式,并不改变底层的实现原理。
属性和方法
我们过去使用对象,往往是为了将多个变量整合到一个变量上,其实,这种思想就是面向对象的一种核心思想——封装。
面向对象有三大显著特征:封装、继承、多态
那些整合到一个对象中的变量,我们称之为对象的属性。属性往往是一个名词,表示对象所拥有的特征。
const user1 = { loginid: "bangbangji", //loginid为对象的属性,表示用户账号 loginpwd: "123456", //loginpwd为对象的属性,表示用户密码 name: "棒棒鸡" //name为对象的属性,表示用户姓名};const user2 = new object(); user2.loginid = "xiaobaitu";//loginid为对象的属性,表示用户账号user2.loginpwd = "654321";//loginpwd为对象的属性,表示用户密码user2.name = "小白兔";//name为对象的属性,表示用户姓名
对象的属性还可以是一个对象或者数组或者其他任意类型,用于表示多层次的结构:
const user1 = { loginid: "bangbangji", loginpwd: "123456", name: "棒棒鸡", //属性address也是一个对象 address: { province: "四川省", city: "成都市" } };//输出:四川省-成都市console.log(`${user1.address.province}-${user1.address.city}`); const user2 = new object(); user2.loginid = "xiaobaitu"; user2.loginpwd = "654321"; user2.name = "小白兔"; user2.address = new object(); //属性address也是一个对象user2.address.province = "四川省"; user2.address.city = "南充市";//输出:四川省-南充市console.log(`${user2.address.province}-${user2.address.city}`);
对象除了有特征,还会有一些行为,这些行为我们称之为对象的方法。方法往往是一个动词,表示对象所拥有的行为,在代码层面,方法表现为函数。
const user1 = { loginid: "bangbangji", loginpwd: "123456", name: "棒棒鸡", //sayhello(打招呼)属于对象方法,它是一个函数,表示对象的行为 sayhello: function () { console.log("你好!"); } }; user1.sayhello(); //调用对象的方法,输出:你好!const user2 = new object(); user2.loginid = "xiaobaitu"; user2.loginpwd = "654321"; user2.name = "小白兔";//sayhello(打招呼)属于对象方法,它是一个函数,表示对象的行为user2.sayhello = function () { console.log("你好!"); }; user2.sayhello(); //调用对象的方法,输出:你好!
可以看出,方法和属性在书写上并没有本质的差别,只不过属性是一个普通的值,方法是一个函数而已。
this关键字
我们现在考虑一下,在之前的示例中出现的sayhello方法,假设我们现在要对其功能做一点改造,我们希望该方法要输出对象的姓名和账号,于是,得到了下面的代码:
const user1 = { loginid: "bangbangji", loginpwd: "123456", name: "棒棒鸡", //sayhello(打招呼)属于对象方法,它是一个函数,表示对象的行为 sayhello: function () { console.log("你好!我是棒棒鸡,我的账号是bangbangji"); } }; user1.sayhello(); //调用对象的方法,输出:你好!我是棒棒鸡,我的账号是bangbangjiconst user2 = new object(); user2.loginid = "xiaobaitu"; user2.loginpwd = "654321"; user2.name = "小白兔";//sayhello(打招呼)属于对象方法,它是一个函数,表示对象的行为user2.sayhello = function () { console.log("你好!我是小白兔,我的账号是xiaobaitu"); }; user2.sayhello(); //调用对象的方法,输出:你好!我是小白兔,我的账号是xiaobaitu
这种方式结果固然正确,但是考虑一下100个用户的场景……不敢想象是不?这还仅仅是一个打招呼的方法,如果方法里面代码多一点,复杂一点,会更加棘手。
由于同一类对象(比如这里的用户对象)都具有共同的行为——打招呼,我们何不把这个方法函数提出来呢?就像下面这样:
//提取出来的共同方法function sayhello(){ //怎么办?输出哪个对象的姓名和账号? console.log("你好!我是???,我的账号是???"); }const user1 = { loginid: "bangbangji", loginpwd: "123456", name: "棒棒鸡", sayhello }; user1.sayhello(); //输出:你好!我是???,我的账号是???const user2 = new object(); user2.loginid = "xiaobaitu"; user2.loginpwd = "654321"; user2.name = "小白兔"; user2.sayhello = sayhello; user2.sayhello(); //输出:你好!我是???,我的账号是???
这样虽然解决了代码重复的问题,但新的问题又来了,虽然我们知道每个用户对象都具有打招呼的方法,但是每个对象的属性值不一样,打招呼的时候,我到底输出哪个对象的属性值呢?
于是,js给我们提供了一个关键字this,它指代的是当前对象,即调用方法的对象,于是我们就可以解决这个问题了:
//提取出来的共同方法function sayhello(){ //this指代当前调用方法的对象 console.log(`你好!我是${this.name},我的账号是${this.loginid}`); }const user1 = { loginid: "bangbangji", loginpwd: "123456", name: "棒棒鸡", sayhello };//user1调用sayhello时,方法运行过程中的this指代user1user1.sayhello(); //输出:你好!我是棒棒鸡,我的账号是bangbangjiconst user2 = new object(); user2.loginid = "xiaobaitu"; user2.loginpwd = "654321"; user2.name = "小白兔"; user2.sayhello = sayhello;//user2调用sayhello时,方法运行过程中的this指代user2user2.sayhello(); //输出:你好!我是小白兔,我的账号是xiaobaitu
我们在sayhello函数中使用了this关键字,当函数运行时,该关键字指代调用该函数的对象,即谁在调用我,我里面的this就指代谁。在阅读代码的时候,你可以把this读作我的,以便于你理解它。
借助了神奇的this关键字,就解决了重复代码的问题,之后无论创建多少个用户对象,我们都可以使用同一个方法了。
关于this关键字,目前你需要记住以下两点:
this关键字只能书写在函数内。
this关键字在函数运行的时候才能确定指代的是谁,函数运行前谁也无法知道它将指代谁。
由于javascript语言本身的特性,函数中的this关键字会带来很多坑(比如直接调用sayhello函数,this指代谁呢?)。后面会专门拿一章出来讲解this。
总结
面向对象(oo)是一种编程思维,以对象为切入点分析解决问题,这种思维需要长期的练习才能逐渐建立,而我们学习的是面向对象技术层面的东西。
创建对象可以使用字面量表示法和new关键字,字面量表示法是一个语法糖,js引擎最终会将其变为new关键字方式创建对象。
对象包含属性和方法,分别表示对象的特征和行为,它们没有本质的区别,只是行为提现为一个函数
在函数中可以使用this关键字来指代**当前对象(调用函数的对象)
上一篇: [日常] 腾讯云发送邮件失败问题
下一篇: 程序员50题(JS版本)(二)