JS函数
函数定义,函数实参形参,匿名函数,自执行函数,闭包
##Javascript函数
1、函数定义
定义函数有两种方式:
1、函数声明
function sum(x,y){
alert(x+y);
}
sum(1,2)
2、函数表达式
var sum = function(x,y){
alert(x + y)
}
sum(1,2);
3、函数提升 这两种定义之中只有函数声明会在代码执行之前有提升,即下列情况可以正常执行
sum(1,2)
function sum(x,y){
alert(x+y);
}
2、匿名函数、自执行函数
函数表达式定义可以等价于 定义一个匿名函数 — 将匿名函数赋值给变量sum,执行的时候在变量名后面加上一对(),这其实是匿名函数的一种调用方法。 还有一种匿名函数的调用方法,用括号包裹表示函数声明表达这是一个函数表达式,后面的括号表示立即执行。
(function(){
//块级作用域
})();
function(){
}(); //这是错误的,因为function关键字是一个函数声明的开始,函数声明后面不能立即执行。
var f = function(){**}; //定义一个函数
f(); //当我们在函数后面加个括号就能实现自执行,那么是不是意味着下面这个也可以自执行?
function(){**}(); //SyntaxError: Unexpected token
并不能实现!
######1、原因:
在一个表达式后面加上括号(),该表达式会立即执行,但是在一个语句后面加上括号() ,是完全不一样的意思,他的只是分组操作符。
######2、解决:
这个问题,我们只需要用大括弧将代码的代码全部括住就行了,因为JavaScript里括弧()里面不能包含语句。 解析器在解析function关键字的时候,会将相应的代码解析成function表达式,而不是function声明。
######3、用法:
用于闭包保存状态,eg见上文。利用这些被lock住的传入参数,自执行函数表达式可以有效地保存状态。
for (var i = 0; i < 5; i++) {
(function(i){
setTimeout(function() {
console.log(i);
}, 1000 * i);
})(i)
}
###
3、函数实参形参,参数传值流程
上图三种情况分别是:实参为数值、空、x三种情况,形参不为空且是x,arguments是每个函数都会有的Arguments对象的实例arguments,它引用着函数的实参。 参数传参流程:
1、实参会获取全局环境中的值,如第三块x获取全局定义var x =11;的值,第二块f( )调用获取了一个空值undefined;
2、实参值会传给函数申明中的形参x,值到括号里了,此时形参的值==实参的值;
3、形参x(亦局部变量)的值在函数体内通体有效,值到达函数体内部了,代码块中第一个console.log(x)都是反馈刚刚拿到的形参x的值;
4、函数内部变量x一律将形参x的值覆盖,给arguments[0]赋值,内部变量x的值也相应变化
4、函数调用
函数调用会免费赠送两个额外的参数:this和arguments。函数调用有四种方式:
1、方法调用
先定义一个对象,然后在对象的属性中定义方法,通过对象来调用方法。this指向object
var object = {
value:0,
showValue:function(){
alert(this.value);
}
}
object.showValue();
2、函数调用 即函数定义之后的调用(函数声明和函数表达式两种),this指向全局变量
3、构造器调用
先定义一个函数对象,在对象中定义属性,在原型对象中定义方法。调用方法前先实例化函数对象,在进行调用。
var funcObject = function(name){
this.name = name;
}
funcObject.prototype = {
show:function(){
alert(this.name);
}
}
var newObject = new funcObject('huanhuan');
newObject.show();
4 、apply,call, bind调用
apply,call,bind都是函数对象的一个方法,作用是改变函数的调用对象。第一个参数是函数中this指向的对象。 a.call(b) 意思是仍然使用a的方法,只不过方法中的this指向b的。也可以说b继承了a的方法。
var myobject={};
var sum = function(a,b){
return a+b;
};
var sum2 = sum.call(myobject,10,30); //var sum2 = sum.apply(myobject,[10,30]);
alert(sum2);
call与apply的区别是apply接收的是数组参数。
同时我们知道,bind也可以改变this的指向。
bind()方法会创建一个新函数,称为绑定函数,当调用这个绑定函数时,绑定函数会以创建它时传入 bind()方法的第一个参数作为 this,传入 bind() 方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。
改变bar执行函数的this指向
var bar = function(){
console.log(this.x);
}
var foo = {
x:3
}
bar(); // undefined
var func = bar.bind(foo);
func(); // 3
那么,什么时候用bind,什么时候用apply,call呢?我们来个对比:
var obj = {
x: 81,
};
var foo = {
getX: function() {
return this.x;
}
}
console.log(foo.getX.bind(obj)()); //81
console.log(foo.getX.call(obj)); //81
console.log(foo.getX.apply(obj)); //81
三个输出的都是81,但是注意看使用 bind() 方法的,他后面多了对括号。
也就是说,区别是,当你希望改变上下文环境之后并非立即执行,而是回调执行的时候,使用 bind() 方法。而 apply/call 则会立即执行函数。
5、闭包
js闭包之前有文档记录过,可以去看。
####6、JS变量提升与函数提升
请注意变量赋值并没有被提升,只是声明被提升了
1、变量提升
var bar=1;
function test(){
console.log(bar); //undeifned
var bar=2;
console.log(bar); //2
}
test();
这就是JavaScript的变量提升,虽然变量bar的定义在后面,不过浏览器在解析的时候, 会把变量的定义放到最前面,相当于下面:
var bar=1;
function test(){
var bar;
console.log(bar); //undefined
bar=2;
console.log(bar); //2
}
请注意变量赋值并没有被提升,只是声明被提升了。
2、函数提升
function test() {
foo(); // TypeError "foo is not a function"
bar(); // "this will run!"
var foo = function () { // 变量指向函数表达式
alert("this won't run!");
}
function bar() { // 函数声明 函数名为bar
alert("this will run!");
}
}
test();
函数提升和变量提升不同,函数体也会一同被提升。但是请注意,函数的声明有两种方式,只有函数式的声明才会连同函数体一起被提升。foo的声明会被提升,但是它指向的函数体只会在执行的时候才被赋值。
3. eg
var a = 1;
function b() {
a = 10;
return;
function a() {}
}
b();
alert(a);
相当于:
var a = 1;
function b() {
function a() {} //相当于这里定义一个局部变量a
a = 10;
return;
alert(a);
}
b();
alert(a);
现在可以看到局部变量的a是10,外部的是1;
4.作用域
var x = 1;
if (x) {
var x = 2;
console.log(x); // 2
}
console.log(x); // 2
因为javascript是函数作用域。这是和c家族语言最大的不同。该程序里面的if并不会创建新的作用域 为了实现本来的结果,我们使用闭包来解决。
var x = 1;
if (x) {
(function () {
var x = 2;
console.log(x); // 2
}());
}
console.log(x); // 1
7、回调函数
一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数。
#####1.eg1
function a(callback) { callback(); }
function b() { console.log(‘hello callback’); }
a(b);
#####2.eg2
function print(){ console.log(‘callback’); } setTimeout(print,1000);
####8、函数关键字