JavaScript高级程序设计学习22_高级技巧

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

关于作用域安全的构造函数的贴心提示。实现这个模式后,你就锁定了可以调用构造函数的环境。如果你使用构造函数窃取模式的继承且不使用原型链,那么这个继承很可能被破坏。

function Polygon(sides){
  if (this instanceof Polygon) {
    this.sides = sides;
    this.getArea = function(){
        return 0;
    };
  } else {
    return new Polygon(sides);
  }
}
function Rectangle(width, height){
  Polygon.call(this, 2);
  this.width = width;
  this.height = height;
  this.getArea = function(){
    return this.width * this.height;
  };
}
Rectangle.prototype = new Polygon();
var rect = new Rectangle(5, 10);
alert(rect.sides); //2

2、惰性载入函数

惰性载入表示函数执行的分支仅会发生一次。有两种实现惰性载入的方式,

第一种就是在函数被调用时再处理函数。在第一次调用的过程中,该函数会被覆盖为另外一个按合适方式执行的函数,这样任何对 原 函数 的调 用 都不 用再 经 过执 行的 分 支了 。例 如 ,可 以用 下 面的 方式 使 用惰 性载 入 重 写createXHR()。

// 在这个惰性载入的 createXHR()中, if 语句的每一个分支都会为 createXHR 变量赋值,有效覆
// 盖了原有的函数。最后一步便是调用新赋的函数。下一次调用 createXHR()的时候,就会直接调用被
// 分配的函数,这样就不用再次执行 if 语句了。
function createXHR(){
  if (typeof XMLHttpRequest != "undefined"){
    createXHR = function(){
      return new XMLHttpRequest();
    };
  } else if (typeof ActiveXObject != "undefined"){
    createXHR = function(){
      if (typeof arguments.callee.activeXString != "string"){
        var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0","MSXML2.XMLHttp"],i, len;
      for (i=0,len=versions.length; i < len; i++){
        try {
          new ActiveXObject(versions[i]);
          arguments.callee.activeXString = versions[i];
          break;
        } catch (ex){
        //skip
        }
      }
    }
    return new ActiveXObject(arguments.callee.activeXString);
  };
} else {
    createXHR = function(){
      throw new Error("No XHR object available.");
    };
  }
  return createXHR();
}

第二种实现惰性载入的方式是在声明函数时就指定适当的函数。这样,第一次调用函数时就不会损失性能了,而在代码首次加载时会损失一点性能。

var createXHR = (function(){
  if (typeof XMLHttpRequest != "undefined"){
    return function(){
      return new XMLHttpRequest();
    };
  } else if (typeof ActiveXObject != "undefined"){
    return function(){
      if (typeof arguments.callee.activeXString != "string"){
        var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0","MSXML2.XMLHttp"],i, len;
        for (i=0,len=versions.length; i < len; i++){
          try {
            new ActiveXObject(versions[i]);
            arguments.callee.activeXString = versions[i];
            break;
          } catch (ex){
          //skip
          }
        }
      }
    return new ActiveXObject(arguments.callee.activeXString);
    };
  } else {
    return function(){
      throw new Error("No XHR object available.");
    };
  }
})();

3、函数绑定

另一个日益流行的高级技巧叫做函数绑定。函数绑定要创建一个函数,可以在特定的 this 环境中以指定参数调用另一个函数。该技巧常常和回调函数与事件处理程序一起使用,以便在将函数作为变量传递的同时保留代码执行环境。请看以下例子:

var handler = {
  message: "Event handled",
  handleClick: function(event){
    alert(this.message);
  }
};
var btn = document.getElementById("my-btn");

btn.addEventListener('click',handler.handleClick)
/*在上面这个例子中,创建了一个叫做 handler 的对象。 handler.handleClick()方法被分配为
一个 DOM 按钮的事件处理程序。当按下该按钮时,就调用该函数,显示一个警告框。虽然貌似警告框
应 该 显 示 Event handled , 然 而 实 际 上 显 示 的 是 undefiend 。 这 个 问 题 在 于 没 有 保 存
handler.handleClick()的环境,所以 this 对象最后是指向了 DOM 按钮而非 handler(在 IE8 中,
this 指向 window。)可以如下面例子所示,使用一个闭包来修正这个问题。*/

btn.addEventListener('click',function(event){
  handler.handleClick(event);
})

这个解决方案在 onclick 事件处理程序内使用了一个闭包直接调用 handler.handleClick()。当然,这是特定于这段代码的解决方案。创建多个闭包可能会令代码变得难于理解和调试。因此,很多JavaScript 库实现了一个可以将函数绑定到指定环境的函数。这个函数一般都叫 bind()。一个简单的 bind()函数接受一个函数和一个环境,并返回一个在给定环境中调用给定函数的函数,并且将所有参数原封不动传递过去。语法如下:

function bind(fn, context){
  return function(){
    return fn.apply(context, arguments);
  };
}

btn.addEventListener('click',bind(handler.handleClick,handler))
/*只要是将某个函数指针以值的形式进行传递,同时该函数必须在特定环境中执行,被绑定函数的效
用就突显出来了。它们主要用于事件处理程序以及 setTimeout() 和 setInterval()。然而,被绑
定函数与普通函数相比有更多的开销,它们需要更多内存,同时也因为多重函数调用稍微慢一点,所
以最好只在必要时使用。*/

4、函数柯里化

与函数绑定紧密相关的主题是函数柯里化( function currying),它用于创建已经设置好了一个或多个参数的函数。函数柯里化的基本方法和函数绑定是一样的:使用一个闭包返回一个函数。两者的区别在于,当函数被调用时,返回的函数还需要设置一些传入的参数。

function curry(fn){
	console.log(arguments)
	var args = Array.prototype.slice.call(arguments, 1);
	console.log(args)
	return function(){
		console.log(arguments)
		var innerArgs = Array.prototype.slice.call(arguments);
		var finalArgs = args.concat(innerArgs);
		console.log(innerArgs)
		console.log(finalArgs)
		return fn.apply(null, finalArgs);
	};
}
			
function add(num1, num2){
	return num1 + num2;
}
var curriedAdd = curry(add, 5);
console.log(curriedAdd(3)); //8

在这里,柯里化的 add()函数两个参数都提供了,所以以后就无需再传递它们了。函数柯里化还常常作为函数绑定的一部分包含在其中,构造出更为复杂的 bind()函数。例如:

function bind(fn, context){
  var args = Array.prototype.slice.call(arguments, 2);
  return function(){
    var innerArgs = Array.prototype.slice.call(arguments);
    var finalArgs = args.concat(innerArgs);
    return fn.apply(context, finalArgs);
  };
}

5、不可扩展对象

默认情况下,所有对象都是可以扩展的。也就是说,任何时候都可以向对象中添加属性和方法。例如,可以像下面这样先定义一个对象,后来再给它添加一个属性。

var person = { name: "Nicholas" };
person.age = 29;
// 即使第一行代码已经完整定义 person 对象,但第二行代码仍然能给它添加属性。现在,使用
// Object.preventExtensions()方法可以改变这个行为,让你不能再给对象添加属性和方法。例如:
var person = { name: "Nicholas" };
Object.preventExtensions(person);
person.age = 29;
alert(person.age); //undefined

在调用了 Object.preventExtensions()方法后,就不能给 person 对象添加新属性和方法了。在非严格模式下,给对象添加新成员会导致静默失败,因此 person.age 将是 undefined。而在严格模式下,尝试给不可扩展的对象添加新成员会导致抛出错误。虽然不能给对象添加新成员,但已有的成员则丝毫不受影响。你仍然还可以修改和删除已有的成员。另外,使用 Object.istExtensible()方法还可以确定对象是否可以扩展。

var person = { name: "Nicholas" };
alert(Object.isExtensible(person)); //true
Object.preventExtensions(person);
alert(Object.isExtensible(person)); //false

6、密封的对象

ECMAScript 5 为对象定义的第二个保护级别是密封对象( sealed object)。密封对象不可扩展,而且已有成员的[[Configurable]]特性将被设置为 false。这就意味着不能删除属性和方法,因为不能使用 Object.defineProperty()把数据属性修改为访问器属性,或者相反。属性值是可以修改的。要密封对象,可以使用 Object.seal()方法。

var person = { name: "Nicholas" };
Object.seal(person);
person.age = 29;
alert(person.age); //undefined
delete person.name;
alert(person.name); //"Nicholas"
// 使用 Object.isSealed()方法可以确定对象是否被密封了。因为被密封的对象不可扩展,所以用
// Object.isExtensible()检测密封的对象也会返回 false。
alert(Object.isExtensible(person)); //true
alert(Object.isSealed(person)); //false
Object.seal(person);
alert(Object.isExtensible(person)); //false
alert(Object.isSealed(person)); //true

7、冻结对象

最严格的防篡改级别是冻结对象( frozen object)。冻结的对象既不可扩展,又是密封的,而且对象数据属性的[[Writable]]特性会被设置为 false。如果定义[[Set]]函数,访问器属性仍然是可写的。ECMAScript 5 定义的 Object.freeze()方法可以用来冻结对象。

var person = { name: "Nicholas" };
Object.freeze(person);
person.age = 29;
alert(person.age); //undefined
delete person.name;
alert(person.name); //"Nicholas"
person.name = "Greg";
alert(person.name); //"Nicholas

// 与密封和不允许扩展一样,对冻结的对象执行非法操作在非严格模式下会被忽略,而在严格模式下会抛出错误。
// 当然,也有一个 Object.isFrozen()方法用于检测冻结对象。因为冻结对象既是密封的又是不可
// 扩展的,所以用 Object.isExtensible()和 Object.isSealed()检测冻结对象将分别返回 false和 true。
var person = { name: "Nicholas" };
alert(Object.isExtensible(person)); //true
alert(Object.isSealed(person)); //false
alert(Object.isFrozen(person)); //false
Object.freeze(person);
alert(Object.isExtensible(person)); //false
alert(Object.isSealed(person)); //true
alert(Object.isFrozen(person)); //true

8、函数节流

函数节流背后的基本思想是指,某些代码不可以在没有间断的情况连续重复执行。第一次调用函数,创建一个定时器,在指定的时间间隔之后运行代码。当第二次调用该函数时,它会清除前一次的定时器并设置另一个。如果前一个定时器已经执行过了,这个操作就没有任何意义。然而,如果前一个定时器尚未执行,其实就是将其替换为一个新的定时器。目的是只有在执行函数的请求停止了一段时间之后才执行。

var processor = {
  timeoutId: null,
  //实际进行处理的方法
  performProcessing: function(){
    //实际执行的代码
  },
  //初始处理调用的方法
  process: function(){
    clearTimeout(this.timeoutId);
    var that = this;
    this.timeoutId = setTimeout(function(){
      that.performProcessing();
    }, 100);
  }
};
//尝试开始执行
processor.process();

//这个模式可以使用 throttle()函数来简化,这个函数可以自动进行定时器的设置和清除,如下例
//所示:
function throttle(method, context) {
  clearTimeout(method.tId);
  method.tId= setTimeout(function(){
    method.call(context);
  }, 100);
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值