JavaScript基础篇(四)— — 函数

来源:互联网 发布:linux系统yum指令 编辑:程序博客网 时间:2024/05/24 16:14

一、函数定义

函数重复声明,后一次的函数声明会覆盖了前面一次

  • 函数声明语句
function plus(x ,y) {}

声明式会让函数前置,所以在声明函数之前调用它也是可以调用成功的
* 函数定义表达式

var plus = function (x, y) {}
  • Function构造函数
var add = new Function('x', 'y', 'return (x + y)' );// 等同于function add(x, y) {  return (x + y);}

二、函数调用

  • 作为函数调用
function func(){};func();
  • 作为方法调用
obj = { };obj.funX = function() {};obj.funX();
  • 通过call和apply间接调用函数(改变this)
      call 和 apply带有多个参数,call和apply把当前函数的this指向第一个参数给定的函数或对象中,并传递其余所有的参数作为当前函数的参数。
var O = function () {    this.foo  = 'hello';    this.hello = function () {        return 'world';    }};var fn = function () {    console.log('call', this);};var o = new O();fn.call(o);//此时fn的this指向o

 call和apply的不同之处,在于call传递的参数是作为arguments依次传入的,例如:
fn.call(o, 1, 2, 3);
而apply传递的参数是以一个数组的方式传入的,例如:
fn.apply(o, [1, 2, 3]);

三、参数

 1、当传入参数少于函数声明的参数时,留空的参数的值是undefined。
 2、JavaScript允许传入参数的个数大于声明时制定的参数个数。可以用arguments来访问这些参数

function f(){    for(var i = 0; i < arguments.length ; i++) {        console.log(arguments[i]);    }}f(1,2,3,4,5,6);//打印出1,2,3,4,5,6

 3、默认值

function f(a){  (a !== undefined && a !== null) ? a = a : a = 1;  return a;}

四、作为值的函数

javascript中的函数可以作为值来传递

function square(x) {    return x * x;}var s = square;s(4);

五、函数名的提升

f();var f = function (){};// TypeError: undefined is not a function

上面的代码等同于下面的形式。

var f;f();f = function () {};

第二行中调用f的时候,f只是被声明了,还没有被赋值,等于undefined,所以会报错。

六、函数作用域

 Javascript只有两种作用域:一种是全局作用域,变量在整个程序中一直存在,所有地方都可以读取;另一种是函数作用域,变量只在函数内部存在。
  1、函数内部定义的变量,会在该作用域内覆盖同名全局变量。

var v = 1;function f(){  var v = 2;  console.log(v);}f() // 2v // 1

  2、对于var命令来说,局部变量只能在函数内部声明,在其他区块中声明,一律都是全局变量

if (false) {  var x = 5;}console.log(x);  // 5

  3、函数本身的作用域
 函数本身也是一个值,也有自己的作用域。它的作用域与变量一样,就是其声明时所在的作用域,与其运行时所在的作用域无关。

var a = 1;var x = function () {  console.log(a);};function f() {  var a = 2;  x();}f() // 1

上面代码中,函数x是在函数f的外部声明的,所以它的作用域绑定外层,内部变量a不会到函数f体内取值,所以输出1,而不是2。

  很容易犯错的一点是,如果函数A调用函数B,却没考虑到函数B不会引用函数A的内部变量。

var x = function () {  console.log(a);};function y(f) {  var a = 2;  f();}y(x)// ReferenceError: a is not defined

上面代码将函数x作为参数,传入函数y。但是,函数x是在函数y体外声明的,作用域绑定外层,因此找不到函数y的内部变量a,导致报错。

  同样的,函数体内部声明的函数,作用域绑定函数体内部。

function f1() {  var x = 1;  function f2() {    console.log(x);  }  return f2;}var x = 2;var ff = f1();ff() // 1

上面代码中,函数f1内部声明了一个函数bar,bar的作用域绑定foo。当我们在foo外部取出bar执行时,变量x指向的是foo内部的x,而不是foo外部的x。正是这种机制,构成了下文要讲解的“闭包”现象。

七、闭包

《学习Javascript闭包(Closure)》
接上面的代码
闭包就是函数f2,即能够读取其他函数内部变量的函数。由于在JavaScript语言中,只有函数内部的子函数才能读取内部变量,因此可以把闭包简单理解成“定义在一个函数内部的函数”。闭包最大的特点,就是它可以“记住”诞生的环境,比如f2记住了它诞生的环境f1,所以从f2可以得到f1的内部变量。在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

闭包的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量始终保持在内存中,即闭包可以使得它诞生环境一直存在。请看下面的例子,闭包使得内部变量记住上一次调用时的运算结果。

function createIncrementor(start) {  return function () {    return start++;  };}var inc = createIncrementor(5);inc() // 5inc() // 6inc() // 7

上面代码中,start是函数createIncrementor的内部变量。通过闭包,start的状态被保留了,每一次调用都是在上一次调用的基础上进行计算。从中可以看到,闭包inc使得函数createIncrementor的内部环境,一直存在。所以,闭包可以看作是函数内部作用域的一个接口。inc始终在内存中,而inc的存在依赖于createIncrementor,因此也始终在内存中,不会在调用结束后,被垃圾回收机制回收。
闭包的另一个用处,是封装对象的私有属性和私有方法。

function Person(name) {  var _age;  function setAge(n) {    _age = n;  }  function getAge() {    return _age;  }  return {    name: name,    getAge: getAge,    setAge: setAge  };}var p1 = person('张三');p1.setAge(25);p1.getAge() // 25

闭包应用场景

封装

闭包缺点
1、空间浪费 2、内存泄漏 3、性能消耗
解决方法是,在退出函数之前,将不使用的局部变量全部删除。

八、立即调用的函数表达式(IIFE)

只对匿名函数使用这种“立即执行的函数表达式”。它的目的有两个:一是不必为函数命名,避免了污染全局变量;二是IIFE内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量。

(function() {     /* code */ }());// 或者(function() {     /* code */ })();

参考链接:
1、http://javascript.ruanyifeng.com/grammar/function.html
2、http://pij.robinqu.me/JavaScript_Core/JavaScript_Basics/Function.html

0 0
原创粉丝点击