OOP-面向对象开发(核心是封装)
//面向过程的封装: 封装一个自执行的匿名函数,这样内部的各种属性和方法就不会暴露在全局下面。面向过程可以暂时理解为函数
//类相当于一个工厂,只要给适当的参数就会生产出对应的对象。
//构造函数相当于接头人,负责接收参数,有固定名字: constructor
//实例化相当于生产的过程,就是类创建对象的过程
class car { //constructor构造函数用来接收参数 constructor([name,color]){ //这里函数的写法,跟对象里的简洁表示法一样 this.name = name; //this是在一个类中能被访问的一个关键字 this.color = color; //若没有this,下面实例化完的对象就访问不到这里的属性 this.number = 0; console.log(‘开始接收参数‘); console.log(name,color); } add(){ this.number += 1; //这里可以对constructor里面的参数进行更改 console.log(this.number); } } //只要class准备好了就可以实例化了 const che = new car([‘奔驰‘,‘红色‘]); //这里相当于调用constructor,括号里面可以传递参数 console.log(che); //上面有了this的关键字,这里就能访问到属性了 //实例化之后就可以调用里面的方法了 che.add();
一、封装
二、继承
三、多态: 同一个接口不同的表现
1、不会被类实例所拥有的属性与方法,只是自身拥有。
2、只能通过类调用。
第一种,在类里面,在constructor前面
//定义静态属性一: 在类里面 //static 属性名 = 属性值 static stt = ‘constructor前面定义的静态属性‘;
//修改静态属性
constructor(){
//对第一种方式的,静态属性进行修改。
//必须先实例化再调用才生效
haha.stt = ‘01234‘;
}
第二种,在类外面
// 定义静态属性二: 在类外面 // 定义静态属性: 类名 . 属性名 = 属性值 haha.str = ‘静态属性‘;
console.log(haha.stt); //调用静态属性,类名 . 静态属性名。 console.log(haha.str); //调用静态属性,类名 . 静态属性名。
//定义普通方法 bb(){ console.log(‘静态方法跟普通方法重名是没关系的,互不影响‘); } //定义静态方法 static bb(name){ console.log(‘我是静态方法,只有class才能调用。传进来的参数是:‘ + name); }
//调用静态方法,只有该class才能调用静态方法 haha.bb(‘大比‘); //类名 . 静态方法名
//类表达式 const p = class a{ //若不需要,a可以省略 constructor(){ console.log(‘这里p是 === a的,a仅限于内部使用‘); } } // new p(); //类的声明 class bb{ constructor(){ console.log(‘类的声明‘); } } // new bb(); //类的自执行 const aa = new class p{ constructor(){ p.a = 26; console.log(p.a); } }()
//类似给属性提供钩子。在获取属性和设置属性的时候做一些额外的事情
1、在字面量中书写get、set方法
const obj = { _name:‘123‘, get name(){ //获取name属性 console.log(‘这是获取name的操作‘); //除了返回name之外,还可以做其他额外的操作 return this._name; }, set name(val){ //设置name属性 this._name = val; } } //设置name,实际上是设置_name obj.name = ‘设置name,会自动调用set name方法‘; //获取name,实际上是访问_name console.log(obj.name); //name属性名 跟 get、set方法名不可以重名。 若重名会报错,栈内存溢出 //先触发get name,里面this.name访问了name,访问了name又会触发get name ......
2、Object.defineProperty
const obj = { _age : 2 } //设置/添加属性 Object.defineProperty(obj,‘age‘,{ //这里同样不能重名 //value : 88, get : function(){ console.log(‘正在使用get方法‘); return this._age; //这里同样不能重名 }, set : function(val){ console.log(‘正在使用set方法‘); this._age = val; //这里同样不能重名 } }); //第一个参数: obj是表示要操作的对象 //第二个参数: ‘age‘表示需要设置/添加的属性 //第三个参数: 一个对象,里面是对属性的描述 //value是属性的值,即第二个参数的值。 //是否可以枚举: enumerable:true 。默认是false,不可以被遍历出来 //描述还可以添加是否可读。 // console.log(obj); //里面多出了一个age属性。不过默认age属性是不可以被遍历出来的 //修改age obj.age = 88; //获取age console.log(obj.age);
class obj{ constructor(){ this._address = ‘北京‘; } get address(){ console.log(‘class里面的get操作‘); return `我家住在${this._address}`; } set address(val){ console.log(‘class里面的set操作‘); this._address = val; } } //首先需要实例化 const p = new obj(); //修改address p.address = ‘上海‘; //获取address console.log(p.address);
//name属性获取class类名 class bb {} console.log(bb.name); //类名是bb const aa = class {} console.log(aa.name); //类名是aa const cc = class dd{} console.log(cc.name); //类名是dd
//表示该类/函数自身。只有在类 或 构造函数/函数 里面才可以访问
ass Car{ //类使用 constructor(){ console.log(new.target); //打印出这个类本身 } } new Car(); //new后面的Car(),就是new.target属性 function ccr(){ //构造函数 或 普通函数都可以使用 //判断是否使用new关键字调用 if(ccr == new.target){ //还可以这样判断(this instanceof ccr),详情看下面构造函数流程 console.log(new.target); //只有构造函数调用,才能正常使用new.target }else{ throw Error(‘必须用new关键字调用‘); } } new ccr(); //构造函数调用 跟 普通函数调用 走的流程不一样 //1、构造函数this会先指向一个新的空对象 //2、构造函数的prototype属性会成为 空对象的原型
//3、this赋值给这个空对象
//4、执行函数 //5、若该函数没有返回值的话,则返回this。即之前那个空对象
1、构造函数this会先指向一个新的空对象
2、构造函数的prototype属性会成为 空对象的原型
3、this赋值给这个空对象
4、执行函数
5、若该函数没有返回值的话,则返回this。即之前那个空对象
//将构造函数的流程封装成一个函数 function Constructor(fn,args){ // 1、构造函数this会先指向一个新的空对象 // 2、构造函数的prototype属性会成为 空对象的原型 var _this = Object.create(fn.prototype); // var _this = Object.create(fn.prototype); // 3、this赋值给这个空对象 var res = fn.apply(_this,args); // var res = fn.apply(_this, args); // 4、执行函数 // 5、若该函数没有返回值的话,则返回this。即之前那个空对象 return res ? res : _this; // return res ? res : _this; } //构建一个普通函数 function Person(name,age){ this.name = name; this.age = age; } Person.prototype.say = function() { console.log(`我叫${this.name},我今年${this.age}岁了`); } //将 普通函数 转为 构造函数 var ren = Constructor(Person,[‘张三‘,88]); console.log(ren);
class fu{ //定义父类 constructor(name,sex,age){ this.name = name; this.sex = sex; this.age = age; } shuo(){ //定义父类的方法 console.log(`我的名字是${this.name},性别是${this.sex},年龄是${this.age}`); } } class zi extends fu{ //定义子类,并继承父类 constructor(name,sex,age,skill){ //接收父类的参数 和 子类的参数 super(name,sex,age); //给父类传入参数 this.skill = skill; } jineng(){ //定义子类的方法 console.log(`我的技能有${this.skill}`); } } const person = new zi(‘小蝴蝶‘,‘女‘,3,‘嚎啕大哭‘); //实例化子类,并传入参数 person.shuo(); //调用父类的方法 person.jineng(); //调用子类方法
//在继承中有使用到,在调用之后子类就会有父类的属性和方法。 //(相当于把子类的this放到父类的构造函数中运行一遍,后继续走子类的构造函数) class fu{ constructor(name){ this.name = name; } } class zi extends fu{ constructor(name,sex){ super(name); this.sex = sex; } methods(){ console.log(`我的名字是${this.name},我是${this.sex}生。`); } } const child = new zi(‘大白‘,‘女‘); child.methods();
//在调用super,父类的this始终是子类的this
//在非静态方法中访问super,访问的是父类原型 class fu{ fusay(){ console.log(‘您好!‘); } } class zi extends fu{ zisay(){ console.log(super.fusay); //这样的话调用子类的zisay就会输出父类的fusay函数本身。 //若要执行父类的fusay,这里也可以直接调用,如: super.fusay() } } const child = new zi(); child.zisay(); //作为对象调用的时候,不使用super也可以调用父类的方法
//在静态方法中访问super,访问的是父类 class fu{ } fu.total = 898; class zi extends fu{ static tot(){ console.log(super.total); } } zi.tot();
//验证: 调用super时,父类的this始终是子类的this class fu{ check(_this){ console.log(_this === this); //接收子类的this,并把子类的this与父类的this相比较 } } class zi extends fu{ use(){ super.check(this); //调用父类check方法,并把子类的this传过去 } } const yan = new zi(); yan.use(); //true
//同一个接口在不同的情况下,做不同的事情。即相同的接口,不同的表现
//好处: 提高类的扩充性和灵活性。还可以暴露接口,让子类完成父类不需要完成的内容。
class human{ say(){ console.log(‘我是人‘); } } class man extends human{ //多态必须要通过继承来实现。子类写一个父类同名的方法即可达到覆盖的效果 say(){ console.log(‘我是男人‘); } } class woman extends human{ say(){ console.log(‘我是女人‘); super.say(); //同样还可以调用父类的say方法 } } new man().say(); new woman().say();
//根据函数的参数类型、参数个数,让函数做不一样的事情
class simpleCalc{ addCale(...args){ if(args.length < 2){ return this.donot(); }else{ return this.add(args); } } donot(){ console.log(‘参数不够,不执行‘); } add(args){ console.log(args.reduce(function(a,b){return a+b})); } } new simpleCalc().addCale(1);
function P(){ //父函数 this.name = ‘萌萌‘; this.sex = ‘女‘; this.say = function(){ console.log(‘哈哈哈‘); } } P.prototype.test = function(){ //原型的属性和方法不可通过call继承 console.log(‘我是P的原型方法‘); } function C(){ //子函数 P.call(this); //这样就执行了P,并把当前this传过去。这样就继承了父函数的属性和方法,但不包括原型的属性和方法。 this.age = 18; this.address = ‘北京‘; } //若需要继承父函数的原型属性和方法,需要 C.prototype = new P(); //C的prototype 可以访问到 P的prototype //P的实例也就是P函数的prototype属性,所以P的实例可以访问到P.prototype的方法 var bb = new C(); //实例化子函数 bb.say(); //成功执行父函数的方法 console.log(bb.address); //成功执行子函数的属性 bb.test(); //成功执行父函数的原型方法
原文:https://www.cnblogs.com/mingliangge/p/12620154.html