JavaScript面向对象编程
文章目录
JavaScript所有属性和方法全部封装在对象中
-
说明:对比C++,JavaScript的对象相当于C++的类
JavaScript对象的实例化相当于C++的对象
1、JavaScript创建对象
/*
方法一:直接创建对象
*/
var myBook=new Object();
//一个一个添加属性和方法
myBook.name='计算机网络';
myBook.author='谢希仁';
myBook.info=function(){console.log('my book info!')}
//直接赋予方法,默认就是创建了一个Funciton函数对象
//由于在js中函数也是一个对象,因此也能够添加属性和方法
//每个函数对象都会有一个 prototype 的属性(箭头函数除外)
//一个一个添加属性和方法
var myiPad=function(){
var privateName='private name';//添加一个私有属性,只有公有方法才能访问
this.info='this info'; //添加一个公有属性,必须实例化对象才可访问
console.log('myiPad!')
}
myiPad.Show = function(){
console.log("Show Message");
};//添加一个静态方法,只能直接对象名调用,同时无法通过this访问其他属性
myiPad.prototype.Display = function(){
console.log("Property Message");
};//添加一个一个继承方法,必须实例化对象才能调用,但可以通过this访问其他公有属性,私有无法访问!!
myiPad.Show()
myiPad.Display()//error
var myipad=new myiPad();
myipad.Show()//error
myipad.Display()
/*
方法二:使用构造函数创建多个实例
*/
function Car(color,year,make){
this.color=color;
this.year=year;
this.make=make;//添加公有属性
var info="car info";//添加私有属性
this.Show=function(){console.log(info);}//添加公有方法
}
//创建实例及使用函数
var car=Car('red','2018','China');
car.Show();
console.log(car.make);
/*
方法三:使用JavaScript新特性:语法糖
*/
//使用js语法糖创建的是一个test对象,而不是一个对象函数,不能被实例化!!
var test = {
Name:'test is a Object',
Success:function(){
this.Say = function(){
console.log("Hello,world");//Success函数下的添加公有方法
};
console.log("create Obj Success!!");
}
};
//等价于如下写法:
/*
var test = new Object();
test.Name = 'test is a Object';
test.Success = function(){
this.Say = function(){
console.log("Hello,world");
};
console.log("create Obj Success");
*/
//直接使用
console.log(test.Name);
test.Success();
//不能被实例化
// var a=test()//error
//访问内部函数的方法
var s = new test.Success();//此处会再次执行Success函数,同时创建一个实例
s.Say();
2、JavaScript继承对象
使用prototype关键字
//假设Pet为一个定义好的宠物对象
//Dog为一个需要继承Pet的狗对象
//继承方式如下
Dog.prototype = new Pet();
3、从外部访问内部函数的属性及方法
使用this关键字
…
4、JavaScript的闭包函数
4.1闭包函数的定义:
JavaScript函数允许使用内部函数,即函数定义和函数表达式位于另一个函数的函数体内。
其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包。
// 方法一:直接创建函数对象
var Circle = function() {
var obj = new Object();
obj.PI = 3.14159;
obj.area = function( r ) {
return this.PI * r * r;
}
return obj;
}
var c = new Circle();
console.log( c.area( 1.0 ) );//在函数外调用area闭包函数
//方法二:使用语法糖
var Circle={
PI:3.14159,
area:function(r){
return this.PI * r * r;
}
};
console.log( Circle.area(1.0) );
4.2闭包函数的特性:
-
能够通过this访问所在函数的所有局部变量,参数和其他方法
-
执行方式是异步的,不同于所在函数,执行方式是同步的
var outter = []; function clousetest () { var array = ["one", "two", "three", "four"]; for(var i = 0; i < array.length;i++){ var x = {}; x.no = i; x.text = array[i]; x.invoke = function(){ print(i); } outter.push(x); } } //调用这个函数 clousetest(); console.log(outter[0].invoke()); console.log(outter[1].invoke()); console.log(outter[2].invoke()); console.log(outter[3].invoke()); //由于闭包函数为异步执行方式,在函数迭代完成后,外部函数才开始执行x.invoke(),此时i已经为4,故上述结果均为4 //解决方法将闭包函数改为自执行函数 function clousetest2(){ var array = ["one", "two", "three", "four"]; for(var i = 0; i < array.length;i++){ var x = {}; x.no = i; x.text = array[i]; x.invoke = function(no){ return function(){ print(no); } }(i); outter.push(x); } }
4.3闭包函数的强大用途:
4.3.1自执行函数
优点:
- 有的函数只需要执行一次,其内部变量无需维护
- 函数立即执行,由于外部无法引用它内部的变量,因此在函数执行完后会立刻释放资源,关键是不污染全局对象。
var data= {
table : [],
tree : {}
};
(function(dm){
for(var i = 0; i < dm.table.rows; i++){
var row = dm.table.rows[i];
for(var j = 0; j < row.cells; i++){
drawCell(i, j);
}
}
})(data);//创建一个匿名的自执行函数,并将外部数据传入,函数立即执行,立刻释放资源!!
4.3.2闭包缓存结果
- 不会释放外部的引用,从而函数内部的值可以得以保留。
参考博文链接:http://www.makaidong.com/博客园牛/13582.shtml
4.3.3使用闭包访问函数内部变量
var person = function(){
//变量作用域为函数内部,外部无法访问
var name = "default";
return {
getname : function(){
return name;
},
setname : function(newname){
name = newname;
}
}
}();
console.log(person.name);//直接访问,结果为undefined
console.log(person.getname());
person.setname("abruzzi");
console.log(person.getname());
4.3.4使用闭包函数千万小心 this对象引用的错误
$(function(){
var con = $("#p1");
this.id = "content";
con.click(function(){
console.log(this.id);// 一般的闭包函数可以通过this访问外部的变量和方法
});//但是此函数为匿名函数,默认注册在Windows对象中,所以打印的this.id为标签#p1的id值而非content
});
//非常简单的解决办法:使用一个代理变量self或者that代理当前函数对象
$(function(){
var con = $("#p1");
this.id = "content";
var self=this;
con.click(function(){
console.log(self.id);
});
});
注意this的使用:this表示拥有该方法的所在对象
//比如
function foo(){
this.id=10;
setTimeout(function(){
console.log(this.id);
},1000)
}
var id=21;
foo();
//输出21
//由于foo()方法没有绑定对象,则默认在Windows对象下注册,所以this指向Windows对象
//解决方法一:
//自己绑定一个对象
var foo=function(){
this.id=10;
setTimeout(function(){
console.log(this.id);
},1000)
}
var id=21;
foo();
//输出10
//解决方法二:使用箭头函数,this为固定绑定在定义时的函数对象,而非运行时注册的对象
function foo(){
this.id=10;
setTimeout(()=>{
console.log(this.id);
},1000);
}
var id=21;
foo();
//输出10
//解决方法三:使用一个代理变量self或者that代理当前函数对象
function foo(){
this.id=10;
var self=this;
setTimeout(function(){
console.log(self.id);
},1000)
}
var id=21;
foo();
//输出10
5、强大而又烦恼的回调函数
5.1回调函数的定义:
函数A作为参数传递至另一个函数B,并有函数B执行A,则A称为一个回调函数,如果函数没有名称,则就叫匿名回调函数,简单来说,回调函数是作为一个参数传递执行
//无参数的回调函数
function Buy(name,goods1,callback) {
alert(name+' buy '+goods1);
if(callback&&typeof(callback)==="function")
callback();//此API表示如果传入一个回调函数,则执行此函数
}
Buy('xiaoming','apple',function(){
alert("shopping finish");
});
//自定义一个使用回调的API函数,并进行调用
//有参数的回调函数
//定义使用回调的API函数
function getRecommnedMusic(callback){
var data ={
g_tk:5381,
uin:0,
format:'json',
inCharset:'utf-8',
notice:0,
platform:'h5',
needNewCode:1,
_:Date.now()
};
wx.request({
url: 'http://c.y.qq.com/musichall/fcgi-bin/fcg_yqqhomepagerecommend.fcg',
data:data,
header:{
'Content-Type':'application/json'
},
success:function(res){
if(res.statusCode==200){
console.log(res.data)
callback(res.data) //表示将res.data传入回调函数之中
} else{
console.log(res.data)
}
}
})
}
//调用API函数
getRecommendMusic(
function(data){
var self=this;
if(data.code==0){
self.setData({
slider:data.data.slider,
radioList:data.data.radioList, //这里回调函数直接拿到res.data数据,并将其作为data参数提供给回调函数使用
})
}
}
)
5.2回调函数的特性:
- 回调函数同步执行,在调用函数代码执行完之后,才开始回调
- 异步执行,回调函数不一定执行
5.3回调函数的强大作用
5.3.1通过传参给回调函数直接拿到调用函数的传入数据
可以在调用函数定义时将数据传给回调函数,从而使用回调函数直接拿到调用函数的内部操作数据
//拿jQuery请求数据距离
$.Ajax({
url:'https://www.baidu.com',
data:'void',
success:function(res){
if(res.statusCode==200){
console.log(res.data)
callback(res.data) //表示将res.data传入回调函数之中
} else{
console.log(res.data)
}//这里success就是一个回调函数,res就是已经拿到调用函数所响应的数据
}
})
/*解释:
一般封装API,JavaScript允许传入一个回调函数,定义API时将内部数据作为参数传给回调函数
那么,那么,那么,你在调用这个API时,就可以使用回调函数直接拿到API内部的局部数据,之后就可以使用回调函数进行为所欲为的操作,非常强大,对比闭包函数,要想拿到内部数据,必须返回一个公有方法,并且还需要调用这个公有方法,才能访问
*/
//使用闭包函数获取函数API中的局部数据
var person = function(){
//变量作用域为函数内部,外部无法访问
var name = "default";
return {
getname : function(){
return name;
},
setname : function(newname){
name = newname;
}
}
}();
console.log(person.getname());
person.setname("abruzzi");
console.log(person.getname());
//打印default
//打印abruzzi
//使用回调函数拿到函数内部数据
var person=function(callback){
var name='default';
callback(name);
}
person(function(name){
console.log(name);
})
//打印default
5.3.2封装调用API的两种书写形式:
//方法一:直接balabala一大堆的参数传入
var person=function(setname,url,callback){
var name='default';
callback(setname,name,url);
}
person('myname','baidu.com',function(setname,name,url){
console.log(name+' '+setname+' '+url);
})
//方法二:使用对象语法糖
//定义一个person封装的API
var person=function(options){//传入一个options对象,形式为{}里面可以传入各种参数,如 setname,url,callback
var name='default';
options.callback(options.setname,name,options.url);
}
//调用一个封装好的person()方法,并使用语法糖传入对象参数
person({
setname:'myname',
url:'baidu.com',
callback:function(setname,name,url){
console.log(name+' '+setname+' '+url)
}//采用ES6的语法糖的形式进行书写
})
5.3.3深入异步编程
//所谓异步编程,指函数内部还有函数,则此函数相对于外部函数就是异步执行=====>就是闭包函数
function add(x,y,callback){
console.log(1)
setTimeout(function(){
console.log(3)
var ret=x+y
callback(ret)
},1000)
console.log(2)
}
add(10,20,function(ret){
console.log(ret)
})
//打印结果1,2,3,30
//此函数使用setTimeout闭包函数,异步执行,再使用回调函数,取得ret数据,并将其打印出来
5.3.4 ==模拟封装$.ajax()方法
//注册全局对象
$ = {
//自定义一个ajax全局方法
ajax:function(options){//这里options为一个{ }对象,里面可以传入各种数据,实名回调函数均可
//初始化数据
var method=options.method
var url=options.url
//创建服务器请求对象
xmlhttp=new XMLHttpRequest();
//初始化发送请求方式及路径
xmlhttp.open(method,url,true)
//发送请求
xmlhttp.send()
//当加载完成时调用回调函数,将相应的数据传出去
xmlhttp.onload=function(){
// console.log(xmlhttp.responseText)
options.success(xmlhttp.responseText)//调用options的success实名回调函数,将相应数据传给success回调函数
}
} ///封装完毕!!!
}
//开始调用$.ajax方法
$.ajax({
method:'get',
url:'http://www.baidu.com',
success:function(res){
console.log(xmlhttp.responseText)
}
})
**调用出现成功结果:**这里注意本地直接使用Ajax方法请求访问本地文件,会出现跨域问题,必须本地开启服务器,使用完整的Http的URL请求才能访问成功!!!这里我自己开启了本地的Apache服务器,并将文件放置在了Apache网页主目录www中…
5.4回调函数的烦恼—回调地狱!!!
背景描述:
使用多个异步执行函数无法保证执行顺序,如下:
setTimeout(function(){
console.log(1)
},1000)
setTimeout(function(){
console.log(2)
},0)
setTimeout(function(){
console.log(3)
},1000)
setTimeout(function(){
console.log(4)
},1000)
///打印结果3,1,2,4
//解决办法如下,使用嵌套的回调函数
setTimeout(function(){
console.log(1)
setTimeout(function(){
console.log(2)
setTimeout(function(){
console.log(3)
setTimeout(function(){
console.log(4)
},1000)
},1000)
},0)
},1000)
///必定打印结果1,2,3,4
///思考如果嵌套more个函数呢????于是乎嵌套的回调地狱就出现了,代码的维护,可读性非常差!!
使用promise优化:
//使用Promise存放多个异步任务的容器
//ES6提供Promise的API
var p1=new Promise( function(resolve,reject){
//pending正在做的两大状态,resolve成功,reject解决
//创建一个容器,一旦创建,立即执行,Promise本身不是异步,包含异步任务!!
setTimeout(function(){
var err=true;
var data='我的数据';
if(err){
reject(err);
}else{
resolve(data);
}
console.log(2);
},1000);
console.log(1);
})
var p2=new Promise( function(resolve,reject){
setTimeout(function(){
var err=true;
var data='我的数据';
if(err){
reject(err);
}else{
resolve(data);
}
console.log(3);
},1000);
// console.log(1);
})
var p3=new Promise( function(resolve,reject){
setTimeout(function(){
var err=true;
var data='我的数据';
if(err){
reject(err);
}else{
resolve(data);
}
console.log(4);
},1000);
// console.log(1);
})
//使用Promise.then()方法接受容器的回调函数
p1
.then(function(data){
console.log(data)
return p2
},function(err){
console.log(err)
}) //第一个函数参数为resolve函数,第二个函数为reject函数
.then(function(data){
console.log(data)
return p3
})
自己封装Promise
//自己封装Promise
function PsetTimeout(num){
return new Promise( function(resolve,reject){
setTimeout(function(){
var data='我的数据';
resolve(data);
console.log(num);//回调函数resolve和reject可以不使用
},1000);
// console.log(1);
})
}
PsetTimeout(1)
.then(function(data){
console.log(data)
return PsetTimeout(2)
})
.then(function(data){
console.log(data)
return PsetTimeout(3)
})
.then(function(data){
console.log(data)
return PsetTimeout(4)
})
/*
打印结果如下: 1 我的数据
2 我的数据
3 我的数据
4 我的数据
*/
//建议封装好函数,代码易于维护,而且有木有觉得很优美,对比上一段长长的代码
Promise的使用场景:在多个ajax请求接口时,必须使用promise封装
//封装一个get的promise请求
function get(method,url){
return new Promise(function(resovle,reject){
var xmlhttp=new XMLHttpRequest()
xmlhttp.open(method,url,true)
xmlhttp.send()
xmlhttp.onload=function(){
resovle(xmlhttp.responseText)
}
})
}
get('get','http://localhost/callback-ajax/callback.html')//请求接口一的数据
.then(function(data){
console.log(data)
return get('get','http://localhost/callback-ajax/callback2.html')
//一请求完毕之后,再请求接口二的数据
})
.then(function(data){
console.log(data)
})