1.Function类型
javascript所有的函数都只是Function类型的一个实例,函数是对象,函数名是指针,可以通过用Function()构造函数的方式创建函数,这样子效率很低,但是可以很直观的看到——函数只是个对象。所以,如果是函数名赋值为null,但是还有其他的指针指向这个函数的话,这个函数依旧不会被垃圾收集掉。
2.函数声明和函数表达式
浏览器的解析器在向执行环境中加载数据时,对函数表达式和函数声明不是一样对待的
函数声明(可以先调用后声明):
alert(sum(10, 10)); function sum(num1,num2) { return num1 + num2; }
这样子写是没有问题的,虽然函数在执行之后声明,但是解析器会率先读取函数声明,并且使其在执行任何代码之前保证可以访问。所以即使先执行后声明函数都是没有问题的
函数表达式(不能先调用后赋值)
alert(sum(10, 10)); var sum = function (num1, num2) { return num1 + num2; }
这种模式会在引用sum的时候就报错了,因为这时的sum是未定义状态,而且js也不会往下执行了。
ps:还有一种区别就是函数表达式声明的函数实际上是一个匿名函数,其name属性是空值(或者是在某些ie下为未定义),var 后面变量的名字仅仅代表的是这个指针指向匿名函数,但是没有改变函数本身的性质
3.通过arguments减少耦合
函数都有一个arguments对象,这个对象是用来指函数的参数用的,但是,arguments对象有一个callee属性,是一个指针,指向这个arguments对象的函数(换句话说 arguments.callee()就等于调用这个方法)
// 第一种模式,耦合紧密 function factorial2(num) { if (num <= 1) { return 1; } else { return num * factorial2(num-1); } } alert(factorial2(4)); // 第二种模式,耦合不紧密 function factorial1(num) { if (num <= 1) { return 1; } else { return num * arguments.callee(num - 1); } } alert(factorial1(6));
这里有2种方法,第一种方法的话就会使得耦合度很高,如果以后改了函数名字之后会导致在函数内部的名字也需要更改,而第二种就没有这个问题,可以改完之后直接使用
4.通过call()和apply()来扩充函数作用域
apply和call基本一样,就是你传入的参数有些不同
通过call()扩充函数作用域
window.color="red"; var o={color:"blue"}; function sayColor(){ alert(this.color); } sayColor.call(this) ; sayColor.call(window); sayColor.call(o);
这样子的话就可以实现函数在指定的作用域下执行(改变这个函数内的this指针指向的位置)
ps:我在很多地方看到的都是说 sayColor.call(this) ;是把sayColor执行的时候指针指向this,但是其实我的测试结果应该是sayColor的执行作用域被同步为“this所处于的作用域”,因为你把this换成任何对象或者对象属性(非对象)都是可以的;
5.基本包装类型
假如我们声明一个var s1="some text";的话,我们可以有s1.substring(2)这样的方法来切割字符串,但是s1是一个基本数据类型,不是一个对象,怎么会有方法呢?
是这样的,实际上我们在调用substring方法的时候,创建了一个新的String对象的实例,这个实例调用制定的方法,然后这个实例被销毁掉
var s1="some text"; alert(typeof s1); s1=new String("some text"); alert(typeof s1); var s2=s1.substring(2); s1=null; s1="some text";
可以看作是执行了这样的代码,而因为是自动创建的,生存周期只有执行这一行代码的短短时间。执行完了就被销毁了。所以不能给这样的对象添加属性和方法,就算添加了之后还是会被销毁
所以
1:即使js可以方便的给基本类型调用方法,但是基本类型不能添加属性和方法还是保留这个特性的。
2:最好不要显式的声明String,Number,Boolean类型的数据,因为这样可能会造成误解,typeof和instanceof返回的结果是不一致的