《javascript高级程序设计》第22章 高级技巧

时间:2024-01-06 16:24:26

22.1 高级函数
  22.1.1 安全的类型检测
  22.1.2 作用域安全的构造函数
  22.1.3 惰性载入函数
  22.1.4 函数绑定
  22.1.5 函数柯里化
22.2 防篡改对象
  22.2.1 不可扩展对象
  22.2.2 密封的对象
  22.2.3 冻结的对象
22.3 高级定时器
  22.3.1 重复的定时器
  22.3.2 Yielding Processes
  22.3.3 函数节流
22.4 自定义事件
22.5 拖放
  22.5.1 修缮拖动功能
  22.5.2 添加自定义事件

22.1.1 安全的类型检测

function isArray(value){
return Object.prototype.toString.call(value) == "[object Array]";
}
function isFunction(value){
return Object.prototype.toString.call(value) == "[object Function]";
}
function isRegExp(value){
return Object.prototype.toString.call(value) == "[object RegExp]";
}

22.1.2 作用域安全的构造函数
在这里this 被解析成了window对象。由于window 的name 属性是用于识别链接目标和frame 的

function Person(name, age, job){
if (this instanceof Person){
this.name = name;
this.age = age;
this.job = job;
} else {
return new Person(name, age, job);
}
}
var person1 = Person("Nicholas", 29, "Software Engineer");
alert(window.name); //""
alert(person1.name); //"Nicholas"
var person2 = new Person("Shelby", 34, "Ergonomist");
alert(person2.name); //"Shelby"

如果你使用构造函数窃取模式的继承且不使用原型链,那么这个继承很可能被破坏。
如果构造函数窃取结合使用原型链或者寄生组合则可以解决这个问题。考虑以下例子:

function Polygon(sides){
if (this instanceof Polygon) { //如果把判断去掉,不用加粗的那段就可以找到side
this.sides = sides;
this.getArea = function(){
return 0;
};
} else {
return new Polygon(sides);
}
} function Rectangle(width, height){
Polygon.call(this, 2);
//this 对象并非Polygon 的实例,所以会创建并返回一个新的Polygon 对象。
//Rectangle 构造函数中的this 对象并没有得到增长,同时Polygon.call()返回的值也没有用到,
//所以Rectangle 实例中就不会有sides 属性。
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); //

22.1.3 惰性载入函数(有两种方式,我更喜欢这一种)

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();
}

22.1.4 函数绑定

var EventUtil = {
addHandler: function(element, type, handler){
if (element.addEventListener){
element.addEventListener(type, handler, false);
} else if (element.attachEvent){
element.attachEvent("on" + type, handler);
} else {
element["on" + type] = handler;
}
}
} var handler = {
message: "Event handled",
handleClick: function(event){
alert(this.message);
}
};
var btn = document.getElementById("my-btn");
EventUtil.addHandler(btn, "click", handler.handleClick);

alert出来的到底是什么呢?
这个问题在于没有保存
handler.handleClick()的环境,所以this 对象最后是指向了DOM按钮而非handler
bind:
ECMAScript 5 为所有函数定义了一个原生的bind()方法,IE9+、Firefox 4+和Chrome。

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

自定义:

function bind(fn, context){
return function(){
return fn.apply(context, arguments);
};
} var handler = {
message: "Event handled",
handleClick: function(event){
alert(this.message + ":" + event.type);
}
};
var btn = document.getElementById("my-btn");
EventUtil.addHandler(btn, "click", bind(handler.handleClick, handler));