面向对象概念及简单应用

创建构造函数

和普通函数的书写方式一样(命名以大驼峰方式)

let num=123;
console.log(num,num.length,typeof num);//123,undefined,"number
let f70=num.toString();
console.log(f70,typeof f70);

字符串可以直接使用String的方法和属性

console.log("123".length);
基本数据类型:数值不可以直接使用toString方法
原因:数组分整型和浮点型(小数)
123.toString() 123后边的点,会被视为小数点,不会装箱为对象
解决方法如下
console.log((123).toString());

自动拆箱:自动装箱成对象,然后自动变成基本数值类型。

let num=new String(456);
num.abc=123;
console.log(num.abc);//123
console.log(456+1);//4561
console.log(456-1);//455

构造函数

工作原理
1、创建空对象,执行代码
2、执行代码,返回值
3、在外部调用
​
function Students(name,age,gender,weight){
  当创建构造函数时,会自动添加this对象
 let this={};然后向里添加
  this.name=name;
  this.age=age;
  this.gender=gender;
  this.weight=weight;
  之后返回this对象
  return this 默认值
  如果返回值是手动写的基本数据类型
  如 return 123;   权重没有自动生成的返回值高,所有没有用的。
  如果返回的是对象,
  如 return [123];  返回值是undefined;
}
当用调用时,在返回值this中取值
用new的构造函数,返回值一定是个对象
let stu1=new Students("xiaoguo",18,"male","100kg");
let stu2=new Students("xiaomi",15,"female","10kg");
console.log(stu1.name);
console.log(stu2.gender);

面向过程:把解决问题的方法和步骤,一步一步写出来(JS代码)

Oriented Object Analysis Design 面向对象分析设计

面向对象:把解决问题的方法和步骤中

涉及到的属性和方法,封装成一个函数(对象)

即对象的抽象;

类 Class

类是对象的的抽象,对象是类的实例

ES6 类 语法糖
class 首字母大写的名字 {}
class Students{
  name="123";
  sayhello=function(){
  console.log(`hello,${this.name}`);
  }
  haha(){
console.log("haha");
  }
}
let stu=new Students();
console.log(stu.name);
stu.sayhello();
stu.haha();

ES6 类 语法糖

注:类似封装一个构造函数

typeof 类实际情况就是function
普通函数和构造函数本质区别:调用方式
1、普通函数:函数名()
2、构造函数:new 函数名(){}
3、类:   new 类名(){}
class Students{
  // constructor构造器
  constructor(name,age,gender){
this.name=name;
this.age=age;
this.gender=gender;
  }
  sayhello(){
console.log("hello", this.name);
  }
}
ES5写法
function Teachers(name, age, gender) {
  this.name = name;
  this.age = age;
  this.gender = gender;
  this.sayhello=function(){
console.log("hello", this.name);
  }
}
let tex=new Teachers("l4",28,"female");
Students.prototype.banjiName="f70";

类添加原型是 类名.prototype.对象名

其他实例都可以用

let stu=new Students("z3",18,"male");
​
let stu1=new Students("l3",18,"male");

实例添加原型是 类名.proto.对象名;

其他实例都可以用

stu.__proto__.isRich=true;
​
console.log(stu.isRich);
​
console.log(stu1.isRich);
let stu2=new Students();
​
ES5可以使使用普通函数调用方法,ES6会报错,提示调用前加new
​
let stu2=Students();

私有:

this.XXX=XXX;
​
this.XXX=function(){}

公有:

类.prototype.XXX=XXX;
​
类.prototype.XXX=function(){};
​
实例.__proto__.XXX=XXX;
​
实例.__proto__.XXX=function(){};

注:同类别的实例对象可以使用公共的属性和方法

new 一个实例的工作流程

1、this的空对象

2、this的"proto"指向"类.prototype"

3、执行代码(给this添加属性和方法)

4、return this(即返回对象给实例)

注:手动更改返回值,更改的基本数据类型无效

更改引用数据类型有效

【this指向】

1、普通对象,谁调用指向谁

2、构造函数中的this,指向的是实例化的对象

3、监听器中的this,指向的是事件源:绑定事件的节点

4、普通函数中的this,指向的是window

注:严格模式下( "use strict"),指向的是undefined;

【1】
function f70(){
  this.name=10
  console.log(this.name);
}
let stu=f70();
【2】
function f70(){
  this.name=10;
}
let stu=new f70();
stu.name=20;
let stu1=new f70();
console.log(stu.name);
console.log(stu1.name);
【3】
document.addEventListener("mouseover",function(){
  console.log(this);
})
【4】
严格模式
function f70(){
  "use strict";
  console.log(this);
}  
f70();
5、箭头函数中this的指向,取决于所处环境
箭头函数没有自己的arguments和this
class Students{
  constructor(name){
•    this.name=name;
•    this.shuchu();
  }
  shuchu(){
•    console.log(this.name);
  }
}
let stu1=new Students("xiaoguo");
console.log(stu1);
Object.assign(stu1,{gender:true},{name:"gao"});
console.log(stu1);

 

改变this的指向

function f70(){
  this.name="a";
  this.say=function(weight,height){
•    console.log(this.name,weight,height);
  }
}
let stu1=new f70();
let stu2={
  // name:"xiaoguo"
}
谁调用.方法名.call(指向谁的原型,函数上传参数,);
立即执行
​
stu1.say.call(stu2,"70kg","188");
谁调用.方法名.apply(指向谁的原型,函数上传参数用数组方式);
立即执行
​
stu1.say.apply(stu2,["65kg","175"]);
谁调用.方法名.bind(指向谁的原型,函数上传参数)有返回值,返回的是一个函数;
用变量接受后,取决于我什么时候用它;
写法1
​
let xiaoguo=stu1.say.bind(stu2,"80kg","190");
xiaoguo();
​
写法2 可以先接受函数时传参,也可以调用时传参
​
let xiaoguo=stu1.say.bind(stu2,"80kg");
xiaoguo("190");

作用域链是找变量的

原型链

1、在我自身找属性,没有做第2步

2、在原型找属性,没有有做第3步

3、在原型.proto里找

每一个函数都有一个prototype属性叫原型
let fn=new Function();
console.log(Function.prototype);//原型
console.log(fn.__proto__);//原型
console.log(fn.__proto__.__proto__);//{} Object
console.log(fn.__proto__.__proto__.__proto__);//null

面向对象的3大特征

1、封装

2、继承

3、多态

【封装】

ES6 把属性和方法封装成一个对象

【多态】

同一个磨具,生产出不同的产品

同一个构造函数或类、实例化不同的对象(根据参数的不同);

不同的实例对象,调用同一个方法,得到不同的结果

【继承】

function Grandpa(faqi){
  this.faqi=faqi;
}
let g1=new Grandpa("大保健");
function Father(money){
  this.money=money;
}
let f1=new Father(10000);
function Son(age){
  this.age=age;
}
let s1=new Son(80);
Object.assign(s1.__proto__.__proto__,g1);
// Object.assign(Son.prototype,f1);
// ES5写法
// s1.__proto__=new Father(10000);
console.log(s1.money);
console.log(s1.age);
console.log(s1.faqi);
ES6继承
class Father{
  constructor(money){
•    // this.name=name;
•    this.money=money;
  }
  say(){
•    console.log(this.name,this.age);
  }
  hello(){
•    console.log(1);
  }
}
class Son extends Father{
  constructor(name,age,money){
•     super必须放在this之前
•    super(money);
•    this.age=age;
•    this.name=name;
  }
  say(){
•    console.log(this.name,this.age);
  }
}
 let s1=new Son("z3",12,10000);
console.log(s1.money);
s1.hello();
function Grand(money){
  this.money=100;
}
Grand.prototype.say=function(){
  console.log(22);
}
function Father(age){
  // this={}
  // this.__proto__= Father.prototype
  this.xiaoguo=Grand
  this.xiaoguo();
  delete this.xiaoguo;
  this.age=18;
}
Father.prototype.hello=function(){
  console.log(11);
}
function Son(name){
  // 如何拿到名字
  // this={}
  // this.__proto__= Son.prototype
  this.xiaoguo=Father
  this.xiaoguo();
  delete this.xiaoguo;
  this.name=name;
}
Son.prototype=new Father(18);
Father.prototype.__proto__=Grand.prototype
Son.prototype.__proto__=Father.prototype;
let s1=new Son("Z3");
console.log(s1.name);//z3
如何输出Z3
function Son(name){
如何拿到名字z3,当new一个函数后,函数为构造函数
然后会创建一个this为空对象
 this={}
然后this的"__proto__"指向"名字是Son类.prototype";
this.name=name;
向里添加name:"z3";
最后返回this这个对象;
}
console.log(s1.age);//18
如何输出18
function Son(name){
如何拿到名字z3,当new一个函数后,函数为构造函数
然后会创建一个this为空对象
 this={}
然后this的"__proto__"指向"名字是Son类.prototype";
this.name=name;
调用这个普通函数;普通函数指向是谁调用我,this就指向谁
this.xiaoguo=Father
this.xiaoguo();
function Father(age){
  this.age=18;
}
向里添加name:"z3";
跟age:"18";
性能优化删除这个函数
delete this.xiaoguo;
最后返回this这个对象;
}
console.log(s1.money);//100
同样的道理在Father函数里书写
s1.hello();//11
s1.say();//22
Father.prototype.__proto__=Grand.prototype
Son.prototype.__proto__=Father.prototype;
s1没有hello()这个方法,就向原型找,原型没有继续向上找,都没有;
所有要修改指向:Son.prototype.__proto__修改指向到Father.prototype;
S1就得到了hello()的方法;
s1没有say()这个方法,就向原型找,原型没有继续向上找,都没有;
所有要修改指向:Father.prototype.__proto__修改指向到Grand.prototype
S1就得到了hello()的方法;
​

【补充内容:拓展】

let obj={
  name:"z3",
}
obj.age=123;
Object.defineProperty(obj,"gender",{
  value:"male",
  writable:true,    
  enumerable:true
});
console.log("a",obj);
obj.gender="female";
console.log("b",obj);
for(let item of Object.keys(obj)){
  console.log(item);
}

属性被赋值或更改时,触发set的方法

属性被使用时,触发get方法

let age=0;
let obj={
  name:"z3",
}
Object.defineProperty(obj,"cc",{
  set(value){
•    if(value<40){
•      age=18;
•    }else{
•      age=19;
•    }
•    ;
  },
  get(){
•    return age;
  }
});
console.log(obj.cc);
obj.cc=30;
console.log(obj.cc);
obj.cc=50;
console.log(obj.cc);

【深拷贝,浅拷贝】

浅拷贝:共用一个地址,因此其中一个更改,另一个也会发生变化;

let arr=[1,2,3];
​
let arr2=arr;
​
arr[2]="a";
​
console.log(arr2);//[1,2,"a"]
​
console.log(arr);//[1,2,"a"]

深拷:贝共用一个地址,因此其中一个更改,另一个不会发生变化;

 let arr=[1,2,3];
​
 let arr2=arr.slice(0);
​
 arr[2]="a";
​
 console.log(arr2);//[1,2,3]
​
 console.log(arr);//[1,2,"a"]

例2:

深拷贝:只有不共用一个地址就是深拷贝

let obj1={
​
  name:1,
​
  age:2
​
}
​
{...obj1}

... 的功能

功能1:批量修改跟赋值();

功能2:不定参数

let obj2={...obj1};
​
obj1.name="A";
​
console.log(obj2.name);
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值