一、定义函数
- function abs(x){ }
function abs(x) {
if (x >= 0) {
return x;
} else {
return -x;
}
}
2.var abs = function (x){ } ;
var abs = function (x) {
if (x >= 0) {
return x;
} else {
return -x;
}
};
在这种方式下,function (x) { ... }是一个匿名函数,它没有函数名。但是,这个匿名函数赋值给了变量abs,所以,通过变量abs就可以调用该函数。
上述两种定义完全等价,注意第二种方式按照完整语法需要在函数体末尾加一个;,表示赋值语句结束。
- 创建立即调用的函数
(function (x) {
return x * x;
})(3);
二、调用函数
- 调用函数时,按顺序传入参数即可,可传入任意个数参数而不报错
- arguments 关键字(装传入参数的数组)
function foo(x) {
console.log('x = ' + x); // 10
for (var i=0; i<arguments.length; i++) {
console.log('arg ' + i + ' = ' + arguments[i]); // 10, 20, 30
}
}
foo(10, 20, 30);
x = 10
arg 0 = 10
arg 1 = 20
arg 2 = 30
只在函数内部起作用,并且永远指向当前函数的调用者传入的所有参数。arguments类似Array但它不是一个Array:
- 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 []
三、变量作用域
-
如果一个变量在函数体内部申明,则该变量的作用域为整个函数体,在函数体外不可引用该变量:
-
如果两个不同的函数各自申明了同一个变量,那么该变量只在各自的函数体内起作用。
-
JavaScript的函数可以嵌套,此时,内部函数可以访问外部函数定义的变量,反过来则不行:
-
JavaScript的函数在查找变量时从自身函数定义开始,从“内”向“外”查找。如果内部函数定义了与外部函数重名的变量,则内部函数的变量将“屏蔽”外部函数的变量。
-
变量提升
JavaScript的函数定义有个特点,它会先扫描整个函数体的语句,把所有申明的变量“提升”到函数顶部:
由于JavaScript的这一怪异的“特性”,我们在函数内部定义变量时,请将所有变量定义在最前面。最常见的做法是用一个var申明函数内部用到的所有变量:
function foo() {
var x = 'Hello, ' + y;
console.log(x);
var y = 'Bob';
}
foo();
//Hello, undefined
由于变量提升JavaScript引擎看到的代码相当于:
function foo() {
var y; // 提升变量y的申明,此时y为undefined
var x = 'Hello, ' + y;
console.log(x);
y = 'Bob';
}
- 全局作用域(减少命名冲突)
不在任何函数内定义的变量就具有全局作用域。实际上,JavaScript默认有一个全局对象window,全局作用域的变量实际上被绑定到window的一个属性:因此,直接访问全局变量var a和访问window.a是完全一样的
全局变量会绑定到window上,不同的JavaScript文件如果使用了相同的全局变量,或者定义了相同名字的顶层函数,都会造成命名冲突,并且很难被发现。
减少冲突的一个方法是把自己的所有变量和函数全部绑定到一个全局变量中。例如:
// 唯一的全局变量MYAPP:
var MYAPP = {};
// 其他变量:
MYAPP.name = 'myapp';
MYAPP.version = 1.0;
// 其他函数:
MYAPP.foo = function () {
return 'foo';
};
把自己的代码全部放入唯一的名字空间MYAPP中,会大大减少全局变量冲突的可能。
许多著名的JavaScript库都采用这种方法:jQuery,YUI,underscore等等。
- 局部作用域(用let定义变量)
由于JavaScript的变量作用域实际上是函数内部,我们在for循环等语句块中是无法定义具有局部作用域的变量的
为了解决块级作用域,ES6引入了新的关键字let,用let替代var可以申明一个块级作用域的变量:
function foo() {
for (var i=0; i<100; i++) {
//
}
i += 100; // 仍然可以引用变量i
}
function foo() {
var sum = 0;
for (let i=0; i<100; i++) {
sum += i;
}
//不可引用变量i
}
- 解构赋值
1.一次为多个变量赋值
var [x, y, z] = ['hello', 'JavaScript', 'ES6'];
let [x, [y, z]] = ['hello', ['JavaScript', 'ES6']];
let [, , z] = ['hello', 'JavaScript', 'ES6'];
// 忽略前两个元素,只对z赋值第三个元素
2.对一个对象进行解构赋值时,同样可以直接对嵌套的对象属性进行赋值,只要保证对应的层次是一致的:
var person = {
name: '小明',
age: 20,
gender: 'male',
passport: 'G-12345678',
school: 'No.4 middle school',
address: {
city: 'Beijing',
street: 'No.1 Road',
zipcode: '100001'
}
};
var {name, address: {city, zip}} = person;
name; // '小明'
city; // 'Beijing'
zip; // undefined, 因为属性名是zipcode而不是zip
// 注意: address不是变量,而是为了让city和zip获得嵌套的address对象的属性:
address; // Uncaught ReferenceError: address is not defined
3.交换数值
var x=1, y=2;
[x, y] = [y, x]
四、方法
- 定义方法
在一个对象中绑定函数,称为这个对象的方法
var xiaoming = {
name: '小明',
birth: 1990,
age: function () {
var y = new Date().getFullYear();
return y - this.birth;
}
};
- this指向关系
1.如果以对象的方法形式调用,比如xiaoming.age(),该函数的this指向被调用的对象,也就是xiaoming,这是符合我们预期的。
2.如果单独调用函数,比如getAge(),此时,该函数的this指向全局对象,也就是window
3.在函数内部定义的函数,this又指向undefined了!(在非strict模式下,它重新指向全局对象window!)
用var that = this;,你就可以放心地在方法内部定义其他函数,而不是把所有语句都堆到一个方法中。
var xiaoming = {
name: '小明',
birth: 1990,
age: function () {
var that = this; // 在方法内部一开始就捕获this
function getAgeFromBirth() {
var y = new Date().getFullYear();
return y - that.birth; // 用that而不是this
}
return getAgeFromBirth();
}
};
- apply函数(指定this指向)
它接收两个参数,第一个参数就是需要绑定的this变量,第二个参数是Array,表示函数传入的参数。
getAge.apply(xiaoming, []); // 25, this指向xiaoming, 参数为空
也可以改变函数行为
var count = 0;
var oldParseInt = parseInt; // 保存原函数(转成int类型)
window.parseInt = function () {
count += 1; //计数器
return oldParseInt.apply(null, arguments); // 调用原函数
};
// 测试:
parseInt('10');
parseInt('20');
parseInt('30');
console.log('count = ' + count); // 3