《JavaScript高级程序设计 第三版》学习笔记 (十三)高级函数

1.安全的原生类型检验

  js的原生类型,可以用typeof判断,但有时会失效。比如typeof array返回的不是Array,而是Object;再比如老版本IE,会将function识别为Object。另外一个判断类型的是instanceof,它能够在对象的原型链中查找构造函数,但这种方法对于原生类型的判断也会出问题,因为某些原生构造函数用户是可以覆盖的,比如Array和JSON。请看下面的例子:
function Array(){
	this.type="new Array";
	this.length=7;	
}

var a=new Array();
alert(a.length);//7
alert(typeof a);//object
alert(a instanceof Array);//true

var b=[1,2,3];
alert(b.length);//3
alert(b[1]);//2
alert(typeof b);//object
alert(b instanceof Array);//false
这是个很有意思的例子,我们覆盖了Array的构造函数,然后创建了一个不是Array的对象a,但instanceof把它识别成了Array,相反,本来是Array的b,却误判成其他。
  解决这一个问题的方法是利用toString。原生类型调用toString后会返回诸如"[object array]"的字符串,即便构造函数被恶意覆盖了,原生类型的toString方法是不会被覆盖的。
function Array(){this.length=7;}
var a=1;
var b=true;
var c="string";
var d=[];
var e=new Array();

console.log(Object.prototype.toString.call(a));//[object Number] 
console.log(Object.prototype.toString.call(b));//[object Boolean]
console.log(Object.prototype.toString.call(c));//[object String]
console.log(Object.prototype.toString.call(d));//[object Array]
console.log(Object.prototype.toString.call(e));//[object Object]
注意:在老IE中以COM对象实现的函数,返回的是Object

2.作用域安全的构造函数

  在说对象创建的时候,我们给出了很多构造对象的方法,后来在说继承的时候,也使用了那些方法。但那些构造对象的方法都是不安全的。
function Person(name,age){
	this.name=name;
	this.age=age;	
}

var p1=new Person("Brain",18);
var p2=Person("Brain",18);
alert(p1.name);//Brain
alert(p2);//undefined
alert(window.name);//Brain
上面例子里,有一个构造函数Person,p1是使用正确调用方式创建出来的对象,p2则使用了不正确的调用。这种不正确的调用有一个非常严重的副作用,就是在Person的执行空间中加入了name属性和age属性,当前的执行空间是window。解决这个问题的思路是让构造函数new和不new的运行结果是一样的,这在之前有一个解决方案,现在提出第二个解决方案。
function Person(name,age){
	if(this instanceof Person){
		this.name=name;
		this.age=age;
	}else{
		return new Person(name,age);
	}
}

var p1=new Person("Brain",18);
var p2=Person("TOM",18);
alert(p1.name);//Brain
alert(p2.name);//TOM
alert(window.name);//""

3.懒惰载入函数

  在写跨浏览器的函数时,难免要判断浏览器是否支持某功能。比如在IE8-的浏览器中绑定事件,是不能用addEventListener的,只能用attachEvent,然后就有了一下的函数:
function addListener(element,type,func){
	if(typeof element.addEventListener == "function"){
		console.log("addEventListener");
		element.addEventListener(type.toLocaleLowerCase(),func,false);	
	}else{
		console.log("attachEvent");
		element.attachEvent("on"+type,func);
	}
}
addListener(document.getElementById("box1"),"Click",function(){});
addListener(document.getElementById("box2"),"Click",function(){});
/*控制台输出
addEventListener 
addEventListener 
*/
这样做看似不错,但往往这个函数要调用很多次,每次都要进行if判断,这明显会降低效率。于是,可以把函数重写:
function addListener(element,type,func){
	if(typeof element.addEventListener == "function"){
		console.log("addEventListener");
		element.addEventListener(type.toLocaleLowerCase(),func,false);
		addListener=function(e,t,f){
			console.log("override addEventListener");
			e.addEventListener(t.toLocaleLowerCase(),f);	
		}	
	}else{
		console.log("attachEvent");
		element.attachEvent("on"+type,func);
		addListener=function(e,t,f){
			console.log("override attachEvent");
			e.attachEvent("on"+t,f);
		}
	}
}
addListener(document.getElementById("box1"),"Click",function(){});
addListener(document.getElementById("box2"),"Click",function(){});
/*控制台输出
addEventListener 
override addEventListener 
*/

4.函数绑定

  有些时候,我们希望某个函数的执行,一直呆在事先规定好的执行环境中,而且这个函数可以由事件触发,这种需求是很常见的。比如下面这个例子,通过对象名称调用对象的方法,结果如我们预期;但如果想通过按钮点击触发showMsg,alert出来的确实undefined,和我们的预期相左。
var handle={
	message:"handle mesasge",
	showMsg:function(event){
		alert(this.message);
	}
}
handle.showMsg();//handle message
btn.addEventListener("click",handle.showMsg);
不符合预期的原因,前面几篇博客已经说过了:handle.showMsg是一个函数的指针,把函数指针添加到按钮的click事件中,函数的执行空间不再是handle,而是btn,btn没有message属性,因此提示undefined。解决这一问题的思路是创建闭包:
btn.addEventListener("click",function(event){
	handle.showMsg(event);
});
这次给btn的click事件绑定了一个闭包,闭包中直接通过handle调用了showMsg,符合我们的预期了。这样写虽然能解决问题,但扩展性不是很好。我们真正要达到的目的,是让某个函数永远在某个执行空间中执行。这样,自然会想到函数的apply方法。于是可以创建这样一个闭包。
function bind(func,content){
	return function(){
		return func.apply(content,argument);
	}
}
bind函数传入两个参数,一个是待执行函数,一个是待执行函数的执行环境。bind函数返回一个闭包,闭包执行时,通过apply方式调用待执行函数,并为其指定执行空间。使用时,只要做如下变换即可:
btn.addEventListener("click",bind(handle.showMsg,handle));
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值