读javascript高级程序设计16-几条函数小技巧

内容概要

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

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

我们知道,当使用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后才会将相应的方法加入执行队列。

时间: 2024-08-29 07:27:31

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

JavaScript高级程序设计16.pdf

第8章 BOM BOM的核心对象就是window,它表示浏览器的一个实例,在浏览器中window对象有双重角色,它既是JavaScript访问浏览器的一个接口,又是规定的Global对象,因此所有在全局作用域中声明变量.函数都会是window对象的属性和方法 定义全局变量与在window对象上直接定义属性的差别是全局变量不能通过delete操作符删除,而直接在window对象上定义的属性可以尝试访问未声明的变量会抛出错误,但通过查询window对象,可以知道某个可能未声明的变量是否存在 如果页面

读javascript高级程序设计15-Ajax,CORS,JSONP,Img Ping

平时用惯了jQuery.ajax之类的方法,却时常忽略了它背后的实现,本文是学习了AJAX基础及几种跨域解决方案之后的一些收获. 一.AJAX——XMLHttpRequest 谈起Ajax我们都很熟悉,它的核心对象是XMLHttpRequest(简称XHR). 1.创建对象: 在ie7及以上版本支持原生的写法创建该对象. var xhr=new XMLHttpRequest(); 2.发送请求: open(type,url,isasync):第一个参数是请求类型(get,post),第二个参数是

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

虽然Object构造函数或者对象字面量都可以用来创建单个对象,但是缺点非常明显:使用同一接口创建很多对象,会产生大量重复代码. 工厂模式  1 function CreatePerson(name,age,job){ 2         var o=new Object(); 3         o.name=name; 4         o.age=age; 5         o.job=job; 6         o.sayName=function(){ 7            

读书时间《JavaScript高级程序设计》三:函数,闭包,作用域

上一次看了第6章,面向对象.这里接着看第7章. 第7章:函数表达式 定义函数有两种方式:函数声明.函数表达式 //函数声明 function functionName(arg0,arg1,arg2){ //code... } //函数表达式 var functionName = function(arg0,arg1,arg2){ //code... }; 函数声明有个重要的特征是函数申明提升.就是在执行代码前会先读取函数声明,意味着可以把函数声明放在调用它的语句后面. //函数声明提升 sayH

《JavaScript高级程序设计》3.7 函数

位于return语句之后的代码不会执行; return语句也可以不带有任何返回值. 这种情况下, 函数在停止执行后会返回undefined值. 这种用法一般用在需要提前停止函数执行而又不需要返回值的情况下. 严格模式对函数的限制: 不能把函数命名为eval或arguments 不能把参数命名为eval或arguments 不能出现两个命名函数同名的情况 如果发生以上错误,就会导致语法错误,代码无法执行 3.7.1 理解参数 在ECMAScript中,参数在内部是用一个数组来表示的. 因此解析器不

[已读]JavaScript高级程序设计(第3版)

从去年开始看,因为太长,总是没有办法一口气把它看完,再加上它与第二版大部分一致,读起来兴致会更缺一点. 与第二版相比,它最大的改变就是增加了很多html5的内容,譬如:Object对象的一些新东西,数据属性.访问器属性及相应的一些方法;比如它对跨域常见方法的比较和总结:postMessage,IE8的XDR,升级的XHR,jsonp跨域原理,单向的图片ping;又比如对数据推送的一些介绍,长轮询和http流是什么样子,单向的SSE和双向的Web Socket的详细介绍和比较. PS,关于SSE,

读javascript高级程序设计17-在线检测,cookie,子cookie

一.在线状态检测 开发离线应用时,往往在离线状态时把数据存在本地,而在联机状态时再把数据发送到服务器.html5提供了检测在线状态的方法:navigator.onLine和online/offline事件. 1.navigator.onLine属性 表示当前的网络状态是否在线,true表示在线,false表示离线.当网络状态变化时,该属性也会随之变化. 2.online和offline事件 HTML5提供了这两个事件,会在网络状态变化时触发.online在网络由离线变为在线时触发:offline

《JavaScript高级程序设计》读书笔记 ---函数

函数函数对任何语言来说都是一个核心的概念.通过函数可以封装任意多条语句,而且可以在任何地方.任何时候调用执行.ECMAScript 中的函数使用function 关键字来声明,后跟一组参数以及函数体.函数的基本语法如下所示: function functionName(arg0, arg1,...,argN) { statements}以下是一个函数示例:function sayHi(name, message) { alert("Hello " + name + ",&qu

javaScript高级程序设计--第7章函数表达式

1.定义函数的方式有两种: a.函数声明: getName(); function getName(){ alert("123")}; 函数声明的特征:函数声明的提升,所以上面的例子才可以在函数声明前调用函数而不报错 b.函数表达式(又叫匿名函数,注意不能在表达式完成前调用,会报错) var getName = function(){alert("123")}; getName(); 2.递归 arguments.callee  指向一个正在执行的函数的指针,所以实