一.函数的理解
有人把函数比作现实中的菜谱,这个比喻很形象。函数就是预先定义好的代码段,里面规定了一些步骤。在被调用后,将会按照预设的步骤执行,直到碰到返回语句或步骤结束。
二.function的定义方式
1.直接声明:
function myfunc(/arguments/){
} 或者
var myfunc=function(/arguments/){
}
这两种定义的区别:第一种方式在声明时是一个命名的函数,无论在调用之前、调用之后,甚至是不会执行到的位置(例如return语句之后或者永远不会为真的分支里),都在整个作用域可访问;第二种方式是通过把匿名函数赋值给变量的方式,严格意义上说这个只是一个函数表达式,在赋值之前这个函数不能被任何代码访问到,可以理解为必须先赋值,后调用。
例子:
var inner;
function outer(){
//inner=10;
return inner;
function inner() {
}
/* var inner=function() {
}*/
}
console.log(typeof outer());
return 前面如果inner没有赋值时,这个console输出结果是function。
若函数改成 var inner=function,console输出结果为undefined.如果return前面inner 有赋值(10),console输出结果是 number。
小注:定义后myfunc只是指向函数地址,如果要执行函数必须在调用时加上一对小括号,比如:myfunc();
JavaScript 函数的递归调用
比如:
function myfunc(n){
if(n==1){
return 1;
}else{
return myfunc(n-1);
//或者 return arguments.callee(n-1);
}
}
console.log(myfunc(3));//输出结果都为1;
在“use strict” 严格模式下,callee 会报TypeError错误。
小知识点扩展:arguments对象是比较特别的一个对象,实际上是当前函数的一个内置属性,里面存储了函数实参的值,比如{“0”,”第一个参数值”}
例子:
function myfunc(n){
console.log(arguments);// 结果:{"0",1}
}
myfunc(1);
实参论证:
function f(a, b, c) {
console.log(arguments.length); // result: "2"
a = 100;
console.log(arguments[0]); // result: "100"
arguments[0] = "qqyumidi";
console.log(a); // result: "qqyumidi"
console.log(c); // result: "undefined"
c = 2012;
console.log(arguments[2]); // result: "undefined"
}
f(1, 2);
2 匿名函数
在JavaScript可以声明一个没有名称的函数,称为匿名函数(Anonymouse Function)。匿名函数好处:由于没有名称,且有自己的作用域,内嵌的函数作用范围也只在这个匿名函数内,不会引入新的变量污染上下文环境。
JS库使用的原理:JavaScript 运行时有一个特殊的全局环境(global object),这个对象上面存放全局的函数和变量,在实际项目中会经常使用若干个第三方的库或js文件,如果众多的js中有些函数声明同名,则会造成代码执行混乱。比如:先后引入两个js文件,分别定义自己的函数log作为内部使用,第二个引入的函数会覆盖掉第一个定义并且不会抛出任何 错误,在后续调用时就有可能造成错误。若使用匿名函数将整个js内的逻辑包装起来,这样库以库之间就不会受到干扰。
例子:
(function () {
function log(msg) {
console.log(msg);
}
console.log(1);
}());//只运行一次,结果输出1;
// 另外一些匿名函数写法
+function(){}(); //声明前面加一元操作符+ !~号,直接把声明变成函数表达式。后面加(),就能直接运行。 不推荐这样写。
(function(){})()
!function(){}();
~function(){}();
如果带参数的匿名函数,可以这样写:
(function(x,y){return x-y})(2,3) //2,3就时调用时,传递给匿名函数的实参
上面代码只能运行一次,无法被调用访问。下面有3种方法作为接口,供外部调用。
var mylib = (function(global) {
function log(msg) {
console.log(msg);
}
log1 = log; // 法一:利用没有var的变量声明的默认行为,在log1成为全局变量(不推荐)
global.log2 = log; // 法二:直接在全局对象上添加log2属性,赋值为log函数(推荐),js全局对象用global,浏览器全局对象用widndow.
return { // 法三:通过匿名函数返回值得到一系列接口函数集合对象,赋值给全局变量mylib(推荐)
log: log
};
}(global));
log1(1);
log2(2);
mylib.log(3);
知识拓展:
异步加载机制;
commonJS 推出的AMD规范,全称是Asynchronous Module Definition,即异步模块加载机制。从它的规范描述页面看,AMD很短也很简单,但它却完整描述了模块的定义,依赖关系,引用关系以及加载机制。从它被requireJS,NodeJs,Dojo,jquery使用也可以看出它具有很大的价值,没错,JQuery也采用了AMD规范。
AMD规范只有一个API,即define 函数:
define([module-name?],[array-of-dependencies?],[module-factory-or-object]);
其中:
module-name:模板标识,可以省略。
array-of-dependencies:所依赖的模块,可以省略。
module-factory-or-object:模块的实现,或者一个JavaScript对象
define 可以定义一个模块:
例子: define(“alpha”,[“require”,”exports”,”beta”],function(require,exports,beta){
exports.veb=function(){
return beta.verb();
//或者:
return require(“beta”).verb();
}
})
define 定义一个对象:
例子:
define({
provinces:[{name:’上海’,ares:[‘浦东新区’,’徐汇区’]}]
});
define 定义一个函数:
例子:
define(function(){
var p=require(‘china’);//用require 引入直接需要的名叫china.js 的模块
})
vue 框架库的定义方式:
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.Vue = factory());
}(this, (function () { 'use strict';
return Vue$3;
})));
typeof exports === ‘object’ && typeof module !== ‘undefined’ 这两句判断应该是判断是不是后端nodejs;
typeof define === ‘function’ && define.amd 这两句判断是不是已经定义了异步amd。
(global.Vue = factory()) 当前面两个都没有时,在全局对象里边增加一个Vue参数,指向factory() 函数。 factory是形参,实参是后面的(function(){ ‘use strict’;… }) 。
闭包(Closure)
闭包:在JavaScript中,在内嵌函数中使用外部函数作用域内的变量,就是使用了闭包。
闭包有时会导致内存泄漏:
var elem = document.getElementById('test');
elem.addEventListener('click', function() {
alert('You clicked ' + elem.tagName);
});
内嵌匿名函数调用了外层函数的DOM对象(elem),这种由匿名函数组成的闭包现象,即使删掉DOM对象(elem),内存里面的闭包也不会释放。
解决这个问题:用this替代DOM(elem),this指向的是DOM元素本身,JS不认为是调用外层函数变量,因此不会形成闭包。
代码如下:
var elem = document.getElementById('test');
elem.addEventListener('click', function() {
alert('You clicked ' + this.tagName); // 不再直接引用elem变量
});
3、类构造函数:
JavaScript的函数如果是类构造函数,直接用new创建类的实例就好了。
var myfunc= new Function(“参数1”,“参数2”,“函数体;return返回值”);
例子:
function Flyer(name){
this.name=name;
this.toString=function(){
return 'Hello,' +this.name+'!';
}
}
var plane= new Flyer('boeing 747');
console.log(plane.toString());//输出:Hello,boeing 747!
JavaScript函数作为类构造函数使用时如果加了返回值,会出现一些问题。
function MyClass(name) {
this.name = name;
return name; // 构造函数的返回值?
}
function MyClass(name) {
this.name = name;
return name; // 构造函数的返回值?
}
var obj1 = new MyClass('foo'); // 创建一个MyClass的对象实例
console.log(obj1);// obj1 指向的是实例对象的地址
var obj2 = MyClass('foo');// 当普通函数调用
console.log(obj2);// 返回 foo值
var obj3 = new MyClass({}); // 创建了一个实例,同时给obj3返回了一个指向空对象的地址。
console.log(obj3); //输出空对象
var obj4 = MyClass({}); //当普通函数调用
console.log(obj4);//返回{};
所以不建议在构造函数里边用return。
三、Function 类
JavaScript 内部类Function,用function关键字声明一个函数其实是创建Function类对象的一种简写形式,所有的函数都拥有Function类所有的方法,例如call、apply、bind 等 。
new Function([arg1[,arg2[,arg3[,…argN],]functionBody)
其中 arg1~argN 是字符串格式的参数,functionBody 也是字符串。Function的构造函数会把最后一个参数当做函数体,前面的都当中参数处理。
例子:
var func1=new Function('name','return "Hello,"+name+"!";');
func1('Ghostheaven');
自更新函数
函数内部再次重新定义函数本身
例子:
function selfUpdate() {
window.selfUpdate = function() { //window用global替换这里会报错
console.log('second run!');
};
console.log('first run!');
}
selfUpdate(); // first run!
selfUpdate(); // second run! 这种函数可以用于只运行一次的逻辑,在第一次运行之后就整个替换成一段新的逻辑。
参考文章:
http://www.cnblogs.com/lwbqqyumidi/archive/2012/12/03/2799833.html
http://www.jb51.net/article/57482.htm
http://blog.youkuaiyun.com/xiebaochun/article/details/42172643
http://www.jb51.net/article/62261.htm
http://blog.youkuaiyun.com/xiangyong58/article/details/50569071