函数、作用域、闭包、this指向问题

函数

函数声明: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);

你可能感兴趣的:(函数、作用域、闭包、this指向问题)