因为全部的函数都是对象,因此使用函数的指针很是简单javascript
场景:假设一个页面有多个 iframejava
array 是 window 的属性数组
/* value是一个数组的状况下,且必须与Array在同一个全局做用域下才会返回true 若是value是在另外一个ifream下定义的数组,那么返回false */
var isArray = value instanceof Array;
复制代码
任何值上面调用 object 的 toString()方法都会返回一个[object NativeConstructorName]格式的字符串,每一个类在内部都有一个[[class]]
属性,指定了上述字符串中构造函数名浏览器
alert(Object.prototype.toString.call(value)); // "[object Array]"
//原生数组的构造函数与全局做用域无关,可使用toString()保证返回一致的值
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]";
}
复制代码
这一技巧也被被普遍的应用于检测原生 JSON 对象,Object 的 toString 方法不能检测非原生构造函数的构造函数名,所以开发人员定义的任何的构造函数都将返回 [obje0ct object]安全
var isNativeJSON=window.JSON && Object.prototype.toString.call(JSON)==“[Onject JSON]”
复制代码
当没有使用 new 操做符来建立的时候,this 对象是在运行时候绑定的,直接调用的话,this 会映射到 window 全局对象上构造函数当作普通函数去调用,这个问题是 this 晚绑定产生的,这里的 this 解析成了 window 对象bash
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
}
var p = Person("cc", 18, "coder");
console.log(window.name);
console.log(window.age);
console.log(window.job);
复制代码
如下方式不管 Person 是否使用 new 操做符调用,都会返回一个新的实例对象,这就避免了在全局对象上意外的设置属性闭包
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);
}
}
复制代码
使用这个模式能够锁定构造函数的做用域。若是使用了构造函数窃取模式的继承且不使用原型链,那么这个继承有可能会被破坏app
//多边形类 ,此构造函数的做用域是安全的
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并不是Polygon的实例
this.width = width;
this.height = height;
this.getArea = function() {
return this.width * this.height;
};
}
var rect = new Rectangle(5, 10);
console.log(rect.sides); //undefined
console.log(rect.getArea()); //50
console.log(rect); //Rectangle {width: 5, height: 10, getArea: ƒ}
复制代码
//多边形类
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);
//原型链,rect是Rectangle实例,也是Polygon的实例,call执行,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);
console.log(rect.sides); //undefined
console.log(rect.getArea()); //50
console.log(rect);
复制代码
产生背景:大多数浏览器之间的行为差别,致使多数 js 代码包含了大量的 if 语句,将执行引导到正确的代码中,因此若是 if 没必要每次执行,那么代码能够运行的更快一些。ide
function createXHR() {
if (typeof XMLHttpRequest != "undefined") {
//...
return new XMLHttpRequest();
} else if (typeof ActiveXObject != "undefined") {
//...
return new ActiveXObject(arguments.callee.activeXString);
} else {
throw new Error("error message");
}
}
复制代码
解决方案就称为惰性载入的技巧: 表示函数执行的分支只会发生一次。函数
这两种方式都能避免执行没必要要的代码,惰性载入函数的优势只执行一次 if 分支,避免了函数每次执行时候都要执行 if 分支和没必要要的代码,所以提高了代码性能,至于那种方式更合适,就要看您的需求而定了。
function createXHR() {
if (typeof XMLHttpRequest != "undefined") {
createXHR = function() {
//...
return new XMLHttpRequest();
};
} else if (typeof ActiveXObject != "undefined") {
createXHR = function() {
//...
return new ActiveXObject(arguments.callee.activeXString);
};
} else {
createXHR = function() {
throw new Error("error message");
};
}
return createXHR();
}
复制代码
var createXHR = (function() {
if (typeof XMLHttpRequest != "undefined") {
return function() {
//...
return new XMLHttpRequest();
};
} else if (typeof ActiveXObject != "undefined") {
return function() {
//...
return new ActiveXObject(arguments.callee.activeXString);
};
} else {
throw new Error("error message");
}
})();
复制代码
该技巧经常和回调函数和事件处理程序一块儿使用,以便将函数做为变量传递的同时保留代码执行环境
//事件兼容封装
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;
}
},
//获取事件对象
getEvent: function(event) {
return event ? event : window.event;
},
//获取目标元素
getTarget: function(event) {
return event.target || event.srcElement;
},
//阻止事件默认行为
preventDefault: function(event) {
if (event.preventDefault) {
event.preventDefault();
} else {
event.returnValue = false;
}
},
//解除监听
removeHandler: function(element, type, handler) {
if (element.removeEventListener) {
element.removeEventListener(type, handler, false);
} else if (element.detachEvent) {
element.detachEvent("on" + type, handler);
} else {
element["on" + type] = null;
}
},
//阻止冒泡
stopPropagation: function(event) {
if (event.stopPropagation) {
event.stopPropagation;
} else {
event.cancelBubble = true;
}
}
};
复制代码
如下时间处理程序,在点击 button 后会弹出 undefined,这个问题在于没有保存 handler.handleClcik()的环境,因此 this 指向了 DOM 按钮而非 handler,咱们能够采用闭包来修正这个问题
var handler = {
message: "event handler",
handleClick: function(event) {
alert(this.message);
}
};
var btn = document.getElementById("btn");
EventUtil.addHandler(btn, "click", handler.handleClick);
//闭包修正
EventUtil.addHandler(btn, "click", function(event) {
handler.handleClick();
});
复制代码
可是大多数使用可能致使代码的难以调试和理解。因此下面咱们使用 bind 解决,大多数的 js 库实现了一个能够将函数绑定到指定环境的函数,通常叫作 bind()
// 自定义bind函数接受一个函数和一个环境
function bind(fn, context) {
return function() {
return fn.apply(context, arguments);
};
}
var handler = {
message: "event handler",
handleClick: function(event) {
alert(this.message);
}
};
var btn = document.getElementById("btn");
EventUtil.addHandler(btn, "click", bind(handler.handleClick, handler));
复制代码
ES5 为全部函数提供了一个原生 bind 方法进一步简化了操做,可是被绑定函数比普通函数相比有更多的开销,须要更多的内存
var handler = {
message: "event handler",
handleClick: function(event) {
alert(this.message);
}
};
var btn = document.getElementById("btn");
EventUtil.addHandler(btn, "click", handler.handleClick.bind(handler));
复制代码
用于建立已设置好了一个或多个参数的函数,函数柯里化的基本方法和函数绑定是同样的:使用一个闭包返回一个函数
如下 curriedAdd 函数虽然并非柯里化的函数,可是很好的展示了其概念
function add(x, y) {
return x + y;
}
function curriedAdd(y) {
return add(5, y);
}
console.log(add(1, 2)); //3
console.log(curriedAdd(5)); //10
复制代码
柯里化是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,而且返回接受余下的参数并且返回结果的新函数的技术。
咱们改造下,实际上就是把 add 函数的 x,y 两个参数变成了先用一个函数接收 x 而后返回一个函数去处理 y 参数。如今思路应该就比较清晰了,就是只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。
// 普通的add函数
function add(x, y) {
return x + y;
}
// Currying后
function curriedAdd(x) {
return function(y) {
return x + y;
};
}
add(1, 2); // 3
curriedAdd(1)(2); // 3
复制代码
柯里化函数一般的动态建立方式
/* arguments是一个关键字,表明当前参数,在javascript中虽然arguments表面上以数组形式来表示,但实际上没有原生数组slice的功能,这里使用call方法算是对arguments对象不完整数组功能的修正。 slice返回一个数组,该方法只有一个参数的状况下表示除去数组内的第一个元素。就本上下文而言,原数组的第一个参数是“事件名称”,具体像“click”,"render"般的字符串,其后的元素才是处理函数所接纳的参数列表。 */
function curry(fn) {
var args = Array.prototype.slice.call(arguments, 1);
return function() {
var innerArgs = Array.prototype.slice.call(arguments);
var finalArgs = args.concat(innerArgs);
return fn.apply(null, finalArgs);
};
}
function add(x, y) {
return x + y;
}
var curryAdd = curry(add, 5);
console.log(curryAdd(1)); //6
var curryAdd2 = curry(add, 1, 5); //两个参数都提供了,就无需在传递了
console.log(curryAdd2()); //6
// ------支持多参数传递---------
function progressCurrying(fn, args) {
var _this = this;
var len = fn.length;
var args = args || [];
return function() {
var _args = Array.prototype.slice.call(arguments);
Array.prototype.push.apply(args, _args);
// 若是参数个数小于最初的fn.length,则递归调用,继续收集参数
if (_args.length < len) {
return progressCurrying.call(_this, fn, _args);
}
// 参数收集完毕,则执行fn
return fn.apply(this, _args);
};
}
复制代码
用于函数绑定的一部分构造更复杂的 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);
};
}
复制代码
柯里化好处:
//将第一个参数reg进行复用,这样别的地方就可以直接调用hasNumber,hasLetter等函数,让参数可以复用,调用起来也更方便
// 正常正则验证字符串 reg.test(txt)
// 函数封装后
function check(reg, txt) {
return reg.test(txt);
}
check(/\d+/g, "test"); //false
check(/[a-z]+/g, "test"); //true
// Currying后
function curryingCheck(reg) {
return function(txt) {
return reg.test(txt);
};
}
var hasNumber = curryingCheck(/\d+/g);
var hasLetter = curryingCheck(/[a-z]+/g);
hasNumber("test1"); // true
hasNumber("testtest"); // false
hasLetter("21212"); // false
复制代码
缺点:
存取arguments对象一般要比存取命名参数要慢一点
一些老版本的浏览器在arguments.length的实现上是至关慢的
使用fn.apply( … ) 和 fn.call( … )一般比直接调用fn( … ) 稍微慢点
建立大量嵌套做用域和闭包函数会带来花销,不管是在内存仍是速度上
复制代码
开发人员可能意外修改别人的代码,因此 ES5 提供了防篡改对象定义, 一旦把对象定义为防篡改。就没法撤销了
var person = { name: "cc" };
object.preventExtensions(person);
person.name = "bb";
console.log(person.name);
复制代码
未完持续中......