内容概要

  • 作用域安全的构造函数
  • 惰性载入函数
  • 函数绑定
  • 函数节流

一、作用域安全的构造函数

我们知道,当使用new操作符调用构造函数时,构造函数内部的this会指向新创建对象的实例。

  1. function Person(name){
  2. this.name=name;
  3. }
  4. var p=new Person('peter');
  5. console.log(p.name);//结果:perter

但是,如果没有使用new操作符,而是将构造函数当作普通函数调用时,this会指向window对象。

  1. var p1=Person('peter');
  2. console.log(p1.name);//报错
  3. console.log(window.name);//peter

因此,在构造函数中应该首先检查this是否为正确的类型实例,这种方式就是作用域安全的构造函数。

  1. function Person(name) {
  2. if (this instanceof

Person) { this.name = name; } else { return new

  1. Person(name);
  2. }
  3. }
  4. var p = new Person('peter');
  5. console.log(p.name); //perter
  6. var p1 = Person('peter');
  7. console.log(p1.name); //perter
  8. console.log(window.name); //报错

在该构造函数基础上,结合原型链的实现如下:

  1. function Person(name) {
  2. if (this instanceof Person) {
  3. this.name = name;
  4. }
  5. else {
  6. return new Person(name);
  7. }
  8. }
  9. function Student(name,sno){
  10. Person.call(this,name);
  11. this.sno=sno;
  12. }
  13. Student.prototype

=new

  1. Person();
  2. var s=new Student('peter','N0015');
  3. console.log(s.name);

通过作用域安全的构造函数,可以保证在缺少new操作符调用构造函数的时候在正确的执行环境中进行。

二、惰性载入函数

有些函数包含很多分支,每次调用时都会执行一遍if分支判断,但实际上可能每次走的都是相同分支。看下这个创建CORS对象的方法:

  1. function createCORSRequest(method,url){
  2. //创建XHR对象
  3. var xhr = new XMLHttpRequest();
  4. //启动请求
  5. if("withCredentials" in xhr){
  6. xhr.open(method,url,true);
  7. }else if(typeof XDomainRequest!='undefined'){
  8. xhr=new XDomainRequest();
  9. xhr.open(method,url);
  10. }else{
  11. xhr=null;
  12. }
  13. return xhr;
  14. }
  15.  
  16. var xhr1 = createCORSRequest('get', 'http://www.othersite.com/weather.ashx');
  17. var xhr2 = createCORSRequest('get', 'http://www.othersite.com/articles.ashx');

实际上对于同一款浏览器每次创建对象时都会执行相同的分支,因此每次都进行if判断是多余的,使用惰性载入函数可以实现函数执行分支只发生一次。有两种常见方案:

方案一:在第一次调用函数时,根据分支结果,将该函数替换为另一个按合适的方式执行的函数。我们改进上面的函数如下:

  1. function createCORSRequest(method, url) {
  2. var xhr0 = new XMLHttpRequest();
  3. if ('withCredentials' in xhr0) {
  4. var xhr = new XMLHttpRequest();
  5. createCORSRequest = function (method, url) {
  6. xhr.open(method, url, true);
  7. return xhr;
  8. }
  9. } else if (typeof XDomainRequest != 'undefined') {
  10. createCORSRequest = function (method, url) {
  11. var xhr = new XMLHttpRequest();
  12. xhr = new XDomainRequest();
  13. xhr.open(method, url);
  14. return xhr;
  15. }
  16. } else {
  17. createCORSRequest = function (method, url) {
  18. return null;
  19. }
  20. }
  21. return createCORSRequest(method, url);
  22. }

这样改进后,每次执行时不必再执行多个if分支判断,而是直接执行替换后的简洁函数。它只会在第一次调用时有一些性能损失。

方案二:在声明函数时,就指定适当的函数。同样改进上面的例子:

  1. var createCORSRequest=(function(method, url) {
  2. var xhr0 = new XMLHttpRequest();
  3. if ('withCredentials' in xhr0) {
  4. return function (method, url) {
  5. var xhr = new XMLHttpRequest();
  6. xhr.open(method, url, true);
  7. return xhr;
  8. }
  9. } else if (typeof XDomainRequest != 'undefined') {
  10. return function (method, url) {
  11. var xhr = new XMLHttpRequest();
  12. xhr = new XDomainRequest();
  13. xhr.open(method, url);
  14. return xhr;
  15. }
  16. } else {
  17. return function (method, url) {
  18. return null;
  19. }
  20. }
  21. })();

这种方案不同的是使用var声明函数,并为每个分支指定了匿名且自动执行的函数。这样,函数第一次加载的时候就确定了执行哪个分支的函数。

三、函数绑定

在执行回调函数或者事件处理程序时,常常需要把函数作为变量传递,此时我们需要保存函数的代码执行环境。函数绑定就是要创建一个函数,可以在特定的this环境中以指定参数调用另一个函数

  1. var demo = {
  2. message: 'hello world',
  3. show: function () {
  4. console.log(this.message);
  5. }
  6. }
  7. var obj = document.getElementById('my-btn');
  8. EventUtil.addHandler(obj, 'click', demo.show);//点击按钮结果undefined

其实我们可以利用闭包写法修复这个问题:

  1. EventUtil.addHandler(obj, 'click', function (event) {
  2. demo.show();
  3. });

但是用太多的闭包看上去代码不够简洁。在很多js库中,都会定义bind()函数来实现函数绑定。

  1. function bind(fn, context) {
  2. return function () {
  3. return fn.apply(context, arguments);
  4. }
  5. }

这个bind函数实现比较简单,两个参数分别为要调用的函数和函数执行环境,执行结果返回一个在指定执行环境调用函数的函数,它把其内部函数的arguments参数全部传递过去。调用方法如下:

  1. EventUtil.addHandler(obj, 'click',bind(demo.show,demo));

四、函数节流

对于周期性执行的代码,应该进行函数节流。

例如下面的滚动事件:

  1. window.onscroll = function(){
  2. throttle(demo,window);
  3. }

当页面滚动的时候,会持续的输出结果。这种高频率的响应,如果方法复杂一些的话会耗用还多的性能。对于这种情况我们可以进行如下优化:

  1. //设置和消除定时器
  2. function throttle(method,context){
  3. clearTimeout(method.id);
  4. method.id=setTimeout(function(){
  5. method.call(context)
  6. },100);
  7. }
  8. function demo(){
  9. console.log(1);
  10. }
  11. window.onscroll = function(){
  12. throttle(demo,window);
  13. }

throttle方法有两个参数:要执行的方法和作用域。第一次调用时会创建定时器,后续调用时都是先消除已有的定时器,然后重新创建定时器。这样一来只有最后一次调用之后100ms后才会将相应的方法加入执行队列。

读javascript高级程序设计16-几条函数小技巧的更多相关文章

  1. 读javascript高级程序设计00-目录

    javascript高级编程读书笔记系列,也是本砖头书.感觉js是一种很好上手的语言,不过本书细细读来发现了很多之前不了解的细节,受益良多.<br/>本笔记是为了方便日后查阅,仅作学习交流 ...

  2. 读javascript高级程序设计-目录

    javascript高级编程读书笔记系列,也是本砖头书.感觉js是一种很好上手的语言,不过本书细细读来发现了很多之前不了解的细节,受益良多.<br/>本笔记是为了方便日后查阅,仅作学习交流 ...

  3. 读javascript高级程序设计08-引用类型之Global、Math、String

    一.Global 所有在全局作用域定义的属性和方法,都属于Global对象. 1.URI编码: encodeURI():主要用于对整个URI编码.它不会对本身属于URI的特殊字符进行编码. encod ...

  4. 读javascript高级程序设计01-基本概念、数据类型、函数

    一. javascript构成 1.javascript实现由三部分组成: ECMAScript:核心语言功能 DOM:文档对象模型,提供访问和操作网页内容的方法和接口 BOM:浏览器对象模型,提供与 ...

  5. 读Javascript高级程序设计第三版第六章面向对象设计--创建对象

    虽然Object构造函数或者对象字面量都可以用来创建单个对象,但是缺点非常明显:使用同一接口创建很多对象,会产生大量重复代码. 工厂模式  1 function CreatePerson(name,a ...

  6. 读javascript高级程序设计02-变量作用域

    一. 延长作用域链 有些语句可以在作用域前端临时增加一个变量对象,该变量对象在代码执行完成后会被移除. ①with语句延长作用域. function buildUrl(){ var qs=" ...

  7. 读javascript高级程序设计05-面向对象之创建对象

    1.工厂模式 工厂模式是一种常用的创建对象的模式,可以使用以下函数封装创建对象的细节: function CreatePerson(name,age){ var p=new Object(); p.n ...

  8. 读javascript高级程序设计07-引用类型、Object、Array

    一.引用类型 ECMAScript是支持面向对象的,可以通过引用类型描述一类对象所具有的属性和方法. 创建对象实例的方法时是用new 操作符加构造函数:var p=new Person(). 二.Ob ...

  9. 读javascript高级程序设计10-DOM

    一.节点关系 元素的childNodes属性来表示其所有子节点,它是一个NodeList对象,会随着DOM结构的变化动态变化. hasChildNodes():是否有子节点. var headline ...

随机推荐

  1. react input 获取/失去焦点

    <div className={ this.state.focus ? "dis_bottom_left_onfocus" : "dis_bottom_left&q ...

  2. Linux:SSH错误"WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! "

    hadoop@master:~$ scp /home/hadoop/.ssh/authorized_keys node3:/home/hadoop/.ssh/ @@@@@@@@@@@@@@@@@@@@ ...

  3. Linux Server 14.04输入数字变为了*

    虚拟机装好了Ubuntu14.04后,大键盘上边的数字输入时变为了*(奇葩的加密吗?!!) 从网上看到别人都遇到的是小键盘输入数字时,会变为字符,我这小键盘却是没问题,大键盘有问题奇葩. 如果小键盘输 ...

  4. YII2.0--------这篇文章记录我学习YII2.0的过程吧,也可以让更多的人少走弯路

    1.情况:今天我从github上下载了一个项目,本以为直接丢到根目录运行就行了,但是不行. 解决办法:首先安装git,安装步骤这里不讲了,稍微讲一下配置环境变量.

  5. eclipse +VISUALSVN SERVER 创建版本控制器,防止误操作(可视化操作,简单方便,不需要修改配置文件)

    第一步:为eclipse安装Subclipse插件 打开eclipse,点击help-->Install New Software...弹出对话框,点击Add..(新增),以http://sub ...

  6. 使用 gulp 搭建前端环境入门篇(转载)

    本文转载自: 使用 gulp 搭建前端环境入门篇

  7. Repeater嵌套绑定Repeater

    前台Html代码 <asp:Repeater runat="server" ID="rpList" OnItemDataBound="rpLis ...

  8. 阿里云OneinStack,Linux下tomcat命令

    阿里云OneinStack,Linux下tomcat命令 Linux下如何查看tomcat是否启动在Linux系统下,重启Tomcat使用命令操作的首先,进入Tomcat下的bin目录cd /usr/ ...

  9. 【转】mysql安装图解

    转载地址:http://www.jb51.net/article/23876.htm 很多朋友刚开始接触mysql数据库服务器,下面是网友整理的一篇mysql的安装教程,步骤明细也有详细的说明.   ...

  10. 2015弱校联盟(1) -A. Easy Math

    A. Easy Math Time Limit: 2000ms Memory Limit: 65536KB Given n integers a1,a2,-,an, check if the sum ...