给类型增加方法
Function.prototype.method=function(name,func){ //if(!this.prototype[name]){ // this.prototype[name]=func; //return this; //} if(typeof this.prototype[name]!=="function"){ this.prototype[name]=func; return this; } }; //提取整数 Number.method("integer",function(){ return Math[(this>0)?"floor":"ceil"](this); }); console.log((-3.3333).integer()); //去掉前后空格 String.method("trim",function(){ return this.replace(/^s+|s+$/,""); }); console.log((" 1214 ").trim());
返回值
一个函数总是返回一个值,如果没有指定返回值,则返回undefined;如果函数以在前面加上new前缀的方式来调用,且返回值不是一个对象,则返回this(改新对象)
假值
null false undefined "" 0 NaN
//创建一个用原对象作为原型的对象
if(typeof Object.beget!=="function"){
Object.beget=function(o){
var F=function(){};
F.prototype=o;
return new F();
};
}
this的值取决于调用模式
1、方法调用模式
当一个函数被保存为对象的一个属性时,我们称之为一个方法。当一个方法被调用时,this被绑定到改对象。如果一个调用表达式包含一个属性存取表达式时,那么它被当作一个方法来调用
2、函数调用模式
当一个函数并非一个对象的属性是,那么它被当作一个韩式来调用,当函数以此模式调用时,this被绑定到全局对象
如果内部函数想访问外部函数的this可以这样做
myObject.double=function(){
var that=this;
var helper=function(){
that.value=add(that.vaue,that.value);
}
helper();
}
3、构造器调用模式
如果在一个函数前面带上new 来调用,那么将创建一个隐藏连接到该函数的prototy成员的新对象,同时this将会被绑定到那个新对象上
4、apply调用模式
函数可以拥有方法;apply方法让我们构建一个参数数组并用其去调用函数。它也允许我们选择this的值,方法接收两个参数,第一个是被绑定给this的值,第二个就是一个参数数组
函数作用域
函内声明的所有变量在函数体内始终是可见的,这意味着变量在声明之前就可用,称为声明提前,函数里声明的所有变量(但不涉及赋值)都被提前至函数的顶部,全局变量在程序中始终是有定义的,而局部变量在它的函数体内以及所嵌套的函数内始终是有定义。
闭包
作用域的好处是内部函数可以访问定义它们的外部函数的参数和变量(除了this和arguments)
var fade=function(node){ console.log("4234"); var level=1; var step=function(){ var hex=level.toString(16); console.log(hex); node.style.backgroundColor="#ffff"+hex+hex; if(level<15){ level++; setTimeout(step,100); } }; setTimeout(step,100); }; var d=document.getElementById("bg"); fade(d);
var myObject=(function(){ var value=0; return { increment:function(inc){ value+=typeof inc ==="number"?inc:1; }, getValue:function(){ return value; } }; }()); myObject.increment(2); console.log(myObject.getValue());//2 var quo=function(status){ return { get_status:function(){ return status; } }; }; var myQuo=quo("amazed"); console.log(myQuo.get_status());//
amazed
我们可以使用函数和闭包来构造模块。模块是一个提供接口却隐藏状态和实现的函数或者对象。通过使用函数去产生模块。我们几乎可以摒弃全局变量的使用。
Function.prototype.method=function(name,func){ //if(!this.prototype[name]){ // this.prototype[name]=func; //return this; //} if(typeof this.prototype[name]!=="function"){ this.prototype[name]=func; return this; } }; String.method('deentityify',function(){ var entity={ quot:'"', lt:'<', gt:'>' }; return function(){ return this.replace(/&([^&;]+);/g,function(a,b){ console.log(a); console.log(b); var r=entity[b]; return typeof r==='string'?r:a; }); }; }()); console.log('<">'.deentityify());//<">
模块形式的一部形式是:一个定义了私有变量和函数的函数;利用闭包创建可以访问私有变量和函数的特权函数;最后返回这个特权函数,或者把它们保存到一个可访问的地方。
模块模式可以用来产生安全的对象。
var serial_maker=function(){ var prtfix=''; var seq=0; return { set_prefix:function(p){ prefix=String(p); }, set_seq:function(s){ seq=s; }, getsym:function(){ var result=prefix+seq; seq+=1; return result; } }; }; var seqer=serial_maker(); seqer.set_prefix('q'); seqer.set_seq(1000); var unique=seqer.getsym(); console.log(unique);//q1000
seqer包含的方法都没有用到this或者that。因此没有办法损害seqer。除非调用对应的方法,否则没法改变prefix或seq的值。seqer对象是可变的,所以它的方法可能会被替换掉,但替换后的方法依然不能访问私有成员。色情而就是一组函数的集合,而且那些函数被授予特权,用户使用或修改私有状态的能力
如果我们把seqer.gensym作为一只传递给第三方函数,那个函数能用它产生唯一字符串,但却不能通过它来改变prefix或seq的值
套用
Function.prototype.method=function(name,func){ //if(!this.prototype[name]){ // this.prototype[name]=func; //return this; //} if(typeof this.prototype[name]!=="function"){ this.prototype[name]=func; return this; } }; Function.method('curry',function(){ var slice=Array.prototype.slice; var args=slice.apply(arguments); var that=this; return function(){ return that.apply(null,args.concat(slice.apply(arguments))); }; }); function add(x,y){ return x+y; } var add1=add.curry(1); console.log(add1(6));//7
记忆
var fibonacci=function(){ var memo=[0,1]; var fib=function(n){ var result=memo[n]; if(typeof result!=='number'){ result=fib(n-1)+fib(n-2); memo[n]=result; } return result; }; return fib; }(); console.log(fibonacci(10));//55
memoizer函数将取得一初始的memo数组和fundamental函数。它返回一个管理memo存储和在需要时调用fundamental函数的shell函数。我们传递这个shell函数和该函数的参数给fundamental函数
var memoizer=function(memo,fundamental){ var shell=function(n){ var result=memo[n]; if(typeof result!=="number"){ result=fundamental(shell,n); memo[n]=result; } return result; }; return shell; }; var fibonacci=memoizer([0,1],function(shell,n){ return shell(n-1)+shell(n-2); }); console.log(fibonacci(10));//55 var prive=memoizer([1,1],function(shell,n){ return n*shell(n-1); }); console.log(prive(4));//24
级联
有一些方法没有返回值。例如,一些设置或者修改的某个状态却不返回任何的方法就是典型的例子。如果我们让这些方法返回this而不是undefined,就可以启用级联。
数组
length属性的值是这个数组的最大整数属性名加上1.它不一定等于数组里的属性的个数:
var myArray=[];
myArray.length//0
myArrayp[100]=true
myArray.length //101
[]后缀下标运算符将它的表达式转换为一个字符串,如果该表达式有toString方法,就使用该方法的值。这个字符串将被用做属性名。如果这个字符串看起来像一个大于等于这个数组当前的length且小于4294967295的正整数,那么这个数组的length就会重新设置为新的下标加1
Function.prototype.method=function(name,func){ //if(!this.prototype[name]){ // this.prototype[name]=func; //return this; //} if(typeof this.prototype[name]!=="function"){ this.prototype[name]=func; return this; } }; Array.method('reduce',function(f,value){ var i; for(i=0;f<this.length;i++){ value=f(this[i],value); } return value; }); var add=function(a,b){ return a+b; }; var mult=function(a,b){ return a*b; }; var data=[4,8,15,16,23,42]; console.log(data.reduce(add,0));//108 console.log(data.reduce(mult,1));7418880
Function.prototype.method=function(name,func){ //if(!this.prototype[name]){ // this.prototype[name]=func; //return this; //} if(typeof this.prototype[name]!=="function"){ this.prototype[name]=func; return this; } }; Array.method('pop',function(){ return this.splice(this.length-1,1)[0]; }); var a=['a','b','c']; Array.method('push',function(){ this.splice.apply(this,[this.length,0].concat(Array.prototype.slice.call(arguments,0))); return this.length; }); console.log(a.push('e')); console.log(a);
var n=[4,8,15,16,23,42]; console.log(n.sort()); console.log(n.sort(function(a,b){ return a-b; })); var m=['aa','bb','a',4,8,15,16,23,421]; console.log(m.sort(function(a,b){ if(a===b) return 0; if(typeof a===typeof b) return a<b?-1:1; return typeof a<typeof b?-1:1; })); var by=function(name){ return function(o,p){ var a,b; if(typeof o==='object'&&typeof p==='object' &&o&&p){ a=o[name]; b=p[name]; if(a===b){ return 0; } if(typeof a===typeof b){ return a<b?-1:1; } return typeof a<typeof b?-1:1; } else{ throw{ name:'error', message:'error'+name }; } }; }; var s=[{first:'Joe',last:'Besser'},{first:'Moe',last:'Howard'},{first:'Joe',last:'DeRita'},{first:'Shemp',last:'Howard'},{first:'Larry',last:'Fine'},{first:'Curly',last:'Howard'}]; console.log(s.sort(by('first'))); console.log(s); var by2=function(name,minor){ return function(o,p){ var a,b; if(o&&p&&typeof o==='object'&&typeof p==='object') { a=o[name]; b=o[name]; if(a===b){ return typeof minor==='function'?minor(o,p):0; } if(typeof a===typeof b){ return a<b?-1:1; } return typeof a<typeof b?-1:1; } else{ throw{ name:'error', message:'errot'+name }; } }; }; console.log(s.sort(by2('last',by2('first'))));
parseInt是一个将字符串转换为整数的函数。它在遇到非数字时停止解析,所以下面同一个结果
console.log(parseInt('16'));//16
console.log(parseInt('16 tom'));//16
如果字符串第一个字符是0,那么该字符串将被基于八进制而不是十进制来求值。在八进制中,8和9不是数字所以下面的结果为0
console.log(parseInt('08',8));//0
console.log(parseInt('09'));//9
这个错误导致了程序解析日期和时间时会出现问题,幸运的是,parseInt可以接受一个基数作为参数。如此一来parseInt('08',10)结果为8,建议总是提供这个基数参数。
二进制的浮点数不能正确的处理十进制的小数,幸运的是,浮点数中的整数运算是精确的。所以小数表现出来的错误可以通过指定精度来避免
NaN
可能在试图将非数字形式的字符串转换为数字是产生
console.log(+'0');//0
console.log(+'oops');//NaN
javascript提供了一个isNaN函数可以辨别数字与NaN
console.log(isNaN('0'));//false
console.log(isNaN('opp'));//true
判断一个值是否后可用数字的最佳方法是使用isFinte函数,因为它会筛选点NaN和Infinity。不幸的是,isFinite会试图把它的运算数转换为一个数字。所以如果值事实上不是一个数字,它就不是一个好的测试,你可以定义自己的isNumber函数
function isNumber(value){ return typeof value==='number' && isFinite(value); } console.log(isNumber(4));//true console.log(isNumber('3'));//false console.log(isFinite('3'));//true