-----根据JavaScript权威指南所学
一:函数概念相关
1.函数是只定义一次,但可能被执行或调用任意次的一段Javascript代码。
2.function(){….}花括号是必需的,即便函数体只有一条语句,也要用花括号括起来。
3.Javascript没有块级作用域,只有函数作用域。
4.在函数体内,局部变量的优先级高于同名的全局变量。如果在函数内声明的一个局部变量或者函数参数中带有的变量和全局变量重名,那么全局变量就被局部变量所遮盖。
5.js中的函数不能重载,如果有两个同名的函数,后面的函数会覆盖前面的函数。
5.补充关于变量作用域:
javascript的全局变量和局部变量的分界是这样的:
过程体 ( 包括方法 function, 对象 Object o ={} ) 外的所有变量不管你有没有加var保留字,他都是全局变量
而在过程体内 ( 包括方法 function(){},对象 Object o={} ) 内的对象,加var保留字则为局部变量,而不加var保留字即为全局变量
var scope="global"; //加了VAR是全局变量
function checkscope(){
s="local";
return [scope,s];
}
checkscope() =>["global", "local"]
s =>"local" //s没有加var,是全局变量
scope =>"global"
scope="global"; //不加var也是全局变量
function checkscope(){
var scope="local";
return scope;
}
s =>undefined //s加了var,是局部变量
scope =>"global"
使用var声明一个变量时,创建的这个属性是不可配的,但是如果没有使用严格模式,并且给一个为生命的变量赋值,这个属性是可以配置的。
var a=1;
b=2;
delete a =>false
delete b =>true
6.声明提前:Javascript函数里生命的所有变量(但不涉及赋值)都被”提前”至函数顶部。”声明提前”是在Javascript引擎和”预编译”时进行的。
var sco="global";
function f(){
console.log(sco);
var sco="local"; //局部变量sco在这里赋初值,但在函数体内任何地方都是有定义的
}
f(); =>undefined
局部变量遮盖了同名全局变量sco。在整个函数体内都存在,但只有执行到到var sco=”local”语句时,局部变量才会被真正赋值。
上述函数等同于:将函数体的变量声明提前至函数顶部,同时变量初始化留在原来的位置。
var sco="global";
function f(){
var sco;
console.log(sco);
sco="local";
}
f() =>undefined
二:函数定义
函数使用关键字function定义。它有两种形式:
1.函数定义表达式
var funcname=function(a,b,c...){........}
对于以表达是定义的函数,要想调用它,必须要能引用它。所以要使用这个函数,必须把它赋值给一个变量。var s=function(..){…}
同var声明变量一样,变量的声明提前了,但给变量赋值是不会提前的,所以,以表达式方式定义的函数在它定义之前的代码不能调用它
通过这种方式定义的函数,通常是匿名函数。匿名函数的Name属性是空字符串。
2.函数声明语句
function funcname(a,b,c...){......}
函数名funcname是一个变量名,指向函数对象。
使用函数声明,函数名称和函数体均提前,在整个脚本和函数内都是可见的:即可以被在它定义之前的代码所调用。
浏览器给函数定义了一个非标准的name属性,通过它可以访问到给函数的名字
sayName.name
=>"sayName"
三:return
1.任何函数在任何时候都可以通过return语句后跟要返回的值来实现返回值。
2.函数在执行完return语句之后停止并立即退出
3.一个函数中可以有多个return语句。
function diff(num1,num2){
if(num1>num2){
return num1-num2;
}else{
return num2-num2;
}
}
4.return语句可以不带任何返回值。这种情况下函数在停止执行后返回undefined
function sayHi(name,message){
return;
alert("hello"+name+","+message); //这一句不会执行到
}
5.不带return语句的函数,会依次执行语句并返回undefined。所以一个函数总有一个返回值。
四:参数
javascript的函数参数不介意传进来几个参数,也不在意传进来的参数是什么类型。当定义函数时有两个参数,调用时可以传递1,3,甚至不传参数,当>2时,后面的参数会被忽略,<2时,少的部分会被定义成undefined。因为在JS中参数使用一个参数数组(arguments)表示的,函数是中接受的是这个数组,而不管这个数组中的元素。
function sayHi(name,message){
alert("hello"+name+","+message);
}
可以写成
function sayHi(){
alert(arguments.length);
alert("hello"+arguments[0]+","+arguments[1]);
}
sayHi("lily","how are you") =>2,hello.....
也可以写成
function sayHi(name,message){
alert(arguments.length);
alert("hello "+arguments[0]+","+message);
name='amy';
alert(arguments[0])
} //arguments可以跟命名参数一起使用,并且它的值永远与对应命名参数的值同步。(他们的内存空间是独立的,只是值可以同步)
sayHi("lily","how are you") =>2,hello.....,amy
arguments只是一个类数组对象,它拥有一个length属性,但没有任何数组方法。
如果调用时只传入一个参数,对arguments[1]的设置不会反映到命名参数中,因为arguments的长度是由传入的参数个数决定的,不是由定义函数的命名参数决定的。
function sayHi(name,message){
alert(arguments.length);
alert("hello"+arguments[0]+","+message);
arguments[1]='i love you';
alert(arguments[1])
alert(message)
}
sayHi("lily")
=>1, hello lily,undefined, i love you, undefined
arguments还有一个callee属性,指向拥有这个arguments对象的函数。
function factorial(num){
if(num<1){
return 1;
}else{
return num * arguments.callee(num-1);
}
}
factorial(5) =>120
caller这个属性中保存着 调用当前函数的函数的引用,如果在全局作用域中调用当前函数,它的值为Null
function outer(){inner();}
function inner(){alert(inner.caller)};
//等同于function inner(){alert(arguments.callee.caller)};
outer(); =>function outer(){inner();}
因为是outer()调用了inner(),所以caller就指向outer()
五:函数嵌套
函数可以嵌套在其他函数内部。
通过函数声明定义的函数:以内函数声明并非真正的语句,可以出现在全局代码里,其他函数内,但不能出现在循环,条件判断,try/catch/finally/with语句中。
函数表达式定义的函数可以出现在js代码的任何位置。
六:作为值的函数
函数名本身就是变量,所以函数也可以作为值来使用。也就是说,不仅可以像传递参数一样把一个函数传递给另外一个函数,而且可以将一个函数作为另一个函数的结果返回。
function callSomeFunction(someFunction,someArgument){
return someFunction(someArgument);
}
function add(a){
return a+10;
}
var result=callSomeFunction(add,10) =>20
七:函数的属性和方法
每个函数都包含两个属性:length和prorotype
length表示函数希望接收参数的个数
function sayHi(name,message){....}
sayHi.length =>2
function sayNo(no){........}
sayNo.length =>1
function nosay(){.......}
nosay.length =>0
每个函数都包含两个非继承而来的方法:call()和apply()
这两个方法的作用是在特定的作用域中调用函数,实际上等于设置函数体this对象的值。
call()和apply()的主要区别是参数不同:call(o,arg1,arg2),apply(o,[arg1,arg2])
window.color="red";
var o={color:"blue"};
function sayColor(){
alert(this.color);
}
sayColor.call(); =>red
sayColor.call(this); =>red
sayColor.call(window); =>red
sayColor.call(o); =>blue
bind()方法:创建一个函数的实例,其this值会被绑定到传给bind()函数的值
var objectSayColor=sayColor.bind(o) =>blue
toString()和toLocalString()返回函数的代码
sayName.toString()
=> "function sayName(name,sex){alert(name)}"
八:递归
递归函数是一个函数通过自己的名字调用自身的情况。
也可以通过argumente.callee来实现递归函数。但严格模式下,不能使用。
也可以使用命名函数表达式来实现递归,这种方式在严格模式下也可以使用
var factorial=function f(num){
if(num<1){
}else{
return num * f(num-1)
}
}
九:闭包
为了理解闭包,首先要理解作用域链。
1.执行环境和作用域
概念:执行环境定义了变量或函数有权访问其他数据,决定了他们各自的行为。每个执行环境有与之对应的变量对象,保存环境中定义的变量和函数。
全局执行环境:全局执行环境是最外围的一个执行环境。web浏览器中全局执行环境是window对象,因此所有全局变量和函数都作为window的属性和方法。
销毁:某个执行环境中的所有代码执行完毕,该执行环境被销毁,保存在其变量对象中的所有变量和函数也会随之被销毁。但是全局执行环境只有在关闭网页或者浏览器时才会被销毁。
每个函数都有自己的执行环境。
2.作用域链
当代码在一个环境中执行时,会创建变量对象的一个作用域链。
作用域链的用途是:保证对执行环境有权访问的所有变量和函数的有序访问。
作用域链的最前端,始终是当前执行的代码所在环境的变量对象。
作用域链的下一个变量对象是外部环境,下一个变量对象又是下一次包含环境,一直延续到全局执行环境。
var color="blue";
function changeColor(){
var anotherColor="red";
function swapColors(){
var temp=anoterColor;
anthorColor=color;
color=temp;
}
swapColor();
}
changeColor();
以上代码有三个执行环境:
1. swapColor()的局部执行环境(包含一个temp变量)
2. changeColor()的局部执行环境(包含一个anotherColor变量和一个swapColor()函数)
3. 全局环境(包含一个color变量和一个changeColor()函数)
内部执行环境可以通过组用于连访问外部执行环境,外部执行环境不能访问内部执行环境中的任何变量和函数。changeColor()不能访问temp变量
延长作用域:
1.try/catch语句,创建一个新的变量对象,其中包含的是被抛出的错误对象的声明。
2.with语句,会将指定的对象添加到作用域链中。with(o)
function bindurl(){
var qw=" withtrue";
with(location){var url=href+qw};
return url;
}
bindurl() =>"chrome-search://local-ntp/local-ntp.html withtrue"