JavaScript 函数 function
admin
2024-02-07 05:41:18

目录

  • 1 函数 function
    • 1.1 函数声明
      • 1.1.1 function 命令
      • 1.1.2 函数表达式
      • 1.1.3 Function 构造函数
    • 1.2 函数提升
    • 1.3 函数属性
    • 1.4 函数方法
    • 1.5 函数作用域
    • 1.6 函数参数
    • 1.7 arguments 对象
      • 1.7.1 arguments 属性
    • 1.8 闭包
    • 1.9 立即调用的函数表达式(IIFE)
    • 1.10 eval 命令

1 函数 function

  • JavaScript 语言将函数看作一种值,与其它值(数值、字符串、布尔值等等)地位相同。凡是可以使用值的地方,就能使用函数。比如,可以把函数赋值给变量和对象的属性,也可以当作参数传入其他函数,或者作为函数的结果返回。函数只是一个可以执行的值,此外并无特殊之处。
  • 由于函数与其他数据类型地位平等,所以在 JavaScript 语言中又称函数为第一等公民。

1.1 函数声明

如果同一个函数被多次声明,后面的声明就会覆盖前面的声明

1.1.1 function 命令

function print(s) {console.log(s);
}

1.1.2 函数表达式

  1. 将一个匿名函数赋值给变量
var print = function (s) {console.log(s);
};
  1. 将具名匿名函数赋值给变量,该函数名只在函数体内部有效,在函数体外部无效
  • 一是可以在函数体内部调用自身,
  • 二是方便除错(除错工具显示函数调用栈时,将显示函数名,而不再显示这里是一个匿名函数)。因此,下面的形式声明函数也非常常见。
var print = function x() {console.log(typeof x);
};x; // ReferenceError: x is not definedprint(); // function

1.1.3 Function 构造函数

这种声明函数的方式非常不直观,几乎无人使用

var add = new Function("x", "y", "return x + y");// 等同于
function add(x, y) {return x + y;
}

1.2 函数提升

  • JavaScript 引擎将函数名视同变量名,所以采用function命令声明函数时,整个函数会像变量声明一样,被提升到代码头部。
f();function f() {}
  • 表面上,上面代码好像在声明之前就调用了函数f。但是实际上,由于“变量提升”,函数f被提升到了代码头部,也就是在调用之前已经声明了。但是,如果采用赋值语句定义函数,JavaScript 就会报错。

1.3 函数属性

  • 函数的name属性返回函数的名字。
  • 函数的length属性返回函数预期传入的参数个数,即函数定义之中的参数个数。

1.4 函数方法

  • 函数的toString()方法返回一个字符串,内容是函数的源码。

1.5 函数作用域

作用域(scope)指的是变量存在的范围。在 ES5 的规范中,JavaScript 只有两种作用域:

  • 一种是全局作用域,变量在整个程序中一直存在,所有地方都可以读取;

  • 一种是函数作用域,变量只在函数内部存在。在函数内部定义的变量,外部无法读取,称为“局部变量”(local variable)。

  • ES6 又新增了块级作用域

  • 函数内部定义的变量,会在该作用域内覆盖同名全局变量。

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

  • 与全局作用域一样,函数作用域内部也会产生“变量提升”现象。var命令声明的变量,不管在什么位置,变量声明都会被提升到函数体的头部。

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

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

1.6 函数参数

  • 函数参数不是必需的,JavaScript 允许省略参数,但是,没有办法只省略靠前的参数,而保留靠后的参数。如果一定要省略靠前的参数,只有显式传入undefined

  • 函数参数如果是原始类型的值(数值、字符串、布尔值),传递方式是传值传递(passes by value)。这意味着,在函数体内修改参数值,不会影响到函数外部。

  • 如果函数参数是复合类型的值(数组、对象、其他函数),传递方式是传址传递(pass by reference)。也就是说,传入函数的原始值的地址,因此在函数内部修改参数,将会影响到原始值。

  • 如果函数内部修改的,不是参数对象的某个属性,而是替换掉整个参数,这时不会影响到原始值

    • 这是因为,形式参数(o)的值实际是参数obj的地址,重新对o赋值导致o指向另一个地址,保存在原地址上的值当然不受影响
var obj = [1, 2, 3];function f(o) {o = [2, 3, 4];
}
f(obj);obj // [1, 2, 3]
  • 如果有同名的参数,则取最后出现的那个值
  • 调用函数f()的时候,没有提供第二个参数,a的取值就变成了undefined。这时,如果要获得第一个a的值,可以使用arguments对象。

1.7 arguments 对象

  • arguments对象包含了函数运行时的所有参数,arguments[0]就是第一个参数,arguments[1]就是第二个参数,以此类推。这个对象只有在函数体内部,才可以使用。
  • 虽然arguments很像数组,但它是一个对象

1.7.1 arguments 属性

  • arguments.length属性,可以判断函数调用时到底带几个参数。
  • arguments.callee属性,返回它所对应的原函数。

1.8 闭包

  • 把闭包简单理解成 定义在一个函数内部的函数 ,能够读取其他函数内部变量的函数
  • 闭包的最大用处有两个,一个是可以读取外层函数内部的变量,另一个就是让这些变量始终保持在内存中,即闭包可以使得它诞生环境一直存在。
function createIncrementor(start) {return function () {return start++;};
}var inc = createIncrementor(5);inc(); // 5
inc(); // 6
inc(); // 7

上面代码中,start是函数createIncrementor的内部变量。通过闭包,start的状态被保留了,每一次调用都是在上一次调用的基础上进行计算。从中可以看到,闭包inc使得函数createIncrementor的内部环境,一直存在。所以,闭包可以看作是函数内部作用域的一个接口。

为什么闭包能够返回外层函数的内部变量?原因是闭包(上例的inc)用到了外层变量(start),导致外层函数(createIncrementor)不能从内存释放。只要闭包没有被垃圾回收机制清除,外层函数提供的运行环境也不会被清除,它的内部变量就始终保存着当前值,供闭包读取。

JS 中 return 一个函数与直接 return 一个函数变量的区别
函数的节流与防抖

function makeCounter() {var count = 0;function counter() {count = count + 1;return count;}return counter(); // 将嵌套函数返回
}
var doCount = makeCounter();
console.log(doCount, "--doCount1"); // 1 '--doCount1'
console.log(doCount, "--doCount2"); // 1 '--doCount2'
console.log(doCount, "--doCount3"); // 1 '--doCount3'
  • 当 return counter()时,就自动调用了嵌套函数。
  • 那么嵌套函数返回一个经过+1 的 count,并且 count 的值为 1.
  • 所以 doCount 得到的是一个数字,并不是函数,所以无法得到闭包。
function makeCounter() {var count = 0;function counter() {count = count + 1;return count;}return counter; // 将嵌套函数返回,但只写函数名称
}
var doCount = makeCounter();
console.log(doCount(), "--doCount1"); // 1 '--doCount1'
console.log(doCount(), "--doCount2"); // 2 '--doCount2'
console.log(doCount(), "--doCount3"); // 3 '--doCount3'
  • return counter 返回的是整一个 cunter()函数。

  • 因此执行 var doCount = makeCounter()时,doCount 将引用 counter 函数及其中的变量环境。

  • 那么 counter 函数及其中的变量环境,就是闭包了

  • 闭包的形成:内部函数引用了外部函数的数据(这里为 count),

  • 因此在 doCount=makeCounter(),则会把这个 count 保存在 doCount 中,函数执行完,count 并不会被销毁。

  • 注意: 为什么上面这段代码没有直接写的 function doCount(){…} 而是把 function 赋值给了变量 doCount 呢?

  • 我们通常会想当然的认为每次调用 doCount() 都会重走一遍 doCount()中的代码块, 但其实不然。

  • 注意 makeCounter 方法中的 return 不是 1,2,3 这样的数值, 而是一个方法,并且把这个方法赋值给了 doCount 变量。

  • 那么在这个 makeCounter 自运行一遍之后, 其实最后赋值给 doCount 的是 count = count + 1; return count; 这段代码。

  • 所以后面每次调用 doCount() 其实都是在调用 count = count + 1; return count;

  • 闭包会持有父方法的局部变量并且不会随父方法销毁而销毁, 所以这个 count 其实就是来自于第一次 makeCounter 执行时创建的变量

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

  • 根据 JavaScript 的语法,圆括号()跟在函数名之后,表示调用该函数。比如,print()就表示调用print函数。
(function(){ /* code */ }());
// 或者
(function(){ /* code */ })();

1.10 eval 命令

eval命令接受一个字符串作为参数,并将这个字符串当作语句执行。

eval("var a = 1;");
a; // 1

使用严格模式 加一行 ‘use strict’;

相关内容

热门资讯

延安旅游攻略:一家老小5口人,... 每到暑假或国庆长假,总有很多家庭游客向我们咨询:“我们一家老小来延安,有老人有孩子,行程该怎么安排才...
原创 韩... 韩国明星到延吉旅游,第一次挑战吃羊鞭,惊得嘴都合不上了! 全昭旻等人在延吉录制节目,刚到延吉,他们...
国庆黄金周景区情况:大同古城半... 文| 芙昕 编辑 | 芙昕 国庆长假,很多人都计划着出门走走,可一到了那些热门景点,看到的往往不是山...
来大东北一共分两步:先“冷藏”... 还在被“东北=冰窖”的刻板印象吓退? 南方的“小土豆”们 别急着裹紧小棉袄 这个冬天 让“气候缓冲带...
第三届“长城之约”活动在河北涞... 11月15日,第三届"长城之约"全球推广活动暨世界文化遗产对话15日在河北省保定市涞源县启幕。 本次...