一.函数
1.定义Function对象:
- Function类可以表示开发者定义的任何函数
- 虽然由于字符串的关系,第二种创建方式写起来有些困难,但有助于理解函数只不过是一种引用类型,它们的行为与用 Function类明确创建的函数行为是相同的
- js的函数加载执行与python不同:整体加载完才会执行,所以在函数声明上/下执行函数都可以
//用Function类直接创建函数的语法:
1.function 函数名 (参数){
函数体;
return 返回值;
}
alert(1);
function func1(){
alert('hello yuan!');
return 8
}
ret=func1();
alert(ret)
2.var 函数名=new Function('参数1'...'参数n','function_body');
var func1=new Function("name","alert(\"hello\"+name);")
func1("yuan")
<script>
//f();----->OK
function f(){
console.log("hello")
}
f()//----->OK
</script>
2.功能说明:
·可以使用变量,常量,表达式作为函数调用的参数
·函数由关键字function定义
·函数名的定义规则与标识符一致,大小写是敏感的
·返回值必须使用return
3.Function对象的属性
length:声明了函数期望的参数个数
function fun1(a) {return a}
alert(func1.length)//1
4.Function对象的方法
valueOf()参见:https://www.cnblogs.com/fengweb/p/5755052.html
toString()参见:https://www.cnblogs.com/czhyuwj/p/5895766.html
//也有与所有对象共享的valueOf()方法和toString()方法
//返回的都是函数的源代码,在调试时尤其有用
func1.valueOf();
fun1.toString();
运算符void():拦截方法的返回值
alert(void(fun1(1,2)))
5.函数的调用
function func1(a,b){
alert(a+b);
}
func1(1,2);//3
func1(1,2,3);//3--->取前2个
func1(1);//NaN--->第二个参数传入undefined
func1();//NaN--->两个参数均传入undefined
fun1('hello','world');//helloworldundefined
//只要函数名写对即可,参数怎么填都不报错.
function a(a,b){
alert(a+b);
}
var a=1;
var b=2;
a(a,b)//报错:a is not a function
6.函数的内置对象arguments:集成在函数中,所有传入的参数构成1个arguments对象
function add(a,b){
console.log(a+b);//3
console.log(arguments.length);//2
console.log(arguments);//1个arguments对象(见下图)
}
add(1,2)
//用处1:
function nxAdd(){
var result=0;
for (var num in arguments){
result+=arguments[num]
}
alert(result)
}
nxAdd(1,2,3,4,5)
//用处2:
function f(a,b,c){
if (arguments.length!=3){
throw new Error("function f called with"+arguments.length+" arguments,but it just need 3 arguments")
}
else {
alert("success!")
}
}
f(1,2,3,4,5)
7.匿名函数
//func('hello')//报错,匿名函数不加载
var func = function(arg){
alert(arg);
}
func('hello')//hello
//这不是一个标准的函数,是一个匿名函数,赋给func
//匿名函数的应用
(function(){
alert("tony");
} )()
(function(arg){
console.log(arg);
})('123')
8.自执行函数:创建并执行函数
(function [<func>]([<param1>...]) {<command>})()
//实例:
(function inner() {
console.log(this);
})()
//相当于:
function inner() {
console.log(this);
}
inner()
二.函数的作用域链和闭包
1.作用域
- js的作用域和py相似,if/while等控制语句没有自己的作用域,而函数有自己的作用域:
if(1==1){
var s=12;
}
console.log(s);//12
function f(){
var temp=666;
console.log(temp)
}
f();//666
console.log(temp);//报错:Uncaught ReferenceError: temp is not defined
- 嵌套函数的作用域:
var city = 'beijing';
function func(){
var city = 'shanghai';
function inner(){
var city = 'shenzhen';
console.log(city);
}
inner();
}
func();//shenzhen
var city = 'beijing';
function Bar(){
console.log(city);
}
function func(){
var city = 'shanghai';
return Bar;
}
var ret = func();
ret();//beijing
- 作用域是在执行前创建的:
var v=123;
function foo() {
var v=456;
function inner() {
console.log(v);//无论在哪里执行,都输出456
}
return inner
}
result=foo()
console.log(result())
2.闭包
var city = 'beijing';
function func(){
var city = "shanghai";
function inner(){
//var city = "langfang";
console.log(city);
}
return inner;
}
var ret = func();
ret();//shanghai
var city = 'beijing';
function func(){
var city = "shanghai";
function inner(){
//var city = "langfang";
console.log(city);
}
return inner;
}
var ret = func();
ret();//shanghai
var city = 'beijing';
function Bar(){
console.log(city);
}
function func(){
var city = 'shanghai';
return Bar;
}
var ret = func();
ret();//beijing
3.作用域链(Scope Chain):
- 在JS中,函数也是对象,实际上,JS里一切都是对象;函数对象和其它对象一样,拥有可以通过代码访问的属性和一系列仅供JS引擎访问的内部属性;其中一个内部属性是[[Scope]],由ECMA-262标准第三版定义,该内部属性包含了函数被创建的作用域中对象的集合,这个集合被称为函数的作用域链,它决定了哪些数据能被函数访问
var x=1;
function foo() {
var y = 2;
function bar() {
var z = 3;
}
}
#bar的作用域链: barScopeChain=[bar.AO, foo.AO, global.VO];
#foo的作用域链: fooScopeChain=[foo.Ao, global.VO];
AO,VO:
在函数创建时,每个函数都会创建1个活动对象Active Object(AO)全局对象为Global Object(VO)
创建函数的过程也就是为这个对象添加属性的过程,作用域链就是由这些绑定了属性的活动对象构成的
例如:寻找x变量,bar()在搜寻变量x的过程中,先从自身AO对象上找
如果bar.AO存在这个属性,则会直接使用这个属性的值
如果不存在,则会转到父级函数的AO对象,也就是foo.AO
如果找到x属性则使用,找不到继续在global.VO对象查找,找到x的属性,返回属性值
如果在global.VO中没有找到,则会抛出异常ReferenceError
执行上下文:
函数在执行时会创建1个称为"执行上下文"(execution context)的内部对象,该对象定义了函数执行时的环境
每个执行上下文都有自己的作用域链,用于标识符解析,当执行上下文被创建时
它的作用域链初始化为当前运行函数的[[Scope]]所包含的对象
函数执行:
在函数执行过程中,每遇到1个变量,都会检索从哪里获取和存储数据
该过程从作用域链头部(即活动对象)开始搜索,查找同名的标识符
如果找到了就使用这个标识符对应的变量,如果没有则继续搜索作用域链中的下一个对象
如果搜索完所有对象都未找到,则认为该标识符未定义,函数执行过程中,每个标识符都要经历这样的搜索过程
- 创建作用域链的过程:
//函数进入全局,创建VO对象,绑定x属性<入栈>
global.VO={x=underfind; foo:reference of function}
//这里只是预解析,为AO对象绑定声明的属性
//函数执行时才会执行赋值语句,所以值是underfind
//遇到foo函数,创建foo.AO,绑定y属性<入栈>
foo.AO={y=underfind, bar:reference of function}
//遇到bar函数,创建bar.AO,绑定z属性<入栈>
bar.AO={z:underfind}
//作用域链和执行上下文都会保存在堆栈中,所以:
//bar函数的scope chain为:
[0]bar.AO-->[1]foo.AO-->[2]global.VO
//foo函数的scope chain为:
[0]foo.AO-->[1]global.Vo
//建议:少定义全局变量
//理由:因为作用域链是栈的结构,全局变量在栈底
//每次访问全局变量都会遍历一次栈,这样会影响效率
- 函数的scope等于自身的AO对象加上父级的scope,也可以理解为一个函数的作用域等于自身活动对象加上父级作用域
- 函数执行前后的作用域链:
- 作用域链的非自己部分在函数对象被建立(函数声明,函数表达式)的时候建立,而不需要等到执行
for (var i=1; i<=9; i++) {
setTimeout( function timer(){
console.log( i );
},1000 );
}
for (var i=1; i<=9; i++) {
(function(){
var j = i;
setTimeout(function timer(){
console.log( j );
},1000);
})();