JavaScript中的函数
1. 函数的定义
- 两种定义形式:
- 通过函数定义表达式来定义
- 通过函数声明语句来定义
函数声明语句定义一个函数
//计算阶乘的递归函数
function factorial(x){
if (x<=1) return 1;
return x*factotial(x-1);
}
函数定义表达式定义一个函数
var s= function sum(x,y){
return x+y;
}
tips:以表达式方式定义的函数(特别适合用来定义那些只会用到一次的函数),函数名是可选的,
也就可以直接写成这样
var s= function(x,y){
return x+y;
}
2.函数命名
- 函数的名称通常是动词或者以动词为前缀的词组 如:funciton saveMessage(){}
- 函数名的第一个字符通常为小写
- 当我们命名的函数名比较长时,一种是驼峰式命名 如:function readSystemFile(){}
另一种是以下划线分割单词,如:function make_products_iPhones(){} - 有一些函数是用作内部函数或者私有函数的,通常以一条下划线为前缀。 如: _login()
3.函数的调用
- 普通的函数调用
printprops({x:1})
var total = distance(0,1,2)+ distance(3,4,5)
var probability = factorial(5)/factoral(10)
- 对象方法的调用
//定义一个对象
var person = {
name: lihua ,
age: 18 ,
sex: 女 ,
send: function(){//返回 person的name、age、sex
this.message = this.name + this.age + this.age;
}
};
person.send();//这条语句就是函数的方法调用
person.message; //得到name、age、sex相关信息
- 构造函数的调用
构造函数就是用来初始化先创建的对象,通常使用关键字new来调用构造函数,当使用new关键字来调用构造函数的时候就会自动
创建一个新的的空对象,而构造函数只需要初始化这个新对象的状态(属性和方法),调用构造函数的话,新的对象的原型(prototype)等于
构造函数的原型(prototype)属性,由此引出一个特性:通过同一个构造函数创建的所有对象都继承同一个相同的对象。
凡是没有形参的构造函数都可以省略圆括号,以下两行代码是等价的。
var fn = new Object();
var fn = new Object;
- 间接调用
JavaScript中的函数也是对象,所以函数对象也可以包含方法。函数的间接调用用到的call()和apply()方法。
这两个方法都能显示指定调用所需的this的值,这就引出一个特性:任何函数都可以作为对象的方法来调用,
哪怕这个函数不是那个对象的方法。(这也就是在实际开发中我们也会常用这两个方法的原因之一)
4.函数中的形参与实参
- 可选的形参
- 可变长的实参列表:实参对象 arguments 它是一个类数组对象,通过数字下表就能够访问传入函数的实参值,它包含length属性,让函数可以操作任意数量的实参。
- callee和caller属性
callee属性指代当前正在执行的函数,caller指代调用当前正在执行的函数的函数,它可以访问栈。而callee可以来递归调用自身。
5.函数的闭包(!important)
- 概念:通俗地讲函数的闭包就是在一个函数内部定义另一个函数,而这个内部的函数(子函数)可以调用包裹它的函数(父函数等、"爷爷、太爷爷...")的变量。
也可以认为闭包就是能够读取其他函数内部变量的函数。 - 变量作用域:
全局变量:任何函数内部都可以访问全局作用域
局部变量:在函数外部无法读取变量
- 词法作用域:变量的作用域是在定义时决定而不是执行时决定,也就是说词法作用域取决于源码,通过静态分析就能确定,因此词法作用域也叫做静态作用域。
with和eval除外,所以只能说JS的作用域机制非常接近词法作用域(Lexical scope)。
var scope = "global scope";
function checkScope(){
var scope = "local scope";
function f(){return scope;}
return f();
}
checkScope(); //输出可以得到 local scope
闭包的用途
i. 读取函数内部的变量、函数嵌套函数
ii. 让这些变量的值始终保持在内存中(全局变量的值不会在被函数调用过后自动清除,由GC回收)
iii. 避免全局变量的污染、让私有成员存在闭包的注意事项
i. 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。
解决方法是,在退出函数之前,将不使用的局部变量全部删除。
ii. 闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),
把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
以下例子来检验自己是否已经掌握了闭包的运行机制。
example 0ne:
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()());
example Two:
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
var that = this;
return function(){
return that.name;
};
}
};
alert(object.getNameFunc()());
6.函数的属性、方法以及构造函数
- length属性
argument.length属性表示传入函数的实参的个数,而函数本身的length属性则有不同含义,它表示函数定义时的实际形参个数。 - prototype(原型)属性
这个属性指向对象的引用,而这个对象就被称为原型对象(prototype object),每个函数都包含这个属性,都包含不同的原型对象;
在JavaScript中每个函数都有一个特殊的属性叫作原型(prototype)
function doSomething(){}
console.log(doSomething.prototype);
-----------------------------------
结果: 这就是 原型对象
{
constructor: ƒ doSomething(), //构造函数
/* 这些又是这个构造函数里面的方法或者属性
arguments: null
caller: null
length: 0
name: "doSomething"
prototype: {constructor: ƒ} 构造函数的原型属性
__proto__: ƒ ()*/
__proto__: { //原型属性
constructor: ƒ Object(),
hasOwnProperty: ƒ hasOwnProperty(),
isPrototypeOf: ƒ isPrototypeOf(),
propertyIsEnumerable: ƒ propertyIsEnumerable(),
toLocaleString: ƒ toLocaleString(),
toString: ƒ toString(),
valueOf: ƒ valueOf()
}
}
-------------------------------
function doSomething(){}
doSomething.prototype.eat = "food" //doSomething函数的原型(prototype)属性对象添加eat的属性
var doSomeInstancing =new doSomething() //创建doSomething函数的实例 通过new关键字来调用该函数,它返回这个函数的实例化对象给doSomeInstancing
doSomeInstancing.prop = "add value"; //给对象doSomeInstancing添加一个“name为prop值为add value”的属性
console.log(doSomeInstancing); //输出这个doSomeIntancing对象 结果现实这个对象有两个属性,一个是:prop: "add value" 另一个是:__proto__: Object
console.log(doSomething.prototype); //输出doSomething.prototype的值是一个对象{eat: "food",constructor: ƒ doSomething(), __proto__: Object
显然doSomeInstancing.__proto__属性与doSomething.prototype(构造函数的原型属性的值是相同)它们的值是相等的。(可以通过===来判断 __ptototype__为隐式原型)
- call()、apply()和bind()方法 在前面的函数的间接调用中已经介绍了call()以及apply(),在这里就不再叙说,就详细介绍ECMAScript5新增的bind()方法
function f(y){return this.x+y;} //待绑定的函数
var o = {x :1};//将要被绑定的对象
var g = f.bind(o); //将函数f绑定至对象o 相当于var o={x:1, f:function f(y){return this.x+y;}}
console.log(g(2)); //通过g(y)调用o.f(y) 输出3
var sum = function(x,y){return x+y;}
var succ = sum.bind(null,1);
succ(2)
- toString()方法
该方法返回字符串 - Function()构造函数
- 高阶函数
- 记忆(memorization)