Function of Javascript

这篇博客只是给出了使用js中的函数的代码片段,并没有详解js中的函数,具体详解请参考《js权威指南》

1.1函数的定义

//输出object的每个属性的名称和值,返回undefined
function printprops(object) {
  for (var key in object) {
    console.log(key + ":" + object[key] + '\n');
  }
}

//计算两个笛卡尔坐标之间的距离
function distance(x1, y1, x2, y2) {
  var dx = x1 - x2;
  var dy = y1 - y2;
  return Math.sqrt(dx * dx + dy * dy);
}

//利用函数表达式定义一个函数
var square = function (x) {
    return Math.pow(x,x);
}

1.2当函数中没有return返回值的情况

  var object = {
    name: 'zane',
    age: 18
  };
  //定义一个函数
  function printprops(object) {
    for (var key in object) {
      console.log(key + ':' + object[key] + '\n');
    }
  }

  //1.直接调用函数
  printprops(object);


  //2.在控制台打印‘调用函数后’的结果
  //由于printprops没有return返回值,所以下面这条语句,先调用了printprops函数,
  //而后因为没有return返回值,故在控制台输出了undefined.
  //而在这里一定要搞清楚,是在控制台输出调用函数后的结果还是直接在控制台打印这个函数
  //目的不同,那么其结果也会不同。
  console.log(printprops(object));

  //3.打印函数,看是什么结果?
  //结果就是在控制台输出了这个函数的字符串表示,
  //字符串这种数据类型在控制台输出的时候,默认的字体颜色是黑色的。
  console.log(printprops);

2.1函数作为普通函数调用

 //下面这条语句是什么鬼
  //var strict = (function () {return !this;})();
  // var strict = (function () {return !this;}());
  //这两条语句的执行的结果是相同的,都输出了false;
  //在非严格模式下,this(又称为调用上下文)的值是全局对象,在严格模式下,其值是undefined
  //下面这条语句表示,在非严格模式下,this的值是全局对象,那么其boolean的值是true,取反之后便会返回false
  //若在严格模式下,this的值是undefined,其布尔值是false,取反之后是true,返回值便是true.
  //可以用下面这条语句来测试当前脚本运行的模式是否是严格模式,如果返回true,则为严格模式;返回false,则为非严格模式。
  var strict = (function () {return !this;})();

  console.log(strict);

2.2函数作为方法调用

 var o = {
    m: function () {
      var self = this;
      console.log(this === o); //在控制台输出true
      f(); //调用函数f ,在这里f是作为普通函数调用的

      function f() {
        console.log(this === o); //在控制台输出false,这里的this是指全局对象
        console.log(self === o); //在控制台输出true,self就是对象o
      }
    }
  }

  //调用这个对象o中的方法m
  o.m();

3.1将对象属性用作实参

/**
 *
 * @param from{Array}
 * @param from_start{index}
 * @param to{Array}
 * @param to_start{index}
 * @param length{number}
 */
function arraycopy(from, from_start, to, to_start, length) {
  //这里写逻辑代码
}


/**
 *
 * @param args{object}
 */
function easycopy(args) { //定义了一个函数,引入一个形参对象,通过这个函数调用arraycopy这个函数
  arraycopy(
    args.from,
    args.from_start || 0,
    args.to,
    args.to_start || 0,
    args.length
  );
}

var a = [1, 2, 3, 4], b = [];
var args = {
  from: a,
  to: b,
  length: 4
}

easycopy(args);

3.2实参类型

//这个函数可以接收任意数量的实参
function flexisum(a) {
  var total = 0;
  for (var i = 0; i < arguments.length; i++) {
    var element = arguments[i], n;
    if (element == null) continue; //忽略值为null和undefined的情况,结束本次循环
    if (element instanceof Array) //如果实参是数组
      n = flexisum.apply(this, element); //递归地计算累加和
    else if (typeof element == 'function') //如果参数是函数类型
      n = Number(element()); //调用这个函数,并做类型转换
    else
      n = Number(element);  //直接做类型转换
    if (isNaN(n)) //如果无法转换为数字,则抛出错误
      throw Error("flexisum():can't convert" + element + "to number");
    total += n;

  }
  return total;
}

4.1作为值的函数

//定义一些简单的函数
function add(x) {
  return x + y;
}

function subtract(x, y) {
  return x - y;
}

function multiply(x, y) {
  return x * y;
}

function divide(x, y) {
  return x / y;
}

//下面的函数以上面的某个函数作为参数
//并给它传入两个操作数,然后调用它
function operate(operator, operand1, operand2) {
  return operator(operand1, operand2);
}
//折行代码所示的函数调用实际上计算了(2+3)+(4*5)的值。
var i = operate(add, operate(add, 2, 3), operate(multiply, 4, 5));

//我们为这个例子重复实现一个简单的函数
//这次实现使用函数直接量,这些函数直接量定义在一个对象直接量中
var operators = {
  add: function (x, y) {
    return x + y;
  },
  substract: function (x, y) {
    return x - y;
  },
  multiply: function (x, y) {
    return x * y;
  },
  divide: function (x, y) {
    return x / y;
  },
  pow:Math.pow //使用预定义的函数
}

//这个函数接收一个名字作为运算符,在对象中查找这个运算符
//然后将它作用于所提供的操作数
//注意这里调用运算符符函数的语法
function operate2(operation,operand1,operand2) {
    if(typeof operators[operation] === 'function')
      return operators[operation](operand1,operand2);
    else throw 'unknown operator';
}

//试计算('hello'+' '+'world')
var j=operate2('add','hello',operate2('add',' ','world'));
//使用预定义的函数Math.pow()
var k = operate2('pow',10,2);

4.2自定义函数属性

//将函数看成一个对象,因此可以自定义属性
//初始化函数对象的计数器属性
//由于函数声明被提前了,因此这里是可以在函数声明之前给他的成员的赋值的
uniqueInteger.counter = 0;
//每次调用这个函数都会返回一个不同的整数
//它使用一个属性来记住下一次将要返回的值
function uniqueInteger() {
  return uniqueInteger.counter++;//先返回计时器的值,然后计数器再自增1
}

//计算阶乘,并将结果缓存至函数的属性中
function factorial(n) {
  if (isFinite(n) && n > 0 && n == Math.round(n)) {
    if (!(n in arguments.callee))
      arguments.callee[n] = n * arguments.callee(n - 1);
    return arguments.callee[n];
  }
  else return NaN; //如果输入有误,则返回NaN
}
factorial[1] = 1;//初始化缓存以保存这种基本情况

5.1作为命名空间的函数

//案例函数:特定场景下返回带补丁的extend()版本
//定义一个扩展函数,用来将第二个以及后续参数复制到第一个参数
//这里我们处理了IE bug:在多数IE版本中,
//如果o的属性拥有一个不可枚举的同名属性,则for/in循环
//不会枚举对象o的可枚举属性,也就是说,将不会正确地处理诸如toString属性
//除非我们显示地检测它

var extend = (function () { //将这个函数的返回值赋给extend
    //在修复它之前,首先检查是否存在bug
    //这个for循环的作用是检测浏览器是否会处理这个bug,如果处理这个bug,则执行下面这段代码
    //如果不处理这段bug,那么下面这段代码将不会被执行,将会跳到后面的代码处,执行后续的代码
    //至于为什么不会执行这段代码,是因为bug导致浏览器不会枚举具有同一属性名的可枚举的属性。
   // 因此,如果有bug就不会执行for/in循环里面的代码
    for (var p in {toString:null}){
      //如果代码执行到这里,那么for/in循环会正确执行并返回
      //一个简单版本的extend()函数
      return function extend(o) {
        for (var i = 0; i < arguments.length; i++) {
          var source =arguments[i];
          for (var prop in source) o[prop]=source[prop];
        }
        return o;
      };
    }
    //如果代码执行到这里,说明for/in循环不会枚举测试对象的toString属性
    //因此返回另一个版本的extend()函数,这个函数显示测试
    //Object.prototype中的不可枚举属性
  return function patched_extend(o) {
    for (var i = 0; i < arguments.length; i++) {
      var source=arguments[i];
      //复制所有的可枚举属性
      for(var prop in source) o[prop] = source[prop];

      //现在检查特殊性
      for (var j = 0; j < protoprops.length; j++) {
        prop=protoprops[j];
        if(source.hasOwnProperty(prop)) o[prop]=source[prop];
      }
    }
    return o;
  };
  //这个列表列出了需要检查的特殊属性
  var protoprops =['toString','valueOf','constructor','hasOwnProperty','isPrototypeOf','toLocaleString'];
}());

6.1闭包

//比较下面这两个函数的调用结果
//这里为了使变量不冲突,将这段代码放到了匿名函数中,
//在匿名函数中来模拟全局作用域
(function () {
  var scope='global scope'; //全局变量
  function checkscope() {
    var scope = 'local scope'; //局部变量
    function f() {
      return scope; //在作用域中返回这个值
    }
    return f(); //返回调用f()的结果
  }
  checkscope(); //local scope;
});

(function () {
  var scope='global scope'; //全局变量
  function checkscope() {
    var scope = 'local scope'; //局部变量
    function f() {
      return scope; //在作用域中返回这个值
    }
    return f; //返回函数
  }
  checkscope()(); //local scope;
})

6.1.1利用闭包定义一个计数器

function counter(n) {
  return { //返回一个对象
    get count(){return n++}, //获取这个对象的属性
    set count(m){ //设置这个对象的属性
      if(m>=n) n=m;
      else throw Error('count is only set to be a large value');
    }
  };
}
var c=counter(1000);
c.count //1000
c.count //1001
c.count=2000
c.count //2000
c.count=2000 //Error 原因:c.count=2000 ,设置之后n的值为2000,获取对象的count值为2000
             //但是,此时n已经自增,所以再次给对象的属性count赋值为2000,便会显示错误

6.2利用闭包实现的私有属性存取器的方法

//这个函数给对象o增加了属性存取器方法
//方法名称为get<name>和set<name>。如果提供了一个判定函数
//setter方法就会用它来检测参数的合法性,然后再存储它
//如果判定函数会返回false,setter方法会抛出一个异常

//这个函数有一个非同寻常之处,就是getter和setter函数
//所操作的属性值并没有存储在对象o中
//相反,这个值仅仅是保存在函数中的局部变量之中
//getter和setter方法同样是局部函数,因此可以访问这个局部变量
//也就是说,对于两个存取器方法来说,这个变量是私有的
//没有办法绕过存取器方法来设置或修改这个值

function addPrivateProperty(o, name, predicate) {
  var value; //这是一个属性值
  //getter方法简单地将其返回
  o['get' + name] = function () {
    return value;
  };

  //setter方法首先检查值是否合法,若不合法就抛出错误
  //否则就将其存储起来
  o['set' + name] = function (v) {
    if (predicate && !predicate(v))
      throw Error('set' + name + ':invalid value' + v);
    else
      value = v;
  };
}

//展示addPrivateProperty()方法
var o={}; //设置一个空对象
//增加属性存取器方法getName()和setName()
//确保只允许字符串值
addPrivateProperty(o,'Name',function (x) {
  return typeof x == 'string';
});

o.setName('Frank'); //设置属性值
console.log(o.getName()); //获取属性值

7.1函数的length属性

//这个函数使用arguments.callee,因此它不能在严格模式下运行
function check(args) {
  var actual = args.length; //实参的真实个数
  var expected = args.callee.length; //期望的实参个数
  if (actual !== expected) //如果不同,则抛出异常
    throw Error("Expected" + expected + "args;got" + actual);
}

function f(x, y, z) {
  check(arguments);  //这个arguments是函数f的arguments
  return x + y + z;
}

7.2函数的call()方法和apply()方法

//将对象o中名为m()的方法替换为另一个方法
//可以在调用原始方法之前和之后记录日志消息
function trace(o, m) {
  var original = o[m];  //在闭包中保存原始方法
  o[m] = function () {  //定义新的方法
    console.log(new Date(), "Entering:", m); //输出日志消息
    var result = original.apply(this, arguments);  //调用原始函数
    console.log(new Date(), "Exiting:", m);  //输出日志消息
    return result;  //返回结果
  }
}

7.3bind()方法

//bind()方法可以将某个函数绑定至一个对象,称为该对象的一个方法
function f(y) {
  return this.x + y;
}
var o = {x: 1};
var g = f.bind(o); //将函数f绑定到对象o上,称为对象o的方法
g(2) //3

//一下的代码可以实现这种绑定
//返回一个函数,通过调用它来调用o中的方法f(),传递它所有的实参
function bind(f, o) {
  if (f.bind) return f.bind(o); //如果函数f中存在方法bind(),那么直接返回
  else return function () {  //否则,这样绑定
    return f.apply(o, arguments);
  }
}

//函数柯里化(currying)
//除了第一个实参之外,传入bind()的实参也会绑定至this,这种附带的应用称为函数柯里化
var sum = function (x, y) {
  return x + y;
}
//创建一个类似sum的新函数,但this的值绑定到null
//并且第一个参数绑定到1,这个新的函数期望只传入一个实参
var succ = sum.bind(null, 1);
succ(2); //3:x绑定到1,并传入2作为实参y

function f(y, z) {
  return this.x + y + z;
}

var g = f.bind({x: 1}, 2); //绑定this和y
g(3)


//ECMAscript3版本的Function.bind()方法
if (!Function.prototype.bind) {
  Function.prototype.bind = function (o /*,args*/) {
    //将this和arguments的值保存至变量中
    //以便在后面嵌套的函数中可以使用它们
    var self = this, boundArgs = arguments;

    //bind()方法的返回值是一个函数
    return function () {
      //创建一个实参列表,将传入bind()的第二个及后续的实参都传入这个函数
      var args = [], i;
      for (i = 1; i < boundArgs.length; i++) args.push(boundArgs[i]);
      for (i = 0; i < arguments.length; i++) args.push(arguments[i]);

      //现在将self作为o的方法来调用,传入这些实参
      return self.apply(o, args);
    };
  };
}

7.4Function()构造函数

//虽然Function构造函数在实际编程中很少用到,但这里我们还是要简单介绍
//不管是通过函数定义语句还是函数直接量表达式,函数的定义都要使用function关键字
//但函数还可以通过Function()构造函数来定义,例:
var f = new Function('x', 'y', 'return x*y;');
//上面折行代码等价于下面折行代码
function f(x, y) {
  return x * y;
}

//关于Function构造函数最重要的一点是:就是它创建的函数并不是使用词法作用域,相反,函数体代码的编译
//总是会在顶层函数执行,如下面代码所示:
var scope='global';
function construcFunction() {
    var scope = "local";
  return new Function("return scope"); //无法捕获局部作用域
}

//调用construcFunction
construcFunction()(); //返回值为全局变量global

8.1使用函数处理数组

//非函数式编程实现对数组的处理
(function () {
  var data = [1, 1, 3, 5, 5];
  var total = 0;

  for (var i = 0; i < data.length; i++) { //对数组元素求和
    total += data[i];
  }

  var mean = total / data.length; //求total的平均数

//计算标准差:首先计算每个数据减去平均数之后偏差的平方,然后求和
  total = 0;
  for (var i = 0; i < data.length; i++) {
    var deivation = data[i] - mean;
    total += deivation * deivation;
  }
  var stddev = Math.sqrt(total / (data.length - 1));
});

//利用函数式编程方法处理数组
(function () {
  //首先定义两个简单的函数
  var sum = function (x, y) {
    return x + y;
  };
  var square = function (x) {
    return x * x;
  };
  //然后将这些函数和数组方法配合使用
  var data = [1, 1, 3, 5, 5];
  var mean = data.reduce(sum) / data.length;
  var deviations = data.map(function (x) {
    return x - mean;
  });
  var stddev = Math.sqrt(deviations.map(square).reduce(sum) / (data.length - 1));

});


//map()方法和reduce()方法详解
//map()方法和reduce()方法是数组的两个方法
//因此其对象是每个数组元素

//对于每个数组元素调用函数f(),并返回一个结果数组
var map = Array.prototype.map
  ? function (a, f) {  //如果存在这个方法,则直接使用它
  return a.map(f);
} : function (a, f) {
  var results = [];
  for (var i = 0, len = a.length; i < len; i++) {
    if (i in a) results[i] = f.call(null, a[i], i, a);
  }
  return results;
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值