面向对象
1 面向对象OOP
1.1 面向对象
对象:程序描述现实中一个具体事物的属性和功能的程序结构。事物的属性会成为对象的属性,事物的功能会成为对象的方法。
面向对象编程: 程序都是先将数据和功能定义在对象中,再按需使用对象的数据和功能
优点:便于大量数据和功能的维护和使用
特点:封装、继承、多态
1.2 封装
封装:创建对象集中保存一个事物的属性和功能,便于大量数据和功能的管理和使用
面向对象编程都要先封装对象,再按需使用对象的属性和功能
-
用{}直接量:
var 对象名={ 属性名:属性值, ... : ... , 方法名:function(){ ... this.成员 ... } }
对象的方法要访问对象自己的属性或方法,无法直接使用,这是因为所有对象的属性和方法默认都不在作用域链当中,任何程序无权自动进入对象中获取成员(没有加任何前缀的普通变量只能在当前作用域查找)
解决方法:不能直接在方法中使用对象名.成员名(紧耦合,每次更改需要同时改变函数内部的变量);只要对象自己的方法,要使用自己的成员,都要加this.属性名
this指定对象自动获得正在调用方法的.前的对象名,只与调用方法时的前缀有关,而与定义方法时的前缀无关 -
用new创建
//创建空对象 var obj=new Object(); //添加新成员 obj.属性名=值; obj.方法名=function(){};
本质:js中一切对象底层都是关联数组
js中对象的特点:- 对象创建完成后,依然可继续添加新成员
- 访问对象中不存在的属性,不报错!而是返回undefined
- 访问对象的成员,既可用.,又可用[“成员名”]
选择: 如果成员名是变化的,就必须用[变量]
如果成员名固定,就可用.简化与[""]等效
缺点: 一次只能创建一个对象;如果反复创建多个相同结构的对象时,代码重复很多
-
用构造函数创建统一类型的多个对象:
构造函数: 描述一类对象统一结构的函数,重用创建对象的代码!-
定义构造函数:
function 类型名(属性参数...){ this.属性名=参数; this.xxx = 值; this.方法名=function(){ ...this.属性名 ... } }
-
调用构造函数反复创建多个对象:
var obj=new 类型名(属性值,…)
new:- 创建一个新的空对象:var obj = {};
- 让子对象自动继承构造函数的原型对象obj.__proto__ = Base.prototype;
- 调用构造函数: Base.call(obj);
通过强行赋值的方式,为新对象obj添加规定的新属性,保存属性值 - 返回新对象地址
构造函数中的方法定义,会在每个子对象中重复创建函数对象,浪费内存!
-
1.3 继承
从其他对象获得属性和方法,父对象的成员——子对象无需重复创建,就可直接使用(代码重用,节约内存);
只要多个子对象需要相同的属性值和方法就使用原型对象(集中保存子对象共有成员的父对象)继承
通过原型实现继承:Constructor.prototype
1.3.1 原型对象
原型对象:不用创建,定义构造函数时,自动生成的构造函数.prototype
继承: new的第2步,子对象自动继承构造函数的原型对象(子对象.__ proto__=构造函数.prototype)
向原型对象中添加共有成员:
构造函数.prototype.成员=值/function(){…}
自有属性(constructor,通过对象引用添加)和共有属性(prototype原型对象继承而来): 原型对象中的属性和对象自己的属性是有区别的,对象自己的属性是可配置(用子对象修改)可枚举(遍历)的,而我们在原型中添加的属性是可枚举但不可配置(只能用原型对象修改)的;
内置对象的构造函数和原型对象:
内置对象: ES标准中规定的,浏览器已经定义好的可直接使用的对象,包括11个
String Number Boolean——包装类型
Array RegExp Date Math
Error
Function Object
global(window)
除了Math和global外,其余都是构造函数,包含2部分组成: 构造函数——创建该类型的子对象;原型对象—— 保存该类型的所有子对象共有的成员
变量先在构造函数中找,再从原型对象中找,所以自有属性会覆盖原型对象中共有属性的定义
1.3.2 原型链
原型链prototype chain,由多级父对象逐级继承形成的链式结构,包含了所有对象的属性和方法,控制属性和方法的使用顺序以及共享范围。
原型对象中的__proto__属性默认指向Object.prototype;Object处于原型链顶级,其原型对象__proto__的值为null
1.3.3 判断引用对象类型
-
判断原型对象:
判断Array.prototype是不是obj的父级原型对象
var bool=Array.prototype.isPrototypeOf(obj) -
判断构造函数:
obj.constructor === Array
var bool = obj instanceof Array
检查整个原型链 -
检测对象是否具备指定自有属性
obj.hasOwnProperty(‘name’)
1.4 多态
以多种不同的形式运行函数或方法
- 重载overload
相同函数名,不同参数列表的多个函数,可根据传入参数的不同自动选择对应函数调用。
减少API数量以减轻调用者的负担
js不允许重载,因为不允许同名函数同时存在,可以通过判断arguments.length统计参数个数来选择执行
arguments是类数组对象(可以使用下标、length、遍历,但不能使用数组API),是函数自动创建的接收所有传入参数的对象。 - 重写override
子对象中定义和父对象API同名成员
2 补充
2.1 静态方法static
静态方法:无需创建子对象,可以使用构造函数直接调用的方法(部分方法不确定使用的对象类型)
在函数外部使用构造函数名.静态方法名=function(){}定义,通过构造函数名.静态方法名()调用。
实例成员,只能由实例化的对象来访问;静态成员,只能由构造函数本身来访问
2.2 自定义继承
- 仅修改一个实例对象的父对象
child.__ proto__=father.prototype
Object.setPrototypeOf(child,father) - 修改所有子对象的父对象
构造函数.prototype=new Father实质是修改构造函数的prototype属性,必须在创建子对象之前 - 两种类型之间的继承
当多个类型之间拥有部分相同的属性结构和方法定义- 定义抽象父类型
在构造函数中定义相同的部分属性;
在原型对象中定义相同的方法 - 子类型继承父类型
子类型原型对象继承父类型原型对象:
child.prototype.__ proto__=parent.prototype
子类型构造函数借用父类型构造函数
在子类型构造函数中调用父类型函数:parent.call(正确的对象this,参数,…)
不使用call调用会导致父类型构造函数的所有属性泄漏到全局,因为不使用new和.调用的函数this默认指向window
- 定义抽象父类型
对基本继承理解的代码
使用child.prototype.__proto__=father.prototype只继承原型对象,无法使用父对象的实例;而使用child.prototype=new father(),子对象继承父对象的完整实例
<!-- 1.创建对象 -->
<!-- 2.构造函数 -->
<!-- 3.构造函数+原型 -->
<!-- <script>
function Duck(name, word){
this.name=name;
this.word=word;
}
Duck.prototype.say=function(){
console.log(this.name+" say: "+this.word);
}
var duck=new Duck("jerry","Hi");
duck.say();
</script> -->
<!-- 4.寄生构造 -->
<!-- <script>
function Duck1(name,word){
var obj=new Object();
obj.name=name;
obj.word=word;
obj.say=function(){
console.log(this.name+" say: "+this.word);
}
return obj;
}
var duck1=new Duck1("jecy","you");
duck1.say();
</script> -->
<!-- 原型继承 -->
<script>
var Fn = function() {
this.property = true;
}
Fn.prototype.getFnProperty = function() {
console.log( this.property );
};
var SubFn = function() {
// this.property=2;
this.subProperty = false;
};
//SubFn继承了Fn的实例
// SubFn.prototype = new Fn();
SubFn.prototype.__proto__=Fn.prototype;
//为实例添加额外的实例方法;
SubFn.prototype.getSubProperty = function(){
console.log(this.subProperty);
};
var subFn = new SubFn();
subFn.getFnProperty(); //输出了true
subFn.getSubProperty(); //输出了false
SubFn.prototype.constructor = SubFn;
console.log(subFn.subProperty,subFn.property);
/*现在subFn的constructor 是
function () {
this.property = true;
};
所以要修正SubFn.prototype.constructor = SubFn
*/
</script>
<!-- 对象继承Object.create() -->
<!-- <script>
var Fn = function() {
this.property = true;
}
Fn.prototype.getFnProperty = function() {
console.log( this.property );
};
var SubFn = function() {
this.subProperty = false;
};
//SubFn继承了Fn的实例
SubFn.prototype = new Fn();
//为实例添加额外的实例方法;
SubFn.prototype.getSubProperty = function(){
console.log(this.subProperty);
};
var subFn = new SubFn();
subFn.getFnProperty(); //输出了true
subFn.getSubProperty(); //输出了false
SubFn.prototype.constructor = SubFn;
console.log(subFn.subProperty,subFn.property);
/*现在subFn的constructor 是
function () {
this.property = true;
};
所以要修正SubFn.prototype.constructor = SubFn
*/
var newSubFn=Object.create(subFn, {
getFnProperty:{
value:1
}
})
console.log(newSubFn.getFnProperty);
</script> -->
<!-- class -->
<script>
class A {
constructor(a,b){
this.a=a;
this.b=b;
}
sum(){
return this.a+this.b;
}
static len(){
return "000";
}
}
// var err=new A(1,2);
// console.log(err.sum(),A.len());
class B extends A{
constructor(a,b,c){
super(a,b);
this.c=c;
}
sumAll(){
return this.a+this.b+this.c;
}
}
var err=new B(1,2,3);
console.log(err.sum(),err.sumAll());
</script>
链接: js继承和继承基础
https://www.cnblogs.com/diligenceday/p/4246515.html