函数和作用域
JavaScript函数是指一个特定代码块,可能包含多条语句,可以通过名字来供其它语句调用以执行函数包含的代码语句。
比如我们有一个特定的功能需要三条语句实现
statement1;
statement2;
statement3;
那么每次想实现这个功能的时候就需要写这三句话,很麻烦,我们可以把这三条语句打包为一个函数
function doSomething(){
statement1;
statement2;
statement3;
}
这样每次想实现功能的时候我们就调用一下函数就可以了,调用函数通过函数名称()
的形式调用
doSomething();
声明函数
ECMAScript规定了三种声明函数方式
构造函数
首先函数也是对象的一种,我们可以通过其构造函数,使用new
来创建一个函数对象
var printName = new Function("console.log('Byron');");
不推荐使用
函数声明
function functionName(){
statement;
}
使用function
关键字可以声明一个函数,看个例子
function printName(){
console.log('Byron');
}
printName();
函数表达式
var printName = function(){
console.log('Byron');
};
参数
上面定义的函数有一个缺陷,就是过于僵化,只能打印Byron
,当我们希望打印Casper
的时候就需要一个新的函数,这也是很复杂的,我们可以通过参数的方式来让函数能灵活一些
function printName(name){
console.log(name);
}
printName('Byron');
printName('Casper);
可以看出我们在定义函数的时候在括号内写了个name
做占位符,也就是在定义函数的时候也不知道要输出什么,用个占位符代替,在函数实际执行的时候,用真实的值替代占位符
我们说的占位符在术语上叫做形参
,也就是形式参数,而在调用函数的时候传入的值叫做实参
,也就是实际的参数
函数在定义的时候可以写多个参数
function printPersonInfo(name, age, sex){
console.log(name);
console.log(age);
console.log(sex);
}
auguments
在函数内部,你可以使用arguments
对象获取到该函数的所有传入参数
function printPersonInfo(name, age, sex){
console.log(name);
console.log(age);
console.log(sex);
console.log(arguments);
}
重载
重载是很多面向对象语言实现多态的手段之一,在静态语言中确定一个函数的手段是靠方法签名——函数名+参数列表,也就是说相同名字的函数参数个数不同或者顺序不同都被认为是不同的函数,称为函数重载
在JavaScript中没有函数重载的概念,函数通过名字确定唯一性,参数不同也被认为是相同的函数,后面的覆盖前面的,这是不是意味着JavaScript不能通过重载功能实现一个函数,参数不同功能不同呢?
在JavaScript中,函数调用没必要把所有参数都传入,只要你函数体内做好处理就行,但前提是传的参数永远被当做前几个
function printPeopleInfo(name, age, sex){
if(name){
console.log(name);
}
if(age){
console.log(age);
}
if(sex){
console.log(sex);
}
}
printPeopleInfo('Byron', 26);
printPeopleInfo('Byron', 26, 'male');
返回值
有时候我们希望在函数执行后给我们一个反馈,就像表达式一样,给我们个结果,我们可以通过return
来实现
function fn(a, b){
a++;
b++;
return a + b;
}
var result = fn(2, 3);
conslole.log(result);
这样我们就能拿到函数希望给我的反馈了,调用return后,函数立即中断并返回结果,即使后面还有语句也不再执行
其实我们不写return语句,函数也会默认给我们返回undefined
声明提前
和变量的声明会前置一样,函数声明同样会前置,如果我们使用函数表达式那么规则和变量一样
console.log(fn); //undefined
var fn = function(){}
如果我们使用函数声明的方式,那么即使函数写在最后也可以在前面语句调用,前提是函数声明部分已经被下载到本地
fn(); // "1"
function fn(){
console.log('1');
}
命名冲突
当在同一个作用域内定义了名字相同的变量和方法的话,无论其顺序如何,变量的赋值会覆盖方法的赋值
var fn = 3;
function fn(){}
console.log(fn); // 3
当函数执行有命名冲突的时候,函数执行时载入顺序是 变量、函数、参数
看个例子
function fn(fn){
console.log(fn);
var fn = 3;
console.log(fn);
}
fn(10); //10 3
道理我们懂了,但是看个例子
var fn = 1;
function fn(fn){
console.log(fn);
}
console.log(fn); // 1,竟然不是说好的函数
作用域
在大多数语言中都是用花括号{}
来形成一个作用域,俗称块作用域,举个C语言的例子
{
int a = 2;
}
a++; //报错,a不存在
这也是我们大多数人能接受的理解方式,但是在JavaScript中{}
并没有带来块作用域,JavaScript的作用域是靠函数来形成的,也就是说一个函数内定义的变量函数外不可以访问
function fn(){
var a =1;
if(a > 2){
var b = 3;
}
console.log(b);
}
fn(); // undefined
console.log(a); // "ReferenceError: a is not defined
var
在声明变量的时候需要var
,这样声明的变量作用域才符合我们上面提到的规则,那么不写var会怎么样呢
function fn(){
a = 1;
}
fn();
console.log(a); // 1
可以看到不写var会声明一个全局的变量,这是我们在编程中应该要避免的,即使真的需要全局变量,也应该在最外层作用域使用var声明
Function.prototype.bind
ES5 为Function新增bind
方法
Function.prototype.bind返回一个把内部this设为thisArg的方法,读起来很绕,其实就是,返回一个新方法,这个方法内部的this是参数thisArg
// DOM
<div id="test">Click Here</div>
// js
var handler = {
message: 'This is a test',
click: function () {
alert(this.message);
}
};
document.getElementById('test').onclick = handler.click;
如果这样绑定div的click事件处理程序,大家都会看出来,点击的时候弹出来的对话框内容是undefined,因为执行的时候this是window,我们需要一定的技巧才可以处理此事,达到预期效果,但是使用新添的bind我们可以轻松改变this
document.getElementById('test').onclick = handler.click.bind(handler);
这样就可以达到我们预期效果了。