JS中的面向对象

1.面向对象:对代码的一种抽象,对外统一提供调用接口的编程思想
基于原型的面向对象方式中,对象是依靠构造器利用原型(prototype)构造出来的

一切事物皆对象
属性:事物的特性
方法:事物的功能
对象:事物的一个实例(众多人中的一个人)
原型:Js函数中由prototype属性引用了一个对象,即原型对象
所有函数都有一个prototype属性

//Object 是JS中所有函数的父对象
function F(){}
alert(F.prototype);//{Object object}
//F.prototype本身就是一个对象,它是Object的子对象
//构造函数对象:函数构造器 创建函数对象
var obj = new Function(var1,var2,...,functionBody();//函数的参数可以变量或数组的方式存在,最后一个参数应为自定义函数体,前面定义的参数在函数体中都可以引用

构造器构造的对象,效率低,传递给构造器的字符串都会被当作生成函数的变量参数,函数体内(functionBody)的参数顺序与传递参数的顺序一致,不能改变!

var obj = new Function("a","b","return a+b");
var s = obj(2,5);
alert(s);
alert(typeof obj);//返回function,typeof返回变量的数据类型

对象可区分为函数对象和普通对象,通过new Function()创建的是函数对象,其它为普通对象

2.闭包
闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数)

全局变量在函数内部可以访问
局部变量不能在全局中访问
在函数内部用var声明的变量是局部变量,省略var,就是一个全局变量

要在全局中访问局部变量,可以在函数内部再定义一个与局部变量同级的函数,并返回该函数(闭包),通过它访问

function a(){
	var i = 0;
	function b(){//b与i同级,可以访问i
		alert(++i);
	}
	return b;//返回b的函数体
}
var c = a();//注意是将a运行的结果(b)赋值给c
c();//1,此处相当于运行b()

闭包特点:函数b是在a内嵌套的,函数a需要返回函数b
闭包用途:
1.读取函数内部的变量
2.让i变量的值保留在内存中

function f1(){
	var n = 999;
	nAdd = function(){//没有var,是全局变量
		n = n+1;
	}
	function f2(){
		alert(n);
	}
	return f2;
}
var rs = f1();//rs是f2的闭包函数
rs();//999
nAdd();//全局变量可以执行
rs();//1000,以上几步之间n一直存在

f1是f2的父函数,在f1中n相对于f2来说是一个全局变量,f2依赖于f1,所以f1/f2都一直在内存中,n也就一直在内存中
一旦声明了闭包函数,它的父环境和父环境内部的变量就一直在内存中,直到闭包函数(rs)被收回

闭包的原理是,把局部函数赋值给全局变量,由于全局变量在代码执行的过程中是不会销毁的,所以它用到的值即局部函数不会销毁。而局部函数中使用到的变量及它存在的作用域也就保存在内存中了。

闭包的优点:有利于封装,可以访问局部变量
闭包的缺点:内存占用浪费严重,容易产生内存泄漏
闭包要谨慎使用!

3.声明对象
1)字面量

var person = {
	name:"aa",
	sex:"11",
	eat:function(fds){
		alert("我在吃"+fds);
	}
}

2)构造函数

var person = new Object();
person.name = 'zhangsan';
person.age = 100;
person.infos = function(str){
	return this.name;
}

3)Js中构造方法

function test([参数列表]){
	this.属性 = 属性值;
	this.方法 = function(){
		代码
	}
}
var obj = new test(参数列表);
function person(name,sex,age){
	this.name = name;//this.name是属性,习惯性将其名称定义为与参数名称一致
	this.sex = sex;
	this.age = age;
	this.show = function(){
		alert(this.name+"---"+this.sex+"---"+this.age);
	}//this代表当前对象,obj1和obj2之间是独立的,函数内部只能用this房屋属性和方法
}
var obj1 = new person("zhangsan","nan",18);//实例化
alert(obj1.name);
obj1.show();
var obj2 = new person("lisi","nv",20);
obj2.show();

4)工厂模式:按照某种模式,可以不断创造对象

funciton createObject(name,age){
	var obj = new Object();
	obj.name = name;
	obj.age = age;
	obj.run = function(){
		return this.name + this.age +'运行中...';
	};
	return obj;
}
var box1 = createObject("zhangsan",100);
var box1 = createObject("lisi",200);

工厂模式相当于将通过new Object()定义对象的过程封装在函数中,在内部创建对象并返回对象,不需要在外部写new
注意this永远指向当前调用值

funciton createObject(name,age){
	var obj = new Object();
	obj.name = name;
	obj.age = age;
	obj.run = function(){
		return this.name + this.age +'运行中...';
	};
	obj.say = function(){
		return "hello";
	}
	return obj;
}
var box1 = createObject("zhangsan",18);
var box2 = createObject("lisi",20);
alert(box1.name);
alert(box2.name);
alert(box1.run());
alert(box2.say());

无论以何种方式创建的对象,彼此之间都是无关联的

工厂与构造方式的区别:
1.构造方式不会显示创建对象(new Object),将属性赋值给this,不需要return对象;
2.工厂方式在方法内部创建object对象,并返回它,属性和方法都赋值给object对象

5)原型模式

function test(){
}//空方法!
test.prototype.属性 = 属性值;
test.prototype.属性 = 属性值;
test.prototype.方法名称 = function(){
	代码
};
var obj = new test();

任何js方法或函数,都自带一个prototype属性,且它以对象方式存在。
原型模式根本:
函数本身声明为空内容,利用prototype定义属性和方法。

function test(){
}
test.prototype.color = "red";
test.prototype.heights = "1.7";
test.prototype.width = "1.2";
test.prototype.showInfo = funciotn(){
	alert('aaa');
};//不能缺少prototype
var car1 = new test();
car1.showInfo();

也可以采用字面量的写法

function test(){
}
test.prototype = {
	color:"red",
	heights:"1.7",
	width:"1.2",
	showInfo:funciotn(){
		alert('aaa');
	}
}
var car2 = new test();
car2.showInfo();

6)混合模式:构造模式+原型模式

function test(v1,v2,v3){
	this.v1 = v1;
	this.v2 = v2;
	this.v3 = v3;
}
test.prototype.方法名称 = function(){
	代码
}
var obj = new test(v1,v2,v3);
function blog(name,url,friend){
	this.name = name;
	this.url = url;
	this.friend = friend;
}
blog.prototype = {
	test:"awt",
	showInfo:function(){
		alert(this.friend);//注意this
	}
}
var peo = new blog('zhangsan','http://www.imooc.com','李四');
alert(blog.test);

4.遍历对象
1)遍历对象的属性
对象有的时候是可以当做数组处理的

var ren = {};
ren.name = "zhangsan";
ren.age = 18;
ren.demo = function(){
	document.write("aaa");
};
for(var i in ren){//for..in..语句
	alert(i);//弹出属性和方法名称
	alert(ren[i]);//弹出属性和方法内容
}

变量i往往表示数组的下标,在遍历对象时,i的值取的是对象所有的属性和方法名称。
for in 的遍历过程就相当于循环取值,能取到多少个值,就执行多少次函数体。
使用构造函数及其它方法声明的对象要实例化后才可以进行遍历。

2)对象的存储
内存分区
变量中存储的只是引用地址,一般为16进制,地址指向堆内存中对象属性和方法名存储的位置,方法内容存储在代码段中。

5.封装
把对象内部数据和操作细节进行隐藏,只对外提供一个对象的专门访问的接口(接口一般为调用方法)。
Js中可以通过闭包实现封装(函数内部声明的变量,外部访问不到),能否在对象外部被访问,区分了公有和私有内容。

function demo(){
	var n = 1;
	function test(){//特权方法
		return ++n;
	}
	/*
	this.test = function(){
		renturn ++n;
	}
	*/
	return test;
}
var at = demo();
alert(at());
function A(){
	var t = 3;
	function _xx(){alert(11);}
	this.xx = function(){
		return _xx;
	}
}
A.prototype = {
	oth:function(){
		alert("普通方法");
	}
}
var a = new A();
var b = a.xx();
b();

封装占用内存且不利于继承,尽量少写。

6.原型继承
原型是利用prototype对象添加属性和方法
原型链:JS在创建对象时,都有一个叫做__proto__(两边双下划线)的内置属性,用于指向创建它的函数对象的原型对象prototype

var person = function(){};
var p = new person();//三个阶段
  1. var p = {};先创建对象
  2. p.proto = person.prototype
  3. 初始化对象P:person.call§
    __proto__是实例上的属性,而prototype是构造函数上的属性

p.proto(person.prototype)是对象,拥有自己的__proto__属性,指向创建它的函数对象的原型对象prototype–>p.proto.proto == person.prototype.proto–>此链条可以无限回溯(原型链)

var person = function(){};
person.prototype.say = function(){
	alert("天气不错");
};
var p = new person();
p.say();//p本身只是一个变量,没有say方法

p调用say方法,实际是调用__proto__(person.prototype)的say方法!

var person = function(){
	this.age = "18";
};//父方法
person.prototype.say = function(){
	alert("天气不错");
};
person.prototype.gongzi = 500;

var programmer = function(){};//子方法
programmer.prototype = new person();//将prototype对象替换为person的一个实例
programmer.prototype.gongzi = 1000;
programmer.prototype.wcd=function(){
	alert("明天天气也不错");
};
var p = new programmer();//原型继承
//p.__proto__ = programmer.prototype
//programmer.prototype.__proto__ = person.prototype
p.say();
p.wcd();
alert(p.gongzi);
alert(p.age);//18

对p调用任何属性或方法,先到__proto__ (programmer.prototype)中查找,若有则成功调用,若没有则到person.prototype(programmer.prototype__proto__)中查找
在原型对象prototype中定义的属性和方法,与在函数体内定义的属性和方法位于同一层级(或者说最终都归入prototype中),所以才可以通过__proto__(person.prototype)调用
如果定义有同名属性,使用哪个属性与定义和继承的先后顺序有关(区别于函数作用域中变量的查找)

programmer.prototype = new person();
programmer.prototype.gongzi = 1000;//先继承后定义,则定义覆盖继承

programmer.prototype.gongzi = 1000;
programmer.prototype = new person();//先定义后继承,则继承覆盖定义(与函数作用域不同!)

原型继承是用父方法实例化,若父方法带参数,实例化时也要传入参数!
programmer.prototype = new person(name,age);

原型的值可以是一个对象也可以是null,当回溯到Object时,Object.prototype等于null(终点)

7.构造函数继承
1)在子类内部构造父类的对象来继承

function parents(name){
            this.name = name;
            this.say = function(){
                alert("父亲的名字:"+this.name);
            }
        }
function child(name,age){//继承parents
            this.pObj = parents;//用父对象来创建子对象,必须步骤一!
            this.pObj(name);//实例化,必须步骤二!
            this.age = age;
            this.sayC = function(){
                alert("child:"+this.name+"---"+"age:"+this.age);
            }
        }
var c = new child("李四",20);
c.sayC();
c.say();//李四

父对象被子对象继承,它所有的属性和方法都将传递到子对象中
注意:如果child中也有this.name = name(同名属性),最终返回的结果与定义的顺序有关;属性在前继承在后会被继承覆盖,继承在前属性在后会被属性覆盖(调用父对象中的方法时返回的也是属性的值)

2)call和apply继承
call调用一个对象的一个方法,以另一个对象替换当前对象
apply应用某一对象的一个方法,用另一个对象替换当前对象
它们的差别只在于传入的参数是列表还是数组

function person(name,age,len){
	this.name = name;
	this.age = age;
	this.len = len;
	this.say = function(){
		alert(this.name+":"+this.age+":"+this.len);
	};
}
function student(name,age){
	person.call(this,name,age);//此处通过this将stu传入person,person中的this.name被替换为stu.name,其余属性及方法的this都被替换为stu
}
function teacher(name,age,len){
	person.apply(this,[name,age,len]);//必须要有this,才能够继承
}
var stu = new student("lisi",10);
stu.say();
var tea = new teacher("wangwu",20,"178");
tea.say();

与前面间接调用的逻辑一致,第一个参数用于修改this的指向,本身应该指向person的实例,但修改之后指向了student/teacher的实例(也可理解为参数传入)

8.JS面向对象的关键词
1)instanceof 变量是否是对象的实例

var arr = new Array();
console.log(arr instanceof Array);
console.log(arr instanceof Object);//所有对象都是Object的实例

2)delete 用于删除对象属性和方法

function Fun(){
	this.name = "zhangsan";
	this.say = function(){
		alert(this.name);
	}
}
var obj = new Fun();
delete obj.name;//属性名
delete obj.say;//方法名,不带括号
console.log(obj.name);//undefined
obj.say();//报错

delete只能删除实例的属性和方法,对构造函数无影响

delete不能删除变量与定义在原型链中的属性

var per = "hello";
delete per;
console.log(per);//依然输出
function Fun(){	
}
Fun.prototype.greeting = function(){
	alert("hello");
}
var obj = new Fun();
delete obj.greeting;
obj.greeting();//依然弹出hello

3)call apply arguments callee

function add(a,b){
	alert(a+b);
}
function subs(a,b){
	alert(a-b);
}
add.call(subs,a,b);//8
add.apply(subs,[a,b]);//8

第一个参数只能引用存在的(已定义的)对象,运行的函数体依然是add,只是将指针指向了subs(可以理解为用add替换了subs)

function animal(){
	this.name = "ani";
	this.showName = function(){
		alert(this.name);
	}
}
function cat(){
	this.name = "cat";
}
var an = new animal();
var c = new cat();
an.showName.call(c);//通过call方法,将showName方法传递给cat使用了

4)callee 返回正在执行的function对象(function内容)
arguments.callee 默认值就是正在执行的function对象(callee就是指代函数本身)

arguments存在于所有函数中

function demo(){
	alert(arguments.callee);//callee当作属性,返回函数内容
	alert(arguments.callee());//报错,陷入死循环
}

callee当作方法时调用函数本身,可用于递归

var sum = function(n){
	if(n<=1){//有终止条件就不会陷入死循环
		return 1;
	}else{
		return n+arguments.callee(n-1);
	}
}
alert(sum(6));

5)arguments 每个函数都有一个Arguments对象的实例arguments,引用函数的参数(实参),可以用数组下标方式引用arguments元素,有length及callee属性
6) this
this 可以在函数内部定义属性和全局变量

function test(){
	this.x = 1;//相当于 X = 1
	alert(this.x);
}

this在构造函数内作为方法调用,指代当前对象

function test(){
	this.name = "zhangsan";
}

this在call / apply中代表第一个参数的指向

var x = 0;
function test(){
	alert(this.x);//全局变量
}
var o = {};
o.x = 1;
o.m = test;
o.m.apply();//0
o.m.apply(o);//1

9.对象冒充:适用于构造继承
将父类的特权属性和特权方法一起传给子类,但不能继承共有方法和属性

function person(name,age){
	this.name = name;
	this.age = age;
	this.sayHi = function(){
		alert("hi");
	}
}
person.prototype.walk = function(){
	alert("walk.....");
}
function student(name,age,grade){
	this.newMethod = person;//冒充person对象,传递特权属性和特权方法给子类
	this.newMethod(name,age);
	this.grade = grade;
}
var s1 = new student("zhangsan",15,5);//s1是student对象,继承person,拥有person的所有属性和方法
alert(s1.name);//特权属性
alert(s1.age);//特权属性
s1.sayHi();//特权方法
s1.walk();//共有方法,无法调用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值