本文介绍Javascript中函数相关知识。
函数声明和表达式
前面介绍的函数是用函数声明方式
function walk(){
// …
}
还可以用表达式,注意表达式方式后面有分号。
let walk = function () {
// …
};
调用:
walk();
提升 Hoisting
函数声明可以在声明之前调用,比如:
walk();
function walk() {
// …
}
而表达式函数则不可以在定义之前调用。
原因:
javascript引擎会在运行时将所有声明的函数先提到最前面(Hoisting)。
参数
Javascript是动态函数,不一定要输入与形参一样的参数个数。
function sum(a, b) {
return a + b;
}
// 调用
console.log(sum(1, 2)); //3
console.log(sum(1));
// 输出 NaN,相当于1+undefined
console.log(sum());
// 输出 NaN,相当于undefined+undefined
函数可以获取 arguments数组,该数组存着所有参数。
function sum() {
let total = 0;
for (let value of arguments) total += value;
return total;
}
// 然后调用
console.log(sum(1, 2, 3, 4, 5));
// 输出 15
余下操作符
余下操作符是三个点 …
与拆分操作符的标识一样,现代javascript才有。
function sum(…args) {
// … 是余下操作符,表示将参数放数组args中
return args.reduce((a, b) => a + b);
// 累加
}
console.log(sum(1, 2, 3, 4, 5, 10));
// 输出 25
余下操作符参数必须是最后一个参数。
function sum(discount,…price,someValue){
// 不最后一个将出错
//一些操作
}
sum(0.1,20,30,1);
// 提示出错:Rest parameter must be last formal parameter
余下操作符 有用的一个例子:购物车打折
function sum(discount, …price) {
const total = price.reduce((a, b) => a + b);
return total * (1 – discount);
}
// 调用:
console.log(sum(0.1, 20, 30));
// 输出 45
参数默认值
参数默认值在ES6及之后支持。
function interest(princal,rate=3.5,years=5){
// 3.5和5是默认值
return princal*rate/100*years;
}
interest(10000);
在ES6之前,可以用逻辑操作符代替。
function interest(princal, rate, years) {
rate = rate || 3.5;
years = years || 5;
return ((princal * rate) / 100) * years;
}
存取器
用特殊的方法get、set来设置属性
const person = {
firstName: “chen”,
lastName: “yongping”,
// 定义一个属性fullName,设置get和set
get fullName() {
return `${person.firstName} ${person.lastName}`;
},
set fullName(value) {
const parts = value.split(” “);
this.firstName = parts[0];
this.lastName = parts[1];
},
};
// 下面调用试试
person.fullName = “John Smith”;
//调用set,注意 fullName是没有小括号的。
实际上,使用Object的方法也可以来定义属性。
Object.defineProperty(this, “fullName”, {
get: function () {},
set: function () {},
});
Try和Catch
直接看代码:
try {
if (true) {
// 检查某条件
throw new Error(“some error”);
// 抛出错误
}
} catch (e) {
// 捕获处理
console.log(e);
}
局部与全局作用域
局部变量只能在代码块内可见,如在 {}内。
而在代码文件中的变量是全局变量,全局可见。
当局部变量与全局变量一样时,局部覆盖全局变量。
Let和Var
var 的缺点:变量的作用域在它的函数内,比如if语句、循环语句中的变量,在代码块外也能访问。
function start() {
for (let i = 0; i < 5; i++) console.log(i);
console.log(i);
// 这句将出错,因为 i 只在for循环作用域
// 但如果将let改成var,将输出 5
}
start();
在ES6或者说是ES2015之前,只能使用var;
ES6后,出现了let 和 const等关键字。
- var。属于函数作用域 function-scope。
- let,const。属于块作用域 block-scope。
var还在一些问题,如:
var color=’red’;
color会被添加到window的属性。window.color 就是 ‘red’
总之,避免使用var。
this关键字
this引用了执行当前函数的对象实例。
- 方法method中,this指向对象。
- 函数function中,this指向全局,比如window,global。
- 构造函数中,this指向新建的空对象,new {}。
来看个初涉javascript用户觉得奇怪的例子:
const video = {
title: “a”,
tags: [“a”, “b”, “c”],
showTags() {
this.tags.forEach(function (tag) {
console.log(this.title, tag);
// 这个this实际指向了全局 window,因为在调用函数中,不属这个对象的方法
});
},
};
video.showTags();
// 输出 undefined ‘a’…,而不是 ‘a’ ‘a’,奇怪吧
javascript的this问题,是每个程序员必须了解的。
改变this的值
在代码块中可以事先保存this,后面再使用。以避免奇怪事情发生。以前面的例子,可以先将this保存到变量self。
…
showTags() {
const self = this;
self.tags.forEach(function (tag) {
console.log(self.title, tag);
// 这个this实际指向了全局 window,因为在调用函数中,不属这个对象的方法
});
},
…
这样就能输出正确的结果,不会指向window了。
还有其他一些方案:
可以用call,apply,bind传递this。
看下面代码:
function playVideo() {
console.log(this);
}
// 直接执行 playVideo,将显示window对象,
playVideo();
// 可以通过call,apply,build等方法执行
playVideo.call({ name: “kelemi” }, 1, 2);
playVideo.apply({ name: “kelemi” }, [1, 2]);
playVideo.bind({ name: “kelemi” })();
call和apply方法的不同在于call需一个一个传递,而apply可以将参数放数组里传。
如果将foreach里回调函数 function() 改成箭头函数 =>,那么这里的this自动继承前面的对象了。
…
showTags() {
this.tags.forEach((tag) => {
// 改成箭头函数,this就正常指向了
console.log(this.title, tag);
});
},
…
小结
本文介绍了Javascript中函数的相关知识,尤其是javascript中this的相关要点必须要了解。下一篇计划介绍javascript的面向对象编程。