面向对象(OOP)

什么是面相对象?

程序中,都是用对象来集中描述现实中一个具体事物的属性和功能。

为什么要使用面向对象?

为了便于大量数据和功能的维护和使用

何时使用面向对象?

只要使用面相对象编程,都要先创建对象

如何使用?

事物的属性会成为对象的属性

事物的功能会变成对象的方法

成员=属性+方法

创建对象的3中方法:
1、对象直接量方式:
var obj = {

属性名:值,

属性名:值,

方法名:function(){

....this.属性名...

},

方法名:function(){

...this.属性名 ...

}

}
访问对象的成员:
    访问对象的属性:对象名.属性名

    本质:属性其实是保存在对象中的变量

    访问对象的方法:对象名.方法名()

    本质:方法其实是要保存在对象内的函数

    问题:对象在自己的方法中使用自己的属性

    错误1:在方法中直接使用属性名

    原因:默认,没有加任何前缀的普通变量,只能在作用域链中查找,无权进入任何对象查找

    错误2:在方法中使用对象名.属性名

    原因:对象名只是一个普通变量,随时可能发生改变

    正确做法:只要方法想访问自己的属性,必须用this.属性名

    this->自动获得正在调用方法的"."前的对象名

2、用new创建
var obj = new Object();
obj.属性名=值;
obj.方法名=function(){
...this.属性名...
}

本质:js中一切对象底层都是关联数组:

与关联数组的区别:

相同:

        1.可用["下标"],也可用.下标方式访问成员

                如何选择:

                    1、如果属性名是固定的,首选

                    2、如果属性名是动态获得的,应用[]

         2.随时可在任意位置添加新成员

         3.访问不存在的成员不会报错,返回undefined

问题:一次只能创建一个对象,如果反复创建多个对象,代码会很冗余

解决:用构造函数反复创建多个就够功能相同的对象

用构造函数创建

定义构造函数:
function 类型名(属性参数,..){
  this.属性名=属性参数;
  ...=...;
  this.方法名=function(){
   ...this.属性名 ...
  }
}
用构造函数创建对象:
var obj = new 类型名(属性值,...)

其中,new共做了4件事:

    1、创建一个空对象

    2、让新的子对象继承构造函数的原型对象

    3、调用构造函数

        1.this=>new

        2.this.属性名=值:通过强行赋值的方式,为对象添加新的属性,并保存属性值

    4、返回新对象的地址,保存在变量中


问题:每个对象都有一个重复的相同的方法定义,浪费内存

解决方案:继承

继承

什么是继承?

父对象的成员,子对象无需重复创建,即可直接使用

为什么要使用继承?

代码重用,节约内存

什么时候使用?

只要多个子对象,拥有相同的属性值和方法时

如何使用?

通过原型对象

什么是原型对象?

集中保存子对象共有成员的父对象

如何使用原型对象?

    创建:不需要创建

    定义构造函数时自动附赠一个原型对象

    创建子对象时:new的第2步,让子对象自动继承构造函数的原型对象

添加共有成员:

    构造函数中不再包含共有方法的定义

    所有共有方法都应该添加到构造函数的原型对象中

  构造函数.prototype.方法名=function(){

    ...this.属性名 ...

}

例如:

<script>
		//描述所有学生的统一结构
		function Student(sname,sage){
			this.sname=sname;
			this.sage=sage;
		}
		Student.prototype.intr=function(){            //使用原型对象定义(利用继承)
			console.log(
				"I'm "+this.sname+", I'm "+this.sage);
		}
		Student.prototype.className="初一2班";
		//创建lilei
		var lilei=new Student("Li Lei",11);
		var hmm=new Student("Han Meimei",12);    
		console.log(lilei);
		console.log(hmm);
		lilei.intr();     //调用对象中的方法
		hmm.intr();        //调用对象中的方法
	</script>

这个例子中,由于每个对象都有一个相同的方法,故利用继承性定义对象的方法,所以构造函数Student中的子对象都可以直接使用方法

自有属性和共有属性:

自有属性:保存在子对象本地,归子对象独有的属性

共有属性:保存在父对象中,所有子对象共享的属性

获取属性指:子对象.属性名

修改属性值的方法:自有属性只能用子对象修改

                                共有属性只能用原型对象修改

内置类型的原型对象:
笔试题:ES中内置类型/对象共有多少个,分别是?

String Number Boolean  --包装类型

Array Date RegExp Math

Error

Function Object

Global(在浏览器中被window代替)


他们的本质:

所有能new的都是构造函数

所有的API都保存在构造函数的原型对象中:

Array.prototype、Date.prototype...

问题:旧浏览器无法使用新的API

如何解决:

    向该类型的原型对象中添加自定义的API

    开发时:在MDN中查找:MDS 类型名.prototype.函数名->找到Polyfill

笔试:解释包装类型

什么是包装类型?

包装类型是专门保存一个原始类型的值,并提供操作原始类型值得API

为什么要使用包装类型?

原始类型的一个值本身没有任何功能

什么时候使用?

只要试图对原始类型的值调用API时,都会自动实用该原始类型对应的包装类型对象

例:

"hello".toUpperCase();//由于hello是原始类型string 本身没有任何功能,所以他会自动调用
//new String.toUpperCase("hello");将字符串变为大写

如何使用包装类型?

创建:不需要自己创建,当试图对原始类型的值调用API时,自动创建对应的包装类型对象。

调用的API其实是包装类型对象提前定义好的API

问题:包装类型的API也有浏览器兼容问题

如何解决?

同内置类型的API解决方式相同

例如:

//如果想在数组中查找一个数据,使用indexOf是很好的方法,但是在IE8中不支持,如何解决兼容问题呢?

if(typeof Array.prototype.indexOf !== "funciton")
Array.prototype.indexOf=function(val,starti){    //val 想找的字符 starti 开始的位置
    starti = stari||0; //使用短路逻辑创建备用值,当用户没有传入starti时为0
    for(var i=0;i<this.length;i++){
    if(this[i]===val){   //判断传入的想要查找的值是否能找到
    return i;        //如果能,返回查找到的值
    }
    return -1;        ///如果不能,返回-1
    }
}
var arr = [1,2,3,4,3,2,1];
document.write(
arr.indexOf(3)+"<br>"+//2
arr.indexOf(3,3)+"<br>"//4
);

原型链

什么是原型链?

由多级父元素逐级继承,形成的链式结构

包括:所有对象的属性和方法

控制:属性和方法的使用顺序,以及共享范围


笔试题: 判断一个对象是不是数组类型,有几种方法:

  问题: typeof只能区分五种原始类型和function

     无法进一步细致区分引用类型对象的类型

例如:

var n=1,s="a",b=true,nu=null,un;    //这些都是原始类型
var obj1={},obj2=[],obj3=new Date(),obj4=function(){}; //非原始类型
console.log(
			typeof s, //string
			typeof n, //number
			typeof b, //boolean
			typeof nu, //object
			typeof un, //undefined
			typeof obj1, //object
			typeof obj2, //object
			typeof obj3, //function
			typeof obj4  //object
		);//可以看出typeof只能区分原始类型与function 无法细分引用类型对象

  1. 判断原型对象:

    varbool=Array.prototype.isPrototypeOf(obj)

    判断Array.prototype是不是obj的父级原型对象

例如:

		var obj1={},obj2=[],obj3=new Date();
		console.log(
			Array.prototype.isPrototypeOf(obj1),//false
			Array.prototype.isPrototypeOf(obj2),//true
			Array.prototype.isPrototypeOf(obj3),//false
		);

 

 2. 判断构造函数:

   //obj.constructor===Array

    varbool= obj  instanceof  Array

  问题: 不仅检查直接父级,且检查整个原型链

       检查不够严格

原因:

  <script>
		var obj1={},obj2=[1,2,3],obj3=new Date();
		var obj4={};
		    obj4.__proto__=obj2; //当给obj4的继承关系强行赋值为Array时
		//1.
		console.log(
			Array.prototype.isPrototypeOf(obj1),//false
			Array.prototype.isPrototypeOf(obj2),//true
			Array.prototype.isPrototypeOf(obj3),//false
			Array.prototype.isPrototypeOf(obj4) //true    //无法检测
		);
		//2. 
		console.log(
		//obj1.constructor===Array,
			obj1 instanceof Array,//false
			obj2 instanceof Array,//true
			obj3 instanceof Array,//false
			obj4 instanceof Array //true        //无法检测
		);
	</script>

  3. 判断每个对象内部的隐藏属性class:

  class属性在创建对象时保存对象的类型

   不随继承关系改变而改变

   如何: 唯一的办法: Object.prototype.toString

   问题: 多数父对象都重写了toString方法,将顶级的toString覆盖了,无法直接调用到

   解决: call: 让任何一个对象抢到任何一个想调用的函数执行

    要调用的函数.call(obj)

使用了.call让他们都使用Object的toString
console.log(
	Object.prototype.toString.call(obj1)==="[object Array]"    //false
	,    
	Object.prototype.toString.call(obj2)==="[object Array]"    //true
	,
	Object.prototype.toString.call(obj3)==="[object Array]"    //false
	,
	Object.prototype.toString.call(obj4)==="[object Array]"    //false 可以检测
);

在这里运用了多态

 什么是多态?

 同一个函数,在不同情况下表现出不同的状态

 包括哪2种方式?

  1. 重载:

  2. 重写(override):

   什么是重写?

        子对象中定义了和父对象中的API同名的成员

   为什么重写?

         子对象觉得父对象中的成员不好用

   何时需要重写?

         只要子对象觉得父对象中的成员不好用,就可以在子对象本地重写父对象成员

   如何重写?

         只要在子对象本地定义和父对象中同名的API

    方法:

var arr = [1,2,3];
toString.call[arr];

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值