一、js面向对象
1.什么是面向对象
面向对象编程(OOP)是一种计算机编程架构,他将真实世界的各种复杂的关系,抽象为一个个对象,然后由对象之间的分工与合作,完成对真实世界的模拟
2.面向对象的目的
- 重用性:针对相同的功能可以重复的使用程序
- 灵活性:针对差异性的功能做出适配与调整
- 扩展性:针对功能的变化做出添加或删除的改进
3.面向对象的特性
- 封装性:封装是一种信息隐蔽技术,使得用户只能见到用户的外特性,而对象的内特性对用户是隐蔽的,封装的目的在于把对象的设计者和对象的使用者分开,使用者不必知晓实现的细节,只需用设计者提供的消息来访问该对象
- 继承性:复用一些原有的功能,同时可修改和扩充
- 多态性:对象根据所接收的消息而做出动作,同一消息为不同的对象接收时可产生完全不同的行动
4.面向对象的组成
- 属性:描述一种状态
- 方法:描述一种行为
- 程序中变量就是属性,函数就是方法
5.创建面向对象程序
在面向对象编程中,是通过“类”来创建对象的,类相当于模具。根据传递的数据来创建对象,并且可以重复的创建对象
在ES6之前的JavaScript中,是没有类的概念的,不过可以利用构造哈数来代替类进行创建对象
构造函数跟普通的函数并没有太大区别,知识调用的时候需要通过new关键字来调用,构造哈数中的this会指向创建出来的对象,并且具有隐式返回
示例代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script type="text/javascript">
function fun(name){
this.name=name;
this.showName=function(){
console.log(this.name);
}
}
var obj=new fun('hi');
var obj1=new fun('hello');
obj.showName();
obj1.showName();
</script>
</head>
<body>
</body>
</html>
二、object对象详解
1.对象的引用
- 类型比较方式:对象比较时,值跟引用地址都相同时才相等
var obj1={};
var obj2={};
console.log(obj1==obj2);
输出为:false
- 类型赋值方式:对象赋值时,值跟引用地址都进行赋值操作
var obj={
name:'hello'
}
var obj2=obj;
obj2.name='hi';
console.log(obj.name)
输出为:hi
赋值时把obj的地址也赋给了obj2
- 浅拷贝与深拷贝:实现对对象的复制操作
浅拷贝:
var obj={
name:'hello'
}
var obj2=copy(obj);
function copy(obj){//浅拷贝
var result={};
for(var attr in obj){
result[attr]=obj[attr];
}
return result;
}
obj2.name='hi';
console.log(obj.name) ;
通过这个就可以只实现对象值得赋值而不会改变原有对象,不过如果对象的属性也是一个对象,就需要深拷贝
利用递归进行深拷贝:
var obj={
name:{age:20}
}
var obj2=copy(obj);
function copy(obj){//深拷贝
var result={};
for(var attr in obj){
if(typeof obj[attr]=='object'){
result[attr]=copy(obj[attr]);//递归
}else{
result[attr]=obj[attr];
}
}
return result;
}
obj2.name.age=30;
console.log(obj.name.age) ;
2.原型与原型链
- 原型:
在构造函数下面有一个prototype属性叫做原型,构造函数+原型整体也是一个对象叫做原型对象,在原型对象下面可以添加属性和方法 - 共享:
原型对象下的属性和方法,可供多个对象进行共享访问,这样就可以节省内存消耗(因为这样用的是一个地址,而上面咱们写的,每一个对象的方法都是不同的地址)
function Foo(name){
this.name=name;
}
Foo.prototype.showName=function()
{
console.log(this.name)
}
var obj=new Foo('hello');
var obj2=new Foo('hi');
console.log(obj.showName==obj2.showName);
输出结果:true
证明用的是同一个地址
- 原型链:
为什么创建出来的对象可以访问到原型对象下面的属性和方法呢?这是通过哦原型链进行查找的。原型链即:连接对象与原型对象之间的纽带就是原型链
【注】:原型链最外层是Object.prototype,可以通过控制台查看
3.面相对象相关语法
- constructor:原型对象下唯一默认自带的属性,用于查看对象的构造函数
- instanceof:左边是实例对象,右边是构造函数,他会检查右边构造函数的原型对象,是否在左边对象的原型链上
- in与for…in:in运算返回一个布尔值,表示一个对象是否具有某个属性。for…in循环可获得对象所有可枚举属性
示例代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>相关语法</title>
<script type="text/javascript">
/* function Foo(){
}
//Foo.prototype.constructor=Foo;
Foo.prototype={
constructor:Foo
showName:function(){}
};
var obj=new Foo();
console.log(obj.constructor);//Foo(){}
*/
/* function Foo(){
}
var obj=new Foo();
console.log(obj instanceof Foo);//true
*/
function Foo(){
this.name='hello'
}
var obj=new Foo();
console.log('name' in obj);//true
console.log('age' in obj);//false
</script>
</head>
<body>
</body>
</html>
4.系统对象与包装对象
- 系统对象:JavaScript语言本身就是基于面向对象的程序,最大的对象为window对象。内置了很多系统对象,如:数组、时间、正则等。
可以自行打印一下window对象,可以看到很对内置的对象
仔细想想其实JS本身就是一个很大的面向对象的程序,包括我们平时用的数组之类的都是一个对象,有push,sort等方法
首先,假如我们进行了这样的操作
<script type="text/javascript">
var arr=[1,2,3];
Array.prototype.push=function(){
}
arr.push(4,5,6)
console.log(arr);
</script>
</head>
这样push操作就会失败,因为我们重写了数组对象的push方法,也验证了上面说的,数组就是一个对象
接下来咱们自己实现一个push方法:
<script type="text/javascript">
var arr=[1,2,3];
Array.prototype.push=function(){
for(var i=0;i<arguments.length;i++){
this[this.length]=arguments[i];
}
return this.length;
}
arr.push(4,5,6)
console.log(arr);
</script>
</head>
- 包装对象:基本类型如:字符串、数字、布尔值等都具备对应的包装对象。可以通过包装对象来获取提供的属性与方法
如:字符串其实就是new String创造出来的包装对象
当我们用字符串的一下方法比如 str.trim()时,他其实是先找到new String()对象,再找到trim方法,他写在原型链上共享给了str
5.继承与多态
(1)继承方式
拷贝继承:
<script type="text/javascript">
function Foo(){//父类
this.name='hello';
}
Foo.prototype.showName=function(){
console.log(this.name);
};
function Bar(){//子类
Foo.call(this);
this.age=20
}
extend(Bar,Foo);
Bar.prototype.showAge=function(){
console.log(this.age);
}
function extend(subs,sups){
for (var attr in sups.prototype) {
subs.prototype[attr]=sups.prototype[attr];
}
}
var obj1=new Foo();
var obj2=new Bar();
console.log(obj1);
console.log(obj2);
</script>
类式继承:
<script type="text/javascript">
function Foo(){//父类
this.name='hello';
}
Foo.prototype.showName=function(){
console.log(this.name);
};
function Bar(){//子类
Foo.call(this);//继承属性还是用call
this.age=20
}
extend(Bar,Foo);
Bar.prototype.showAge=function(){
console.log(this.age);
}
/*function extend(subs,sups){
for (var attr in sups.prototype) {
subs.prototype[attr]=sups.prototype[attr];
}
}*/
function extend(subs,sups){
//不直接用原型链继承而是通过新建一个函数实现只继承方法
//否则创造多个对象之后可能就会影响到这个属性
var F=function(){};
F.prototype=sups.prototype;
subs.prototype=new F();
subs.prototype.constructor=subs;//修改了原型之后要把,constructor修正一下
}
var obj1=new Foo();
var obj2=new Bar();
console.log(obj1);
console.log(obj2);
</script>
(2)多态
重写继承的方法
就是简单的重写一下方法,例如上面的例子,咱们把Bar的showName方法重写一下
Bar.prototype.showName=function(){
console.log('aaa'+this.name);
}
接下来调用Bar.showName()就会输出aaahello