[js] 函数 闭包

函数声明与函数表达式
函数声明会自动提升(function declaration hoisting)

test();
function test() {console.log('for fun')} // for fun
test();
var test = function() {} // error

if(condition){
  function sayHi(){}
} else {
  function sayHi(){}
} // 无效语法,不同引擎会采取不同方式返回第一或第二个声明
var sayHi;
if (condition) {
  sayHi = function (){}
} else {
  sayHi = function (){}
} // 正常

函数参数
函数在调用时会获取arguments变量,和参数对应
两者并不访问相同的内存空间,但值会同步
agruments对象有一个callee属性,指向拥有该对象的函数

function a() {
  console.log(arguments.callee == a);
}
a(); // true

所有的值类型都通过值类型传递不通过引用传递

function doo(num1, num2) {} // arguments[0] 和 num1 对应

函数属性

caller保存调用该函数的函数的引用,若是全局变量返回null

function a() {
  b();
}
function b() {
  console.log(arguments.callee.caller);
}

a(); // function a() { b(); }
b(); // null

length表示函数希望接受的命名参数
arguments见下

函数调用
函数在调用时会自动获取this和arguments两个特殊变量
函数调用都是通过值传递,即使引用类型也是通过值传递

// professional js for web developers p71
function setName(obj) {
    obj.name = 'N'; // obj是一个内存中对象的引用,指向内存
    obj = {}; // obj是值传递,因此不会造成原来的指针变化
    obj.name = 'M';
}
var p = {};
setName(p);
p.name; // N

函数调用有四种方式,不同调用方法this指向不同
即使在不同环境中执行(传递的参数,如this值不同),指向的仍是同一个函数

function a(){}
var x = {
    b: function(){}
}
function NewObj(){}

x.b() // 方法调用(The Method Invocation Pattern) this == x
a() // 函数调用(The Function Invacation Pattern) this == window(this指向全局变量)
var Obj = new NewObj() // 构造器调用(The Constructor Invocation Pattern) this == Obj
a.apply(obj, arguments) // apply调用(The Apply Invocation Pattern) this == obj

arguments并不是真正的数组,arguments有length属性,但没有任何数组的方法

函数返回值

匿名函数(lambda)

闭包
指有权访问另一个函数作用于衷的变量的函数
不合理的使用闭包会造成内存泄漏

function outter(property) {
  return function() {
    alert(property);
  }
}
var def = outter('just for fun');
def();
def = null; // 解除对匿名函数的引用以释放内存

function domHandle(){
    var ele = document.getElementById('a');
    ele.onclick = function(){
        alert(ele.id); // 循环引用造成无法销毁ele
    }
}

function domHandle(){
    var ele = document.getElementById('a');
    var id = ele.id; // 消除循环引用
    ele.onclick = function(){
        alert(id);
    }
    ele = null; // 闭包保存的是外部函数所有的对象,所以要将ele设为null解除对DOM对象的引用
}

闭包通过作用域链获取数据,因此保存的是整个变量对象而非特定值

function create() {
  var result = [];

  for (var i = 0; i < 10; i++) {
    result[i] = function() {
      return i; // 在闭包调用的时候保存的i并非每次的值,而是最后的值10
    }
  }

  return result;
}

闭包的调用和声明时间不一致,因此在闭包中this值可能会发生改变

块级作用域
创建并立即调用一个函数,块级作用域中的变量会被从内存中立即销毁

(function(){
    // 块级作用域
})();

私有变量和特权方法
在函数中定义私有变量,通过闭包返回特权方法访问私有变量

//1
function MyObject(){
    var privateVariable = 10; // 私有变量
    function privateFunction(){} // 私有方法

    this.publicMethod = function(){ // 特权方法
        privateVariable ++;
        return privateFunction();
    }
}
var obj = new MyObject();
obj.publicMethdo(); // 通过特权方法访问私有变量和方法

//2
(function(){
    var privateVariable = 10; // 私有变量
    function privateFunction(){} // 私有方法

    MyObject = function(){ // 构造函数
    };
    MyObject.prototype.publicMethod = function(){ // 特权方法
        privateVariable ++;
        return privateFunction();
    }
})();
var obj = new MyObject();
obj.publicMethdo(); // 通过特权方法访问私有变量和方法

//3
(function() {
  var pub_pri_var = 0;
  function pub_pri_fun () {
    return pub_pri_var;
  }

  MyObj = function (name) {
    this.set = function (val) {
      name = val;
    }
    this.get = function () {
      return name;
    }
  }
  MyObj.prototype.pub_pub_fun = function () {
    pub_pri_var ++;
    return pub_pri_fun();
  };
})();
var a = new MyObj('a\'s name');
var b = new MyObj('b\'s name');
  • 第一种方法,不同的实例有自己的变量及方法
  • 第二种方法,所有实例享有同样的私有变量和私有方法
  • 第三种方法,所有实例有共同的方法和变量,也有私有的方法和变量
  • 使用私有变量会多增加一层作用域链,因此造成查找速度减慢

模块(Module)
利用闭包和私有变量构造模块

// <js: the good parts> p41
var serial_maker = function () { // 一个序列号生成函数
    var prefix = '';
    var seq = 0;
    return {
        set_prefix: function(p) {
            prefix = p;
        },
        gensym: function() {
            var result = prefix + seq;
            seq ++;
            return result;
        }
    }
}

var seqer = serial_maker();
seqer.set_prefix('Q');
var unique = seqer.gensym(); // Q1

通常可和单例共用,为单例创建私有变量和特权方法

var singleton = function () {
  var  privateVariable;
  function privateFunction() {}

  var obj = {}
  obj.publicProperty;
  obj.publicMethod = function () {}

  return obj;
} ();

级联
让方法返回this

var MyString = function (fir, sec) {
  this.first = fir;
  this.second = sec;
}
MyString.prototype.setF = function (val) {
  this.first = val;
  return this;
};
MyString.prototype.setS = function (val) {
  this.second = val;
  return this;
};
MyString.prototype.toString = function () {
  return this.first + ' ' + this.second;
};

var obj = new MyString('a', 'b');
obj.toString(); // 'a b'
obj.setF('aa').setS('bb').toString(); // 'aa bb'

柯里化(Currying)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值