函数
函数声明:function fnName(){};
使用function关键字声明一个函数,在指定一个函数名。
- 函数声明后不能直接跟括号,会报错
function fnName(){
alert('Hello World');
}();
函数表达式:var fnName = function(){};
使用function关键字声明一个匿名函数并赋予给一个变量。
- 函数表达式后面可以直接加括号,当JavaScript引擎解析到此处时能立即调用函数
var fnName = function(){
alert('Hello World');
}();
匿名函数:function(){};
匿名函数属于函数表达式,可用于赋予一个变量创建函数,赋予一个事件成为事件处理程序,或创建闭包等。
特殊情况
以下两种情况下,函数名为只读状态无法修改(在严格模式下修改会报错),且在函数外无法访问。
var b = 10;
(function b() {
b = 20;
console.log(b);//function b
})()
var a = function b(){
b = 10;
console.log(b);
}
a();//function b
b();//b is not defined
作用域
- 函数中新声明的变量均为局部变量
- while{...}、 if{...}、for(...){}、for(){...} 之内仅是代码块,而非局部作用域,因此其中声明的变量均为全局变量
-
let
命令声明的变量只有在该变量所在的代码块内生效
var result = [];
for (var i = 0; i<10; i++){
result[i] = function () {
console.info(i)
}
}
console.log(i);//10
result[0]();//10
var array = [];
for (var i = 0; i < 3; i++) {
// 三个箭头函数体中的每个'i'都指向相同的绑定'3'。
array.push(() => i);
}
var newArray = array.map(el => el());
console.log(newArray); // [3, 3, 3]
使用let或闭包即可变为记录每一次的值
let array = [];
for (var i = 0; i < 3; i++) {
array[i] = (function(x) {
return function() {
return x;
};
})(i);
}
const newArray = array.map(el => el());
console.log(newArray); // [0, 1, 2]
if的括号()中声明的是局部变量,无法被外部获取
if (function fn(){
console.log(123)
}){
fn()//报错
}
闭包
闭包指的是:能够访问另一个函数作用域的变量的函数。(即在一个函数中声明的另一个函数)
闭包会携带包含它的函数的作用域,该作用域只有当闭包被销毁时,才会销毁。
function generator(input) {
var index = 0;
return {
next: function() {
if (index < input.length) {
index += 1;
return input[index - 1];
}
return "";
}
}
}
var mygenerator = generator("boomerang");
mygenerator.next(); // returns "b"
mygenerator.next() // returns "o"
mygenerator = generator("toon");
mygenerator.next(); // returns "t"
内存泄露问题
function showId() {
var el = document.getElementById("app")
el.onclick = function(){
aler(el.id) // 这样会导致闭包引用外层的el,当执行完showId后,el无法释放
}
}
// 改成下面
function showId() {
var el = document.getElementById("app")
var id = el.id
el.onclick = function(){
aler(id) // 这样会导致闭包引用外层的el,当执行完showId后,el无法释放
}
el = null // 主动释放el
}
this指向
- this指向其直接调用者
var obj={};
obj.a={};
obj.a.b=function(){
console.log(this)
}
obj.a.b()//a
- 当函数作为参数使用arguments[0]()调用时,this指向arguments
var length = 10;
function fn() {
console.log(this.length);
}
var obj = {
length: 5,
method: function(fn) {
arguments[0](); //2
fn(); //10 此时fn没有调用者
console.log(fn===arguments[0]) //true
}
};
obj.method(fn,1);
- 匿名函数的 this 总是指向 window , 通过
arguments.callee()
可以实现匿名函数的递归,此时 this 指向 arguments
var num = 10;
function A() {
this.num = 20;
this.say = function () {
var num = 30;
return function () {
console.log(this,this.num--)
if(this.num>=9){
arguments.callee();
}
}
}
}
var a = new A();
console.log(a.num);//20
a.say()();//Window 10
//arguments NaN
- 当var a=obj.fn,直接调用a()时其中this不再指向obj而指向window
var baz=123;
var foo = {
bar: function () {
return this.baz;
},
baz: 1
};
var aaa=foo.bar;
(function (a) {
console.log(a());
})(foo.bar);//123
console.log(foo.bar())//1
console.log(aaa())//123
- 当构造函数存在return时,若返回值为对象/函数,则new获取的为该返回值,原有的this无效
function fn()
{
this.name= '小明';
return {name:'大明'};
}
var obj = new fn;
console.log(obj.name); //大明
- 箭头函数的this是在定义函数时绑定的,不是在执行过程中绑定的。因此this指向函数定义时所处的作用域。
改变this指向
call和apply : 改变this指向并立即执行。第一个参数为null时,指向window
var a = {
user:"追梦子",
fn:function(e,ee){
console.log(this.user); //追梦子
console.log(e+ee); //3
}
}
var b = a.fn;
b.call(a,1,2);//若没有call,则this指向window
b.apply(a,[10,1]);
bind : 返回一个修改后的函数,而不是直接将其执行。参数可以按照形参顺序分别添加
var a = {
user:"追梦子",
fn:function(e,d,f){
console.log(this.user); //追梦子
console.log(e,d,f); //10 1 2
}
}
var b = a.fn;
var c = b.bind(a,10);
c(1,2);