layout: post title: JavaScript函数 category: Javascript tags: [JavaScript] —

函数定义


函数名只能包含字母、数字、下划线或$,且不能以数字开头。

创建函数,可用函数定义表达式或者函数声明语句。

var f = function fact(x){}

函数定义表达式包含名称,名称将作为函数的局部变量,在函数内部使用,代指函数。

函数声明语句不是真正的语句,不能出现在循环、条件、try/catch/finally以及with语句中;

声明语句置于在不会执行到的位置,仍可在整个作用域可访问,可在被定义代码之前使用。

定义表达式的变量声明被提前了,但是对变量赋值不会提前,函数在被定义之前无法使用,否则调用时会出现错误:

"TypeError: undefined is not a function"

return 后没表达式,则返回undefined值;

函数不包含return语句,那它只执行函数体内语句,并返回undefined给调用者;

没有返回值的函数,有时称为过程。

函数的执行


函数执行的几种方式,当作函数、方法、构造函数、间接调用

如果构造函数没有形参,可以省略实参列表和圆括号

构造函数返回值情况

function MyClass(name) {
  this.name = name;
  return name;  // 构造函数的返回值?
}

var obj1 = new MyClass('foo');
var obj2 = MyClass('foo');
var obj3 = new MyClass({});
var obj4 = MyClass({});
obj1 = MyClass对象
obj2 = 'foo'
obj3 = {}
obj4 = {}

点击链接,查看结果

函数的形参和实参


执行函数时,没传入值的形参,默认值是undefined的,有人利用这个,隐式定义函数内部的局部变量

调用函数间隔传实参时,可用null或undefined占位符替代空白的参数

函数中实参传入的是引用,函数内部对其操作,对外部是可见的

实参对象arguments

实参对象是个类数组对象, arguments.callee递归调用 如果你把参数命名为arguments,那么这个参数就会覆盖它原有的特殊变量。

函数属性、方法


  • length属性,函数形参的个数

  • prototype属性,实现引用类型定义和继承

  • name 函数名字,匿名函数为空,比如通过var fun = function (){}声明的表达式。

  • _proto_,内部属性,函数也是对象,也有继承的原型
  • call和apply在特定的作用域中执行函数,这里实际设定函数执行的this。apply 的第二个参数是数组或类数组对象
  • toString toLocalString indexOf都是返回函数代码,不同的浏览器表现出不同的效果,比如删除注释,格式化代码。

  • bind方法 ES5中新增,将函数“绑定至”对象并传入一部分参数,传入的实参放在完整实参列表的左侧 这个例子考虑到bind返回的函数被当成构造函数使用情况

      /**
       * ES5.0支持bind函数,ES3.0不支持bind,这里在ES3中模拟实现,能应用于大部分场景,
       * https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind
       */
      Function.prototype.bind || (Function.prototype.bind = function (that) {
          var target = this;
    	
          // If IsCallable(func) is false, throw a TypeError exception.
          // 通过call、apply调用时,校验传入的上下文
          if (typeof target !== 'function') {
              throw new TypeError('Bind must be called on a function');
          }
    	
          var boundArgs = slice.call(arguments, 1);
    	
          function bound() {
              // 返回的bind函数被当构造函数
              if (this instanceof bound) {
                  var self = createObject(target.prototype);
                  var result = target.apply(
                      self,
                      boundArgs.concat(slice.call(arguments)));
                  // Object(result) === result 判断调用返回是不是对象
                  return Object(result) === result ? result : self;
              }
              // 返回的bind函数以一般函数形式调用
              else {
                  return target.apply(
                      that,
                      boundArgs.concat(slice.call(arguments)));
              }
          }
          // NOTICE: The function.length is not writable.
          bound.length = Math.max(target.length - boundArgs.length, 0);
    	
          return bound;
    	
      });
    

函数的使用


作为命名空间的函数

暴露一些函数作为接口的话有如下几种方法

var mylib = (function(global,undefined) {
	function log(msg) {
	  console.log(msg);
	}

	log1 = log;  // 法一:定义不带var的变量,将引用暴露到外部

	global.log2 = log;  // 法二:直接在全局对象上添加log2属性,赋值为log函数(推荐)

	return log // 法三:返回函数值
	   
}(window));

自更新函数

function selfUpdate() {
  window.selfUpdate = function() {
    alert('second run!');
  };

  alert('first run!');
}

selfUpdate(); // first run!
selfUpdate(); // second run! 这种函数可以用于只运行一次的逻辑,在第一次运行之后就整个替换成一段新的逻辑

函数判断


严谨上讲,typeof 识别出函数可能有误,可执行对象,如alert、正则表达式不是函数

函数书写规范


  • 函数参数定义的几种注释

      /*,...*/
      function(fn /*, initial*/)
      getPropertyNames(o,/*optional*/a)
      arraycopy(/*array */ from,/*index*/from_start)
    
  • 优雅的风格

      // bad
      function a() {
          test();
          console.log('doing stuff..');
          //..other stuff..
          var name = getName();
          if (name === 'test') {
              return false;
          }
          return name;
      }
    	
      // good
      function a() {
          var name = getName();// 定义变量放在前面
          test();
          console.log('doing stuff..');
          //..other stuff..
          if (name === 'test') {
              return false;
          }
          return name;
      }
    	
      // bad
      function a() {
          var name = getName();
          if (!arguments.length) {
              return false;
          }
          return true;
      }
    	
      // good
      function a() {
          if (!arguments.length) {// 参数校验放在前面
              return false;
          }
          var name = getName();
          return true;
      }
    

函数式编程


  • 高阶函数 如果函数作为参数或返回值使用时,就称为高阶函数

  • 不完全函数

  • 具有记忆功能的函数

      /**
       *  记忆函数
       */
      function memorize(f) {
          var cache = {};
          return function () {
              var key = arguments.length +''+ Array.prototype.join.call(arguments, ',');
              if (!(key in cache))
                  cache[key] = f.apply(null, arguments);
              return cache[key];
          }
      }
    
分享到: