JavaScript基础——高级技巧

时间:2021-10-13 14:57:07

JavaScript中的函数非常强大,因为它们是第一类对象。使用闭包和函数环境切换,还可以有很多使用函数的强大方法。可以创建作用域安全的构造函数,确保在缺少new操作符时调用构造函数不会改变错误的环境对象。

  • 可以使用惰性载入函数,将任何代码分支推迟到第一次调用函数的时候。
  • 函数绑定可以让你创建始终在指定环境中运行的函数,同时函数柯里化可以让你创建已经填了某些参数的函数。
  • 将绑定和柯里化组合起来,就能够给你在任意环境中以任意参数执行任意函数的方法。
ECMAScript 5允许通过以下几种方式来创建防篡改对象。
  • 不可扩展的对象,不允许给对象添加新的属性或方法。
  • 密封的对象,也是不可扩展的对象,不允许删除已有的属性和方法。
  • 冻结的对象,也是密封的对象,不允许重写对象的成员。
JavaScript中可以使用setTimeout()和setInterval()如下创建定时器:
  • 定时器代码是放在一个等待区域,直到时间间隔到了之后,此时将代码添加到JavaScript的处理队列中,等待下一次JavaScript进程空闲时被执行。
  • 每次一段代码执行结束之后,都会有一小段空闲时间进行其他浏览器处理。
  • 这种行为意味着,可以使用定时器将长时间运行的脚本却分为一小块一小块可以在以后运行的代码段。这种做大有助于web应用对用户交互有更积极的响应。
JavaScript中经常以事件的形式应用观察者模式。虽然事件常常和DOM一起使用,但是你也可以通过实现自定义事件在自己的代码中应用。使用自定义事件有助于将不同部分的代码相互之间解耦,让维护更加容易,并减少引入错误的机会。
拖放对于桌面和web应用都是一个非常流行的用户界面范例,它能够让用户非常方便地以一种直观的方式重新排列或者配置东西。在JavaScript中可以使用鼠标事件和一些简单的计算来实现这种功能类型。将拖放行为和自定义事件结合起来可以创建一个可重复使用的框架,它能应用在各种不同的情况下。实际上,在HTML5规范中已经定义了拖放的方法。
<!DOCTYPE html>
<html lang="zh_CN" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>高级技巧</title>
<style>
#myDiv{
width: 100px;
height: 100px;
background-color: #0040D0;
}
</style>
</head>
<button type="button" id="my-btn">按钮</button>
<button type="button" id="my-btn1">按钮1</button>
<button type="button" id="my-btn2">按钮2</button>
<button type="button" id="my-btn3">按钮3</button>
<button type="button" id="my-btn4">按钮4</button>
<button type="button" id="my-btn5">按钮5</button>
<div id="myDiv"></div>
<div id="myDiv2"></div>

<body>
<script src="l22.js"></script>
</body>
</html>

/**
* 高级技巧
*/
function cl(x){
console.log(x);
}
//跨浏览器的事件处理程序
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;
}
},
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;
}
}
};
/**
* 22.1 高级函数
*/
//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]";
}
//检测是否为原生JSON对象
function isNativeJSON(value){
return window.JSON && Object.prototype.toString.call(value)=="[object JSON]";
}

//22.1.2 作用域安全的构造函数
//作用域安全的构造函数在进行任何更改前,首先确认this对象是正确类型的实例。
//如果不是,那么会创建新的实例并返回。
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("Jason",24,"Software Engineer");
cl(window.name);//=>""
cl(person1.name);//=>"Jason"

//22.1.3 惰性载入函数
//惰性载入表示函数执行的分支仅会发生一次,有两种实现方式
//第一种就是在函数被调用时再处理函数;
//第二种是在声明函数时就指定适当的函数
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"];
var i,len;
for(i=0,len=versions.length;i<len;i++){
try{
new ActiveXObject(versions[i]);
arguments.callee.activeXString=versions[i];
break;
}catch(ex){
//处理错误
console.log(ex);
}
}
}
return new ActiveXObject(arguments.callee.activeXString);
};
}else{
return function(){
throw new Error("No XHR object available");
}
}
})();

//22.1.4 函数绑定
//函数绑定要创建一个函数,可以在特定的this环境中以指定参数调用另一个函数。
var handler={
message:"Event handled",
handleClick:function(event){
alert(this.message+":"+event.type);
}
}
var btn=document.getElementById("my-btn");
EventUtil.addHandler(btn,"click", function(event){
handler.handleClick(event);
});
//很多JavaScript库实现了一个可以将函数绑定到指定环境的函数。
//这个函数一般都叫bind()。
function bind(fn,context){
return function(){
return fn.apply(context,arguments);
}
}
var btn1=document.getElementById("my-btn1");
EventUtil.addHandler(btn1,"click",bind(handler.handleClick,handler));
//ECMAScript5为所有函数定义了一个原生的bind()方法。
var btn2=document.getElementById("my-btn2");
EventUtil.addHandler(btn2,"click",handler.handleClick.bind(handler));

//22.1.5 函数柯里化
//用于创建已经设置好了一个或多个参数的函数。
function add(num1,num2){
return num1+num2;
}
function curriedAdd(num2){
return add(5,num2);
}
cl(add(2,3));//=>5
cl(curriedAdd(3));//=>8
//创建柯里化函数的通用方式
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 add1(num1,num2){
return num1+num2;
}
var curriedAdd=curry(add1,5);
cl(curriedAdd(3));//=>8
var curriedAdd=curry(add1,5,12);
cl(curriedAdd());//=>17
//函数柯里化还常常作为函数绑定的一部分包含在其中,构造出更为复杂的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);
};
}
var handler={
message:"Event handled",
handleClick:function(name,event){
alert(this.message+":"+name+":"+event.type);
}
}
var btn3=document.getElementById("my-btn3");
EventUtil.addHandler(btn3,"click",bind(handler.handleClick,handler,"my-btn3"));
//ECMAScript5的bind()方法也实现函数柯里化。
var btn4=document.getElementById("my-btn4");
EventUtil.addHandler(btn4,"click",handler.handleClick.bind(handler,"my-btn4"));


/**
* 22.2 防篡改对象
*/
//22.2.1 不可扩展对象
var person1={name:"Jason"};
//阻止给对象添加属性和方法
Object.preventExtensions(person1);
person1.age=29;
cl(person1.age);//=>undefined
//确定对象是否可以扩展
cl(Object.isExtensible(person1));//=>false

//22.2.2 密封的对象
//Object.seal()方法,用来密封对象,即不能添加属性,也不能删除既有属性,不过可以修改属性值
var person2={name:"Jason"};
Object.seal(person2);
person2.age=29;
cl(person2.age);//=>undefined
delete person2.name;
cl(person2.name);//=>"Jason"
//检测对象是否被密封
cl(person2.isSealed(person2));//=>true

//22.2.3 冻结的对象
//冻结的对象既不可扩展,又是密封的,而且对数据属性的可写[[writable]]特性会被设置为false
var person3={name:"Jason"};
Object.freeze(person3);
person3.age=29;
cl(person3.age);//=>undefined
delete person.name;
cl(person3.name);//=>"Jason"
person3.name="Greg";
cl(person3.name);//=>"Jason"
//检测对象是否被冻结
cl(Object.isFrozen(person3));

/**
* 22.3 高级定时器
*/
//22.3.1 重复的计时器
setTimeout(function(){
var div=document.getElementById("myDiv");
var left=parseInt(div.style.left)+5;
div.style.left=left+"px";
if(left<200){
setTimeout(arguments.callee,50);
}
},50);

//22.3.2 Yielding Processes
//数组分块的技术 定义一个chunk()方法
//接受三个参数:要处理的项目的数组,用于处理项目的函数,以及可选的运行该函数的环境
function chunk(array,process,context){
setTimeout(function(){
var item=array.shift();
process.call(context,item);
if(array.length>0){
setTimeout(arguments.callee,100);
}
},100);
}
var data=[12,123,1234,453,23,23,5,4123,45,346,5643,2234,345,342];
function printValue(item){
var div2=document.getElementById("myDiv2");
div2.innerHTML+=item+"<br>";
}
chunk(data.concat(),printValue);

//22.3.3 函数节流
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);
}
function resizeDiv(){
var div2=document.getElementById("myDiv2");
div2.style.height=div2.offsetHeight+"px";
}
window.onresize=function(){
throttle(resizeDiv());
}

/**
*22.4 自定义事件
*/
//观察者模式:主体和观察者
//自定义事件:创建一个管理事件的对象,让其他对象监听那些事件。
function EventTarget(){
//用于储存事件处理程序
this.handlers={};
}
EventTarget.prototype={
constructor:EventTarget,
//用于注册给定类型事件的事件处理程序
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);
}
}
},
//用于注销某个事件类型的事件处理程序
removeHandler:function(type,handler){
if(this.handlers[type] instanceof Array){
var handlers=this.handlers[event.type];
for(var i= 0,len=handlers.length;i<length;i++){
if(handlers[i] ===handler){
break;
}
}
handlers.splice(i,1);
}
}
};
//使用EventTarget类型的自定义事件
function handleMessage(event){
alert("收到信息:"+event.message);
}
//创建一个对象
var target=new EventTarget();
//添加一个事件处理程序
target.addHandler("message",handleMessage);
//触发事件
target.fire({type:"message",message:"Hello world!"});
//删除事件处理程序
target.removeHandler("message",handleMessage);
//再次,应没有处理程序
target.fire({type:"message",message:"Hello world!"});
function Person(name,age){
EventTarget.call(this);
this.name=name;
this.age=age;
}
//寄生组合式继承
function inheritPrototype(subType,superType){
var prototype=Object.create(superType.prototype); //创建对象
prototype.constructor=subType; //增强对象
subType.prototype=prototype; //指定对象
}
inheritPrototype(Person,EventTarget);
Person.prototype.say=function(message){
this.fire({type:"message",message:message});
}
function handleMessage(event){
alert(event.target.name+" says:"+event.message);
}
//创建新person
var person=new Person("Jason",29);
//添加一个事件处理程序
person.addHandler("message",handleMessage);
//在该对象调用1个方法,它触发消息事件
person.say("hi there");

/**
* 22.5 拖放
*/
//22.5.1 修缮拖放功能
//22.5.2 添加自定义事件