读javascript高级程序设计16-几条函数小技巧
2014-09-18 11:40
134 查看
内容概要
作用域安全的构造函数
惰性载入函数
函数绑定
函数节流
但是,如果没有使用new操作符,而是将构造函数当作普通函数调用时,this会指向window对象。
因此,在构造函数中应该首先检查this是否为正确的类型实例,这种方式就是作用域安全的构造函数。
Person) { this.name = name; } else { return new
在该构造函数基础上,结合原型链的实现如下:
=new
通过作用域安全的构造函数,可以保证在缺少new操作符调用构造函数的时候在正确的执行环境中进行。
实际上对于同一款浏览器每次创建对象时都会执行相同的分支,因此每次都进行if判断是多余的,使用惰性载入函数可以实现函数执行分支只发生一次。有两种常见方案:
方案一:在第一次调用函数时,根据分支结果,将该函数替换为另一个按合适的方式执行的函数。我们改进上面的函数如下:
这样改进后,每次执行时不必再执行多个if分支判断,而是直接执行替换后的简洁函数。它只会在第一次调用时有一些性能损失。
方案二:在声明函数时,就指定适当的函数。同样改进上面的例子:
这种方案不同的是使用var声明函数,并为每个分支指定了匿名且自动执行的函数。这样,函数第一次加载的时候就确定了执行哪个分支的函数。
其实我们可以利用闭包写法修复这个问题:
但是用太多的闭包看上去代码不够简洁。在很多js库中,都会定义bind()函数来实现函数绑定。
这个bind函数实现比较简单,两个参数分别为要调用的函数和函数执行环境,执行结果返回一个在指定执行环境调用函数的函数,它把其内部函数的arguments参数全部传递过去。调用方法如下:
例如下面的滚动事件:
当页面滚动的时候,会持续的输出结果。这种高频率的响应,如果方法复杂一些的话会耗用还多的性能。对于这种情况我们可以进行如下优化:
throttle方法有两个参数:要执行的方法和作用域。第一次调用时会创建定时器,后续调用时都是先消除已有的定时器,然后重新创建定时器。这样一来只有最后一次调用之后100ms后才会将相应的方法加入执行队列。
作用域安全的构造函数
惰性载入函数
函数绑定
函数节流
一、作用域安全的构造函数
我们知道,当使用new操作符调用构造函数时,构造函数内部的this会指向新创建对象的实例。function Person(name){ this.name=name; } var p=new Person('peter'); console.log(p.name);//结果:perter
但是,如果没有使用new操作符,而是将构造函数当作普通函数调用时,this会指向window对象。
var p1=Person('peter'); console.log(p1.name);//报错 console.log(window.name);//peter
因此,在构造函数中应该首先检查this是否为正确的类型实例,这种方式就是作用域安全的构造函数。
function Person(name) { if (this instanceof
Person) { this.name = name; } else { return new
Person(name); } } var p = new Person('peter'); console.log(p.name); //perter var p1 = Person('peter'); console.log(p1.name); //perter console.log(window.name); //报错
在该构造函数基础上,结合原型链的实现如下:
function Person(name) { if (this instanceof Person) { this.name = name; } else { return new Person(name); } } function Student(name,sno){ Person.call(this,name); this.sno=sno; } Student.prototype
=new
Person(); var s=new Student('peter','N0015'); console.log(s.name);
通过作用域安全的构造函数,可以保证在缺少new操作符调用构造函数的时候在正确的执行环境中进行。
二、惰性载入函数
有些函数包含很多分支,每次调用时都会执行一遍if分支判断,但实际上可能每次走的都是相同分支。看下这个创建CORS对象的方法:function createCORSRequest(method,url){ //创建XHR对象 var xhr = new XMLHttpRequest(); //启动请求 if("withCredentials" in xhr){ xhr.open(method,url,true); }else if(typeof XDomainRequest!='undefined'){ xhr=new XDomainRequest(); xhr.open(method,url); }else{ xhr=null; } return xhr; } var xhr1 = createCORSRequest('get', 'http://www.othersite.com/weather.ashx'); var xhr2 = createCORSRequest('get', 'http://www.othersite.com/articles.ashx');
实际上对于同一款浏览器每次创建对象时都会执行相同的分支,因此每次都进行if判断是多余的,使用惰性载入函数可以实现函数执行分支只发生一次。有两种常见方案:
方案一:在第一次调用函数时,根据分支结果,将该函数替换为另一个按合适的方式执行的函数。我们改进上面的函数如下:
function createCORSRequest(method, url) { var xhr0 = new XMLHttpRequest(); if ('withCredentials' in xhr0) { var xhr = new XMLHttpRequest(); createCORSRequest = function (method, url) { xhr.open(method, url, true); return xhr; } } else if (typeof XDomainRequest != 'undefined') { createCORSRequest = function (method, url) { var xhr = new XMLHttpRequest(); xhr = new XDomainRequest(); xhr.open(method, url); return xhr; } } else { createCORSRequest = function (method, url) { return null; } } return createCORSRequest(method, url); }
这样改进后,每次执行时不必再执行多个if分支判断,而是直接执行替换后的简洁函数。它只会在第一次调用时有一些性能损失。
方案二:在声明函数时,就指定适当的函数。同样改进上面的例子:
var createCORSRequest=(function(method, url) { var xhr0 = new XMLHttpRequest(); if ('withCredentials' in xhr0) { return function (method, url) { var xhr = new XMLHttpRequest(); xhr.open(method, url, true); return xhr; } } else if (typeof XDomainRequest != 'undefined') { return function (method, url) { var xhr = new XMLHttpRequest(); xhr = new XDomainRequest(); xhr.open(method, url); return xhr; } } else { return function (method, url) { return null; } } })();
这种方案不同的是使用var声明函数,并为每个分支指定了匿名且自动执行的函数。这样,函数第一次加载的时候就确定了执行哪个分支的函数。
三、函数绑定
在执行回调函数或者事件处理程序时,常常需要把函数作为变量传递,此时我们需要保存函数的代码执行环境。函数绑定就是要创建一个函数,可以在特定的this环境中以指定参数调用另一个函数var demo = { message: 'hello world', show: function () { console.log(this.message); } } var obj = document.getElementById('my-btn'); EventUtil.addHandler(obj, 'click', demo.show);//点击按钮结果undefined
其实我们可以利用闭包写法修复这个问题:
EventUtil.addHandler(obj, 'click', function (event) { demo.show(); });
但是用太多的闭包看上去代码不够简洁。在很多js库中,都会定义bind()函数来实现函数绑定。
function bind(fn, context) { return function () { return fn.apply(context, arguments); } }
这个bind函数实现比较简单,两个参数分别为要调用的函数和函数执行环境,执行结果返回一个在指定执行环境调用函数的函数,它把其内部函数的arguments参数全部传递过去。调用方法如下:
EventUtil.addHandler(obj, 'click',bind(demo.show,demo));
四、函数节流
对于周期性执行的代码,应该进行函数节流。例如下面的滚动事件:
window.onscroll = function(){ throttle(demo,window); }
当页面滚动的时候,会持续的输出结果。这种高频率的响应,如果方法复杂一些的话会耗用还多的性能。对于这种情况我们可以进行如下优化:
//设置和消除定时器
function throttle(method,context){
clearTimeout(method.id);
method.id=setTimeout(function(){
method.call(context)
},100);
}
function demo(){
console.log(1);
}
window.onscroll = function(){ throttle(demo,window); }
throttle方法有两个参数:要执行的方法和作用域。第一次调用时会创建定时器,后续调用时都是先消除已有的定时器,然后重新创建定时器。这样一来只有最后一次调用之后100ms后才会将相应的方法加入执行队列。
相关文章推荐
- 读javascript高级程序设计16-几条函数小技巧
- JavaScript高级程序设计(第3版)学习笔记8 js函数(中)
- JavaScript高级程序设计之函数性能
- JavaScript高级程序设计之函数表达式之模仿块级作用域第7.3讲笔记
- JavaScript高级程序设计之函数表达式之私有变量之增强的模块模式第7.4.3讲笔记
- <JavaScript高级程序设计>笔记一: 函数function
- Javascript高级程序设计——this、闭包、函数表达式
- Javascript高级程序设计 第七章 --- 函数表达式
- JavaScript高级程序设计之函数表达式之递归第7.1讲笔记
- javascript高级程序设计笔记-第七章(函数表达式)
- JavaScript高级程序设计16.pdf
- Javascript高级程序设计——函数声明与函数表达式的区别
- 读javascript高级程序设计01-基本概念、数据类型、函数
- JavaScript高级程序设计之函数表达式之闭包之关于 this 对象第7.2.2讲笔记
- JavaScript高级程序设计(第3版)学习笔记8 js函数(中)
- JavaScript高级程序设计之函数
- javascript高级程序设计 5.5.3作为值得函数
- javascript 高级程序设计:深入理解Object.keys 函数
- JavaScript高级程序设计学习笔记第五章--引用类型(函数部分)
- JavaScript高级程序设计之函数表达式之私有变量之模块模式第7.4.2讲笔记