高级JavaScript #面向对象 封装 继承 多态 #自定义继承 #this

面向对象

1.什么是:对象解构描述每个事物的属性和方法,按照调用对象中保存的属性和方法;
2.为什么:便于大量数据的管理和使用
3.何时  :几乎所有程序都用面向对象思想开发出来

面向对象三大特点:封装 继承 多态

封装

创建对象

  (1){}创建一个对象:
  var obj={
	属性名:属性值,
	方法名:function(){}	
  }
  //例子
  var lucy={
	sname:"lucy",
	sage:17,
	intr:function(){
		alert(`I'm ${this.sname},${this.sage}`)
	}
  }

 (2)new Object()创建一个对象
 var 对象名=new Object();
 对象名.属性名=属性值;
 对象名.方法名=function(){}
 
 //例子
   var lilei=new Object();
   lilei.sname="Li Lei";
   lilei.sage=11;
   lilei.intr=function(){
     console.log(`I'm${this.sname,this.sage}`)
   }
   console.log(lilei)
   缺点: 麻烦,所以很少用
   揭示: 一切对象底层都是关联数组
   	1).存储结构相同: 都是名值对儿的集合
   	2).强行访问对象中/数组中不存在的一个成员/位置,返回undefined.
   	3).强行给对象/数组中一个不存在的属性/位置赋值,自动添加该属性/元素。
   	4). 其实无论是对象,还是关联数组,都可用["属性名"]方式访问成员的值。而且都可以简写为".属性名"
   	5). 都可用for in循环遍历: for(var 变量 in 对象或关联数组){}
  (3)构造函数
    问题:一个{}只能创建一个对象 如果要反复创建多个相同结构的对象时 代码就会很冗余 重复很多 极其不便于维护
    解决: 使用构造函数
    构造函数:专门描述同一类型的所有对象的统一结构的函数
    何时: 今后只要需要反复创建多个相同结构,但是属性值不同的同一类型的多个对象时,都要用构造函数
    定义构造函数:
    	function 类型名(形参1, 形参2, ...){//Student   Product   Food
		//必须都具有以下属性和方法
		this.属性名1=形参1;
		this.属性名2=形参2;
		this.方法名=function(){
			... this.属性名 ...
		}
	}
	示例:
   function student(sname,sage){  
      this.sname=sname;
      this.sage=sage;
      this.inre=function(){
        console.log(`我是${this.sname},年龄${this.sage}`)
      }
    }
    var lilei=new student('LiLei',23);   //用new调动构造函数
    var hmm=new student('HanMei',21);
    
    构造函数原理:new做了4件事
    (1). 创建一个新的空对象: 
	(2). 自动设置新创建的子对象继承构造函数的原型对象
	(3). 调用构造函数,new自动将构造函数中的this改为指向当前正在创建的空对象。
	a. 每一句this.属性名=属性值,都变成了新对象.属性名=属性值。
	b. 因为新对象中一穷二白,什么属性都没有,所以每一句"新对象.属性名=属性值",都变成了给新对象中强行赋值!
	c. 如果构造函数中包含“this.方法名=function(){ ... }”,则每次调用构造函数时,都会反复创建一个新函数,给新对象使用!
	(4). 返回新创建的对象地址保存到等号左边的变量里

问题:
1.在对象内的方法,也不能直接使用对象自己的属性,如果直接使用,会报错未被定义
原因:
1.对象的方法虽然保存在对象中,但因为不是一级作用域,方法的作用域链不包含对象本身,方法中所有不点的变量
2.方法中所有不点.的变量名,都只能在函数内和全局两个范围内查找。无权擅自进入任何对象中查号属性
解决:
1.写成"对象名.属性名" (此方法如果外部对象改名了,里面也得跟着变,不能写死)
2.写成"this.属性名"
原理:
1.this专门引用正在调动当前的.前的对象的关键字
2.只要想在函数中,获得正在调用函数的.前的对象中的成员时都用this来指代

函数 vs 方法 
相同点:无论函数还是方法,本质都是function;
不同点:不属于任何对象的,在对象外创建的函数称为函数,对象内创建的function称为方法

克隆对象

示例:
  <script>
    var lilei={
      sname:"Li Lei",
      sage:11
    }
    function clone(oldobj){ //定义克隆函数,当调用时传入需要克隆的对象
      var newObj={};        //创建一个新的空对象
      for(var key in oldobj){ //遍历需要克隆对象的属性
        newObj[key]=oldobj[key] //没遍历一个属性,就给新对象添加同名属性
      }
      return newObj;  //返回组装好的新对象
    }
    var lilei2=clone(lilei)
    console.log(lilei2==lilei)  //false 从此lilei和lilei2不再执行同一个对象ok
  </script>

继承

问题:如果构造函数中包含方法定义,没创建一次新对象,就会创建方法的副本,浪费内存.
解决:今后构造函数中不应该包含方法定义.
新问题:方法定义放在哪才能只定义一次就能让所有对象共同使用
解决:继承
什么是继承:父对象中的成员,子对象无需重复创建,可以使用
为什么要用:既重用方法,又能节约内存
何时用:只要希望方法只定义一次,就能让所有新对象共同使用,通过继承实现
如何使用:
(1)JS的继承都是用过继承原型对象来实现的
(2)什么是原型对象:专门保存一个类型的所有子对象共有的成员的父对象
(3)何时:只要为一个类型下的所有子对象定义共有的成员,都要放在原型对象中
(4)如何访问原型对象:构造函数.prototype
(5)何时子对象继承父对象:自动完成继承,new 自动设置新的子对象继承构造函数的原型对象
(6)结果:只要子对象的__proto__属性指向了父对象,子对象可直接调用父对象的成员

示例:
   function student(sname,sage){
      this.sname=sname;
      this.sage=sage;
    }
    console.log(student.prototype) //不用自己创建原型对象,就自动附赠了一个Student类型的空的原型对象
    student.prototype.intr=function(){ //强行向构造函数的原型对象中添加一个共有方法,让所有子对象共用
      console.log(`我是${this.sname},${this.sage}`)
    }
    var lilei=new student('lilei',23)
    var hanm=new student("hanmm",21)
        //验证
    console.log(lilei.__proto__==hmm.__proto__);//true
    //lilei的proto是Student的原型对象吗?
    console.log(lilei.__proto__==Student.prototype);//true

自有属性和共有属性

自有属性: 直接保存在当前对象内部,归当前内部独有的属性
共有属性: 保存在原型对象中,归所有子对象共有的属性

获取属性值:子对象.属性名
修改自有属性: 子对象.自有属性名=新属性值
修改共有属性:  
错误做法: 虽然获取共有属性值时,可以用"子对象.共有属性名",但是,修改时,不能使用"子对象.共有属性=新值"
正确做法:构造函数.prototype.共有属性名=新值  结果: 所有子对象跟着一起变!
  <script>
    //定义构造函数妈妈
    function Student(sname,sage){
      this.sname=sname;
      this.sage=sage;
    }
    //向Student类型的原型对象中添加一个共有属性——班级名
    Student.prototype.className="初一2班"

    //2. 用new来调用构造函数
    var lilei=new Student("Li Lei",11);
    var hmm=new Student("Han Meimei",12);
    //尝试获取每个对象自己内部的自有属性值
    console.log(lilei.sname, hmm.sname);
    //尝试获取原型对象中的共有属性值
    console.log(lilei.className, hmm.className);

    //修改lilei的自有属性年龄
    lilei.sage++;
    console.log(lilei.sage, hmm.sage);

    //错误: 使用lilei,拉着全班同学一起留级
    lilei.className="六年级2班";
    //结果lilei不但修改不了共有属性className,而且会自动在lilei对象中添加一个同名的自有属性className,保存和全班其它同学不同的班级名。
    //从此,李磊在className这一个属性的使用上,跟全班同学,分道扬镳!
    console.log(lilei.className, hmm.className);
    //正确: 过了一年全班同学一起升级到初二2班
    Student.prototype.className="初二2班";
    console.log(lilei.className, hmm.className);
    console.log(lilei);
    console.log(hmm);
  </script>

内置类型的原型对象

ECMAScript标准中规定的,浏览器已经实现的,我们可以直接使用的类型或对象。

string number boolean array date regexp math error function object global

除了math 和 global 其他9种类型 都由构造函数和原型对象组成
示例:Array类型:
	function Array(){ ... } 构造函数
	Array.prototype 原型对象
假如我们经常需要对数组的求和,可以自己创建Array的方法
    Array.prototype.sum=function(){
      var result=0;
      for(var i=0;i<this.length;i++){
        result+=this[i]
      }
      return result
    }
    var arr=[1,2,3];
    console.log(arr.sum())

原型链

(1).问题: 构造函数和原型对象,有没有父对象呢?
(2). 其实js内存中也是大家庭!
	a. 其实每个原型对象也有_ _proto_ _指向自己的父级对象
	b. 因为原型对象是自动创建的,所以原型对象的_ _proto_ _都统一指向顶级父类型——Object类型
	c. Object的原型对象=null,所以Object类型是顶级父类型
	(3). 什么是原型链: 由多级父对象逐级继承形成的链式结构
	(4). 保存着一个对象可用的所有属性和方法。
	a. 只要在原型链上的属性和方法,这个对象都可直接使用!
	b. 如果没在原型链上的属性和方法,这个对象就无法使用
	(5). 控制着属性和方法的使用顺序: 就近原则
	a. 当对象访问一个属性或方法时,现在当前对象内部查找自有属性和方法。优先使用自有属性和方法。
	b. 除非对象自己内部没有想要的自有属性和方法,才被迫延原型链向父元素查找
	
总结一句话:保存着一个对象可用的所有属性和方法。控制着属性和方法的使用顺序:先自有再共有

在这里插入图片描述
多态

重写: 只要觉得从父对象继承来的成员不要用,都在子对象中重写同名成员
如果觉得这个父对象对象都不好用,可以自定义继承: 2: 
 1). 只换一个子对象的父对象: 2: 
   i. 子对象.__proto__=新父对象
  ii. Object.setPrototypeOf(子对象, 新父对象)
 2). 更换多个子对象的原型对象: 构造函数.prototype=新对象
//例子
 <script>
    function Student(sname,sage){
      this.sname=sname;
      this.sage=sage;
    }
    //为了让所有学生都有好用的toString()来输出学生信息,就需要在学生类型的原型对象中添加toString()方法
    Student.prototype.toString=function(){
      //拼一个我自己喜欢的字符串格式——可自定义
      return `{ 
        sname:"${this.sname}", 
        sage:${this.sage} 
      }`
    }
    var lilei=new Student("Li Lei",11);
    var arr=[1,2,3];
    var now=new Date();
    console.log(lilei.toString());
    console.log(arr.toString());//可以输出数组中的元素值
    console.log(now.toString());//可以输出年月日时分秒
  </script>

自定义继承

如果原型对象中只有个别东西不好用,可以重写.但是,如果整个原型对象毫无可取之处,我们就用到自定义继承
如果整个原型对象都不是想要的,想换成新的原型对象时
如何:
1). 只修改一个对象的父对象: 2种方法: 
  a. 子对象._ _proto_ _=新父对象
  b. 问题: _ _proto_ _不建议直接使用
  c. 解决: Object.setPrototypeOf(子对象, 新父对象)
  
示例:
    function student(sname,sage){
      this.sname=sname;
      this.sage=sage;
    }  
    var father={
      money:10000000,
      car:'benz'
    }

    var lilei=new student('lilei',11);
    var hanm=new student('hanmm',12);
    console.log(lilei,hanm)  //prototype 是空的

    Object.setPrototypeOf(hanm,father)  /*******************************/
    console.log(hanm.money,hanm.car)  //10000000  benz
    console.log(lilei.money,lilei.car)  //undefined undefined

this

(1)全局作用域下 this指向window对象
(2)严格模式下的全局函数  this指向undefined
(3)构造函数    this指向new
(4)定时器      this指向window
(5)元素绑定事件触发后 this指向当前元素
(6)对象中调用的方法   谁调用this就指向谁
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值