@demonly
2017-01-13T07:07:06.000000Z
字数 3215
阅读 1057
JavaScript
原生的类型检测并非完全可靠,所以需要更可靠地类型检测。在 Object 上调用 toString()方法会返回一个[Object NativeConstructorName]格式的字符串。这个字符串来源于每个类内部的[[Class]]属性。
alert(Object.prototype.toString.call(value)); //"[Object Array]"
调用构造函数时如果省去了 new 操作符的话,相应的属性会被加在 window 对象上。因此需要创建作用域安全的构造函数,创建的方法就是在构造函数的开头进行一个判断。
function Polygon(){
if (this instanceof Polygon){
//设置属性
}
}
但是在使用作用域安全的情况下就不能够再对该构造函数进行借用构造函数。
function Rectangle (){
Polygon.call(this); //this 的原型链中没有 Polygon,因此判断不会通过
}
这时就需要结合原型链或者寄生组合来解决。
function Rectangle (){
Polygon.call(this);
}
Rectangle.prototype = new Polygon(); //将 Polygon 加入到了 Rectangle 的原型链中,判断可以通过
在某些时候需要对浏览器的行为进行检测,如果同一个函数被多次执行就会进行多次检测。
通常行为检测的思路是在函数中进行判断然后执行相应的分支。惰性载入函数的思路是首先进行判断然后根据相应的分支来定义函数。之后就只需要直接调用定义的函数
函数绑定要创建一个函数,可以再特定的 this 环境中以特定参数调用另一函数。该技巧常常与毁掉函数与事件处理程序一起使用,一边在将函数作为变量传递的同时保留代码执行环境。
function bind(fn, context) {
return function() {
return fn.apply(context, arguments);
}
}
jQuery 中封装了 bind 方法,ES5中也定义了 bind()方法。
函数柯里化用于创建已经设置好一个或多个参数的函数。下面是函数柯里化的通用方式。
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(num1, num2){
return num1 + num2;
}
var curriedAdd = curry(add, 5);
alert(curriedAdd(3));
调用 Object.preventExtensions()方法可以让对象成为不可扩展对象,调用这个方法后的对象不可以添加新的属性和方法,但是已有成员仍然可以修改和删除。使用 Object.isExtensible()方法可以确定一个对象是否可以扩展。
Object.preventExtensions(person);
alert(Object.isExtensible(person)); //false
密封对象不可扩展,而且已有成员的的[[Configurable]]特性将被设置为 false,因此不能够删除属性和方法。
要密封对象可以使用 Object.seal()方法。使用 Object.isSealed()方法可以确定对象是否被密封了,用 Object.isExtensible()检测密封对象同样会返回 false。
冻结对象不可扩展,而且也被密封了,同时对象数据属性的[[Writable]]会被设置为 false,因此冻结对象的属性和方法不能扩展、删除和修改。
用 Object.freeze()方法可以冻结对象,用 Object.isFrozen()可以检测对象是否被冻结了。
当脚本代码运行超过了特定的时间或者特定的语句数量就不会继续执行,此时会弹出一个浏览器错误的对话框,询问允许继续执行还是停止执行。脚本长时间运行通常有两个原因,过长、过深函数嵌套的函数调用或者是进行大量处理的循环。
for (var i=0, len=data.length; i < len; i++) {
process(data[i]);
}
当出现大量处理的循环时,如果这一过程不需要同步完成也不需要按照顺序完成,那么可以使用定时器来分割这个循环。这种技术称为数组分块,就是将要处理的项目创建一个队列,每次去除一小块来进行处理,然后使用定时器取出下一块进行处理。
setTimeout(function(){
var item = array.shift(); //获取要处理的项目
process(item); //进行处理
if(array.length > 0) {
setTimeout(arguments.callee, 100);
}
})
可以使用同样的思路将以上的代码封装为一个函数。
JavaScript 中的事件采用的是一种观察者的模式,观察者模式有两类对象组成,主体和观察者。主体负责发布事件,观察者通过订阅这些事件来观察该主体。通过这一思路可以创建一个自定义的事件。
function EventTarget(){
this.handlers = [];
} //创建对象
EventTarget.prototype = {
constructor: EventTarget,
//由于 constructor 被覆写而手动写入 constructor
addHandler:function(type, handler){
if (typeof this.handlers[type] == "undefined"){
this.handlers[type] = [];
}
//如果不存在相应的事件类型,则创建一个新的事件类型
this.handlers[type].push(handler); //将事件处理程序加入数组
},
fire: function(event){
if (!event.target){
event.target = this;
}
if (this.handlers[event.type] instanceof Array){
var handlers = this.handlers[event.type];
//如果存在相应事件类型的数组,则用该数组中的事件处理程序来进行处理
for(var i=0, len=handlers.length; i < len; i++){
handlers[i](event);
}
//用数组中所有事件处理程序来分别对事件对象进行处理
}
},
removeHandlers: function(type, handler){
if (this.handlers[type] instanceof Array){
var handlers = this.handlers[type];
for (var i=0, len=handlers.length; i < len; i++){
if (handlers[i] === handler){
break;
}
//通过遍历来寻找相应的事件处理程序
}
handler.splice(i, 1);
//将相应事件处理程序从数组中删除
}
}
}
调用 fire()方法即可触发事件,其中传入的 event 也是自定义的事件对象,这个对象需要至少包含一个 type 属性。由于以上功能都封装在一个类型中,因此其他对象只要继承这个类就可以获得以上功能。