在JavaScript中,用var声明的变量实际上是有作用域的。如果一个变量在函数体内部申明,则该变量的作用域为整个函数体,在函数体外不可引用该变量。如果两个不同的函数各自申明了同一个变量,那么该变量只在各自的函数体内起作用。换句话说,不同函数内部的同名变量各自独立,互不影响:
'use strict';
function foo() {
var x = 1;
x = x + 1;
}
function bar() {
var x = 'A';
x = x + 'B';
}
由于JavaScript的函数可以嵌套,此时,内部函数可以访问外部函数定义的变量,反过来则不行:
'use strict';
function foo() {
var x = 1;
function bar() {
var y = x + 1; // bar可以访问foo的变量x!
}
var z = y + 1; // ReferenceError! foo不可以访问bar的变量y!
}
不在任何函数内定义的变量就具有全局作用域,实际上,JavaScript默认有一个全局对象window,全局作用域的变量实际上被绑定到window的一个属性上。
由于JavaScript的变量作用域实际上是函数内部,我们在for循环等语句块中是无法定义具有局部作用域的变量的:
'use strict';
function foo() {
for (var i=0; i<100; i++) {
//
}
i += 100; // 仍然可以引用变量i
}
ES6引入了新的关键字let,用它来代替var可以申明一个块级作用域变量:
'use strict';
function foo() {
var sum = 0;
for (let i=0; i<100; i++) {
sum += i;
}
// SyntaxError:
i += 1;
}
(1) 函数的定义:JavaScript的函数是Function类的一个实例。函数名为引用类型变量,指向该函数对象。它有三种定义的方法:
方法1:定义全局变量
function sum(num1,num2){
return num1+num2;
}
方法2:字面量定义
var sum = function(num1,num2){
return num1+num2;
}
方法3:Function实例
var sayHi = new Function("sName","sMessage",//这两个是变量名
"alert('hello,'+sName+sMessage);");//这是函数体
sayHi("Zhang","come here!");
注:第一种方法定义了window对象的一个方法,也是一个变量。第二种和第三种方法直接把函数定义为一个变量
需要注意的是,函数体内部语句在执行时,一旦执行到return时,函数就执行完毕,并将结果返回。因此,函数内部通过条件判断和循环可以实现非常复杂的逻辑。如果没有return语句,函数执行完毕后,也会返回结果,只是结果为undefined。
因为JavaScript允许传入任意个参数而不影响调用,因此传入的参数比定义的参数多也没有问题,虽然函数内部并不需要这些参数,如此下面的例子:
function abs(x){
if(x >= 0){
return x;
}else{
return -x;
}
}
abs(10);//返回10
abs(-9);//返回9
abs();//返回NaN
abs(10,'jhjhdhd');//返回10
(2)arguments
JavaScript还有一个关键字arguments,它只在函数内部起作用,并且永远指向当前函数的调用者传入的所有参数。它类似于Array,但它不是一个Array。利用arguments,我们可以获得调用者传入的所有参数。也就是说,即使函数不定义任何参数,还是可以拿到参数的值。如下面例子:
function abs(){
if(arguments.length == 0){
return 0;
}
var x = arguments[0];//获得传入的参数里的第一个元素
return x >=0 ? x:-x;
}
实际上,arguments最常用于判断传入参数的个数,如下面的例子:
// foo(a[, b], c)
// 接收2~3个参数,b是可选参数,如果只传2个参数,b默认为null:
function foo(a,b,c){
if(arguments.length == 2){
// 实际拿到的参数是a和b,c为undefined
c = b; // 把b赋给c
b = null; // b变为默认值
}
}
要把中间的参数b变为“可选”参数,就只能通过arguments判断,然后重新调整参数并赋值。
(3)rest参数
下面给出一个例子:
function foo(a,b,...rest){
console.log('a=' + a);
console.log('b=' + b);
console.log(rest);
}
foo(1,2,3,4,5);
//结果:a = 1; b = 2; Array[3,4,5];
foo(1)
//结果:a = 1;b = undefined; Array[]
rest参数只能写在最后,前面使用“…”标识,从运行结果来看,传入的参数先绑定a、b,多余的参数以数组形式交给变量rest,如果传入的参数连正常定义的参数都没填满,那么rest参数会接收一个空数组。
下面是一个函数,它可以接收任意个参数并返回它们的和。
function sum(...rest) {
var sum = 0;
for(i = 0;ireturn sum;
}
定义:在一个对象中绑定函数,称为这个对象的方法。如下面的一个例子:
var xiaoming{
name: 'David',
birth: 1990,
age: function(){
var y = new Date().getFullYear();
return y-this.birth;
}
};
xiaoming.age; // function xiaoming.age()
xiaoming.age(); // 今年调用是25,明年调用就变成26了
上面的例子中,使用了this关键字,在一个方法内部,this是一个特殊变量,它始终指向当前对象,也就是xiaoming这个变量。所以,this.birth可以得到xiaoming的birth属性。如果JavaScript的函数内部调用了this,那么这个this到底指向谁,是视情况而定的。如下面的例子:
function getAge(){
var y = net Date().getFullYear();
return y-this.birth;
}
var xiaoming = {
birth: 1990,
age: getAge
};
//调用
xiaoming.age();//25 正常结果,这里的this指向小明
getAge();//NaN,这里的this指向window
虽然在一个独立的函数调用中,根据是否是strict模式,this指向undefined或window,不过,我们可以使用apply和call来控制this指向。
(1)apply
要指定函数的this指向哪个对象,可以用函数本身的apply方法,它接收两个参数,第一个参数就是需要绑定的this变量,第二个参数是Array,表示函数本身的参数。比如下面的例子:
function getAge(){
var y = new Date().getFUllYear();
return y-this.birth;
}
var xiaoming = {
birth:1990,
age:getAge
};
xiaoming.age();//25
getAge.apply(xiaoming,[]);//25,this指向xiaoming,getAge的参数为空,所以这里传的Array也为空。
(2)call
call()方法与apply()方法相似,唯一的区别是:apply()把参数打包成Array再传入,而call()是把参数按顺序传入。比如调用Math.max(3,4,5),分别用apply()和call()实现如下:
Math.max.apply(null,[3,4,5]);//5
Math.max.apply(null,3,4,5);//5
对于普通函数调用,我们通常把this绑定为null。