什么是面向对象
面向过程和面向对象
面向过程的缺点:程序员编写的代码都是一些变量和函数,随着程序功能的不断增加,变量和函数就会越来越多,此时容易遇到命名冲突的问题,由于各种功能的代码交织在一起,导致代码结构混乱,变得难以理解、维护和复用。
面向对象的优点:将同一类事物的操作代码封装成对象,将用到的变量和函数作为对象的属性和方法,然后通过对象去调用,这样可以使代码结构清晰,层次分明。
面向对象的特征
- 封装性
- 继承性
- 多态性
封装性
隐藏内部的实现细节,只对外开放操作接口。
接口就是对象的方法,无论对象的内部多么的复杂,用户只需要这些接口怎么使用即可。
封装的优势:不管一个对象内部的代码经过了多少次修改,只要不改变接口,就不会影响到使用这个对象时编写的代码。
继承性
继承是指一个对象继承另一个对象的成员,从而在不改变另一个对象的前提下进行扩展。
继承的优点:可以保持接口兼顾的前提下对功能进行扩展;增强了代码的复用性,为程序的修改和补充提供便利。
多态性
指同一个操作作用于不同的对象,会产生不同的执行结果。
JS被设计成一种弱类性语言(一个变量可以存储任意类型的数据),就是多态性的表现。
自定义对象
对象的定义
对象的定义是通过“{}”语法实现的,对象的成员以键值对的形式存放在{}中,多个成员之间使用逗号分隔。
“{}”语法又称为对象的字面量语法,是指在源代码中直接书写的一个表示数据和类型的量。
访问对象成员
对象成员遍历
for ...in
- for...in中的变量保存了每个对象成员的名称
- 通过obj[]即可访问成员的值
- obj[]进行调试
深拷贝与浅拷贝
拷贝是指将一个目标数据复制一份,形成两个个体
深拷贝:如果将一个基本数据类型(数值,字符型)的变量赋值给另一个变量,就可以得到两个值相同的变量,改变其中一个变量的值,不会影响另一个变量的值。
实现深拷贝:1、基本数据类型(如数值、字符型):通过变量赋值即可实现
2、复合数据类型(如数组、对象):复制对象里的成员到另一个对象。
浅拷贝:如果操作的目的是引用数据类型(如数组、对象),则会在出现两个变量指向同一个对象的情况,如果改变其中一个对象的成员,另一个对象也会发生变化。
实现浅拷贝:1、复合数据类型(如数组、对象):通过变量赋值即可实现
2、提示:浅拷贝是复合数据类型中才有的概念
浅拷贝的优势:可以节省内存开销
构造函数
为什么用构造函数
通过字面量的方式创建对象
优点:简单灵活
缺点:当需要创建一组具有相同特征的对象时,无法通过代码指定这些对象应该具有哪些相同的成员。
通过工厂函数,虽然可以创建对象,但是其内部是通过字面量“{}”的方式创建对象的,还是无法区分对象的类型,此时使用另一种创建对象的方式——通过构造函数创建对象。
JS内置的构造函数
Object、String、Number等构造函数
“new构造函数名()”即可创建对象
我们习惯将使用new关键字创建对象的过程称为实例化,实例化后得到的对象称为构造函数的实例。
自定义构造函数
- 构造函数的命名推荐采用帕斯卡命名规则,及所有的单词首字母大写。
- 在构造函数内部,使用this来表示刚刚创建的对象。
在学习JS时,初学者经常会对一些相近的名词感到困惑,如函数、方法、构造函数、构造方法、构造器等。实际上,它们都可以统称为函数,只不过不同使用场景下的称呼不同。根据习惯,对象中定义的函数称为对象的方法,如obj.sayHello()可以称为obj对象的sayHellow()方法。而对于构造函数,也有一部分人习惯将其称为构造方法或构造器,我们只需明白这些称呼所指的是同一个事物即可。
class关键字
私有成员
在构造函数中,使用var关键字定义的变量称为私有成员。
实例对象后无法通过“对象成员”的方式进行访问,但是私有成员可以在对象的成员方法中访问。
私有成员体现了面向对象的封装性。
return关键字:
构造函数也是函数,因此构造函数中也可以使用return关键字,但是在使用时与普通函数有一定的区别。如果使用return返回一个数组或对象等引用类型数据,则构造函数会直接返回该数据,而不会返回原来创建的对象;如果返回的是基本类型数据,则返回的数据无效,依然会返回原来的创建的对象。
函数中的this指向
1、分析this指向
在JS中,函数内的this指向通常与以下3中情况有关。
- 使用new关键字将函数作为构造函数调用时,构造函数内部的this指向新创建的对象。
- 直接通过函数名调用函数时,this指向的是全局对象(在浏览器中表示window对象)。
- 如果将函数作为对象的方法调用,this将会指向该对象。
2、更改this对象
apply()方法,call()方法
相同点:都可以更改函数内的this指向,它们的第1个参数表示将this指向哪个对象,因此method()函数中通过this.name即可访问到传入对象的name属性。
不同点:apply()的第2个参数表示调用函数时传入的参数,通过数组的形式传递;而call()则使用第2~N个参数来表示调用函数时传入的函数。