您的位置:首页 > Web前端 > JavaScript

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

2017-04-06 15:16 393 查看

1.安全的原生类型检验

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

[javascript] view
plain copy

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方法是不会被覆盖的。

[javascript] view
plain copy

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.作用域安全的构造函数

  在说对象创建的时候,我们给出了很多构造对象的方法,后来在说继承的时候,也使用了那些方法。但那些构造对象的方法都是不安全的。

[javascript] view
plain copy

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的运行结果是一样的,这在之前有一个解决方案,现在提出第二个解决方案。

[javascript] view
plain copy

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,然后就有了一下的函数:

[javascript] view
plain copy

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判断,这明显会降低效率。于是,可以把函数重写:

[javascript] view
plain copy

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,和我们的预期相左。

[javascript] view
plain copy

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。解决这一问题的思路是创建闭包:

[javascript] view
plain copy

btn.addEventListener("click",function(event){

handle.showMsg(event);

});

这次给btn的click事件绑定了一个闭包,闭包中直接通过handle调用了showMsg,符合我们的预期了。这样写虽然能解决问题,但扩展性不是很好。我们真正要达到的目的,是让某个函数永远在某个执行空间中执行。这样,自然会想到函数的apply方法。于是可以创建这样一个闭包。

[javascript] view
plain copy

function bind(func,content){

return function(){

return func.apply(content,argument);

}

}

bind函数传入两个参数,一个是待执行函数,一个是待执行函数的执行环境。bind函数返回一个闭包,闭包执行时,通过apply方式调用待执行函数,并为其指定执行空间。使用时,只要做如下变换即可:

[javascript] view
plain copy

btn.addEventListener("click",bind(handle.showMsg,handle));
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐