js面向对象
JavaScript面向对象
1.面向对象编程
两大编程思想:
面向过程编程(POP) - Process-oriented Programming
这种编程思想需要分析出解决问题所需的步骤,然后利用函数将每一步实现,在使用时依次调用。
面向对象编程(OOP) - Object-oriented Programming
这种编程思想需要把事务分解成一个个对象,然后由对象之间分工与合作。
每一个对象都是功能中心且分工明确,OOP具有灵活、代码可复用、容易维护和开发的优点。
2.ES6中的类和对象 - ES6新增了类的概念
当用面向对象的思想描述现实事物的时候,事物可以分为具体事务和抽象事物。
面向对象的思维特点:
- 抽取抽象对象的公共属性和方法封装成一个类
- 对类进行实例化,得到一个具体的对象
3.创建类和实例的方法
class Name {};//创建类
var obj = new Name(); //创建实例
4.类constructor构造函数
constructor()构造函数(方法)是一种用于创建和初始化创建的对象的特殊方法,并没有返回值。在new命令生成实例对象的时候自动调用该方法,如果没有显示的定义,类的内部会自动创一个constructor(),用于传递参数,返回实例对象。
class Person {
constructor(name, age) {
this.name = name;
this.age = age;}
}
当创建刘昕瑶实例 var lxy = Person(‘刘昕瑶’,25)的时候,会自动调用并且给刘昕瑶实例添加name和age属性。
【思考】:所以类中的共有属性可以添加到constructor构造函数中,那么方法呢?
5.类添加方法
在类中,所有的方法不需要function关键字也不能加逗号分割
e.g. 还拿4中的Person类举例
class Person {
constructor(name, age) {
this.name = name;
this.age = age;}
say() {
console.log( ‘hi’ + this.name )}
}
6.类的继承
继承:子类可以继承父类的一些属性和方法,通过extends关键字
class Father {}
class Son entends Father {//子类继承父类
}
【问题:如果父类里某个方法是sum(){this.x+this.y},子类在继承的时候也想使用sum方法,如何调用父类的构造函数呢?】
6.1 super关键字
super关键字用于访问和调用对象父类上的函数,且可以调用父类的构造函数,也可以调用普通函数。
class Father {
constructor(x,y) {
this.x = x;
this.y = y;}
sum() {
console.log(this.x + this.y)}
}
class Son extends Father{
constructor(x,y) {
super(x,y);//调用父类的构造函数
}
}
var son = new Son(1,2)
son.sum()
7. 构造函数
引入:在es6之前,js中并没有引入类的概念,所以在es6之前对象都是用构造函数来定义对象和他们的特征
回忆:创建对象的三种方式:
字面量: var obj1 = {}
new关键字:var obj2 = new Object()
自定义构造函数: function Star(uname, age) {
this.name = uname;
this.age = age;
this.sing = function() {
console.log(‘llalalalala’)}}
7.1 构造函数
构造函数是一种特殊的函数,没有返回值。主要用于初始化对象,他总与new一起使用。
我们可以把对象中一些公共的属性和方法抽象出来,封装到这个函数中。
7.2 new关键字执行时操作
new在执行时会:
- 在内存中创建一个新的空对象
- 让this指向这个新的对象
- 执行构造函数里的代码,给这个对象添加属性和方法
- 返回这个新的对象(故不需要return)
7.3 成员
在构造函数中的属性和方法我们称为成员,成员可以在构造函数本身上添加,也可可以在构造函数内部this上添加。
function Star(uname, age) {
this.name = uname;
this.age = age;}
Star.sex = ‘女’;
在构造函数本身上添加的成员称为静态成员,只能由构造函数本身来访问
console.log(Star.sex)
在构造函数内部创建的成员称为实例成员,只能由实例化的对象来访问
var lxy = Star(‘Lxy’, 25);
console.log(lxy.name)
【问题:浪费内存】
8. 构造函数原型 prototype
在2中构造函数存在一个问题,两个不同的对象中相同的function会占用两份内存空间,造成内存的浪费,如何将不同对象使用同一个函数?
8.1 构造函数原型 prototype
原型:构造函数通过原型分配的函数是所有对象共享的。js规定, 每一个构造函数都有一个prototype属性,指向另一个对象。这个对象所有的属性和方法都会被构造函数所拥有。
故,我们可以把不变的方法直接定义在prototype对象上,这样所有的对象实例可以共享这些方法。
【一般,公共属性定义在构造函数里,公共方法定义在原型对象中】
还是以上面的Star构造函数为例:
Star.prototype.sing = function() {
console.log(‘lalalala’)}
var ldh = Star(‘刘德华’, 18);
var zxy = Star(‘张学友’, 19);
console.log(ldh.sing === zxy.sing);//true
8.2 原型对象 __proto__
对象都会有一个属性 __ proto __,指向构造函数的prototype原型对象。
之所以对象可以使用构造函数中的prototype原型对象的属性和方法,就是因为对象中__ proto __原型的存在。

对象原型存在的意义就是为对象的查找机制提供一个方向,在实际开发中不可以使用这个属性,只用来在内部指向prototype原型对象。
9. constructor构造函数
对象原型__ proto __ 和构造函数原型对象prototype里都有一个属性:constructor构造函数,指回构造函数本身。
constructor主要用于记录该对象引用于哪个构造函数,并让原型对象指向原来的构造函数。
一般情况下,对象的方法都在构造函数的原型对象中设置。
但是如果有多个对象的方法,我们可以给原型对象采取对象形式赋值,但是这样就会覆盖构造函数原型对象原来的内容,这样修改后的原型对象的constructor就不再指向当前的构造函数了。
此时,可以在修改后的原型对象中,添加一个constructor指向原来的构造函数。

总结:构造函数Star、实例ldh、原型对象Star.prototype之间的关系

10. 原型链
当访问一个对象的某个属性时,会现在这个对象本身属性上找,如果没找到就会通过__ proto __ 隐形属性找到它的构造函数的原型对象,如果还没有,就继续在构造函数的prototype(也是对象,也有__ proto __ ) 中的__ proto __里去找。这样一层层向上查找就会形成一个链式结构,称为原型链。

javascript的成员查找机制:按照原型链,一直找到null
所以说__ proto __ 对象原型的意义就是在于为对象成员查找机制提供一个方向。
reference:一文搞懂JS原型与原型链(超详细,建议收藏) - 掘金 (juejin.cn)
11. 原型对象中的this指向问题
- 构造函数中的this指向实例对象。
- 原型对象里面放的是方法,方法里的this指向的是方法的调用者,也就是实例对象。
12. es6之前的继承
es6之前并没有extends继承,但是我们可以通过构造函数+原型对象这种组合继承的方式模拟实现继承
12.1 call():调用,修改函数运行时的this指向

12.2 借助构造函数继承父类属性
核心原理:通过call()将父类的this指向子类的this从而实现继承。

12.3 借助原型对象继承父类方法
核心原理:将子类的共享方法提取出来,让子类的prototype原型对象 = new 父类(),将子类的constructor重新指向子类的构造函数。
reference:[(70条消息) 借用原型对象继承父类型方法_susu1198的博客-CSDN博客](