• 作用:主要是用于减少重复代码

创建(定义、声明)函数

  1. function 函数名(){
  2. //函数体
  3. }
  • 函数体的代码不会直接运行,必须手动调用。

调用函数

  1. //运行函数体
  2. 函数名();
  3. //例子
  4. function test(){
  5. console.log('这是一个函数的输出');
  6. };
  7. test()
  8. // function test(){}其中 function 是函数关键字,test 是函数名,必须有(){},参数可有可
  9. // 没有,参数是写在()括号里面的。
  10. // 如果写成 function test(a,b){},相当于隐式的在函数里面 var a,var b 申明了两个变量,()括号里面不能直接写 var

函数提升

  • 通过字面量声明的函数,会提升到脚本块的顶部。
  • 通过字面量声明的函数,会成为全局对象的属性

其他特点

通过typeof 函数名,得到的结果是”function”

函数内部声明的变量:

  1. 如果不使用var声明,和全局变量一致,表示给全局对象添加属性
  2. 如果使用var声明,变量提升到所在函数的顶部,函数外部不可以使用该变量

参数

  • 参数表示函数运行的未知条件,需要调用者告知的数据
  • (1)形参(形式参数):指的是 function sum(a,b){}括号里面的 a 和 b
  • (2)实参(实际参数):指的是 sum(1,2);里面的 1,2
  • 形参可以比实参多,实参也可以比形参多
  • 如果实参没有传递,则对应的形参为undefined
  1. // 参数的有效返回在函数体中
  2. function 函数名(形参1, 形参2, ...){
  3. }
  4. 函数名(实参)

函数表达式

(1)命名函数表达式

  1. var test = function abc(){
  2. console.log('a');
  3. }

上面这个函数的函数名 name 是 abc
在控制台 console 直接输出 test 就会出现→
在控制台 console 直接输出 abc 会报错,表达式就会忽略他的名字 abc。
在上面例子中,fuction abc(){console.log(‘a’)}这一部分叫表达式,是会忽略
abc 这个地方的名字,会变成匿名函数表达式,不如直接写成匿名函数
函数是一个引用类型,将其赋值给某个变量时,变量中保存的是函数的地址

(2)匿名函数表达式(常用,一般说的函数表达式就是匿名函数表达式)

  1. function test(){
  2. console.log('abc')
  3. };
  4. test();

返回值

函数运行后,得到的结果,调用函数时,调用表达式的值就是函数的返回值
return 会直接结束整个函数的运行
return 后面如果不跟任何数据,返回undefined
如果函数中没有书写return,则该函数会在末尾自动return undefined。

  1. function test(a,b){
  2. return a + b
  3. };
  4. cosole.log(test(2,3));

作用域和闭包

作用域

  • 定义:变量(变量作用于又称上下文)和函数生效(能被访问)的区域
    全局、局部变量
  • 全局作用域:直接在脚本中书写的代码,在全局作用域中声明的变量,会被提升到脚本块的顶部,并且会成为全局对象的属性。
  • 函数作用域:在函数作用域中声明的变量,会被提升到函数的顶部,并且不会成为全局对象的属性.
  • 函数外面不能用函数里面的。里面的可以访问外面的,外面的不能访问里面的,彼此独立的区间不能相互访问
  • 因此,函数中声明的变量不会导致全局对象的污染
  1. var a = 1;
  2. function sun(){
  3. var b = 1;
  4. console.log(a + b);
  5. };
  6. sun();

立即执行函数

  • 定义:此类函数没有声明,在一次执行过后即释放(被销毁)。适合做初始化工作。针对初始化功能的函数:只想让它执行一次的函数
  1. (function() {
  2. function helper1() {
  3. }
  4. function helper2() {
  5. }
  6. })();
  7. // 例
  8. (function (){ //写成(function abc(){}())也调用不到
  9. var a = 123;
  10. var b = 234;
  11. console.log(a + b);
  12. }())
  13. (function (a, b, c){
  14. console.log(a + b + c * 2);
  15. }(1, 2, 3))
  16. // 这一行里面的(1,2,3)是实参
  17. var num = (function (a, b, c){
  18. var d = a + b + c * 2 2;
  19. return d;
  20. }(1, 2, 3))
  21. // 答案 num = 7

闭包

  • 闭包(closure),是一种现象,内部函数,可以使用外部函数环境中的变量。
  • 当内部函数被保存到外部时,将会生成闭包。闭包会导致原有作用域链不释放,造成内存泄露
  • 但凡是内部的函数被保存到外部,一定生成闭包
  1. var g = "Abc";
  2. function A() {
  3. var a = 234;
  4. console.log(a, g);
  5. function B() {
  6. var b = 567;
  7. console.log(b, a, g);
  8. }
  9. B();
  10. }
  11. A();

this关键字

this无法赋值

  1. 在全局作用域中,this关键字固定指向全局对象。
  2. 在函数作用域中,取决于函数是如何被调用的
    1. 函数直接调用,this指向全局对象
    2. 通过一个对象的属性调用,格式为对象.属性()对象["属性"](),this指向对象
  1. var obj = {
  2. firstName: "小",
  3. lastName: "明",
  4. age: 18,
  5. sayHello: function() {
  6. console.log(`我叫${this.firstName}${this.lastName},今年${this.age}岁了`);
  7. }
  8. };
  9. obj.sayHello();

构造函数

构造函数专门用于创建对象
对象中的属性,如果是一个函数,也称该属性为对象的方法

  1. new 函数名(参数);
  2. function User(name, age, gender) {
  3. this.name = name;
  4. this.age = age;
  5. this.gender = gender;
  6. this.sayHello = function() {
  7. console.log(`我叫${this.name},年龄${this.age}岁,性别${this.gender}`);
  8. }
  9. }
  10. var u1 = new User("张三", 18, "男");
  • 如果使用上面的格式创建对象,则该函数叫做构造函数。
  1. 函数名使用大驼峰命名法
  2. 构造函数内部,会自动创建一个新对象,this指向新创建的对象,并且自动返回新对象
  3. 构造函数中如果出现返回值,如果返回的是原始类型,则直接忽略;如果返回的是引用类型,则使用返回的结果
  4. 所有的对象,最终都是通过构造函数创建的

arguments

  • 在函数中使用,获取该函数调用时,传递的所有参数
  • 是一个类数组(也称为伪数组:没有通过Array构造函数创建的类似于数组结构的对象),伪数组会缺少大量的数组实例方法
  • arguments数组中的值,会与对应的形参映射

new.target

  • 该表达式在函数中使用,返回的是当前的构造函数,但是,如果该函数不是通过new调用的,则返回undefined
  • 通常用于判断某个函数是否是通过new在调用。

函数的本质

  • 函数的本质就是对象。
  • 构造函数又称之为构造器
  • 所有的对象都是通过关键字new出来的,new 构造函数()
  • 所有的函数,都是通过new Function创建。
  • 由于函数本身就是对象,因此函数中,可以拥有各种属性。

包装类

  • JS为了增强原始类型的功能,为boolean、string、number分别创建了一个构造函数:
  1. new Boolean()
  2. new String();

静态成员

  • fromCharCode:通过unicode编码创建字符串

实例成员

  • length:字符串长度

字符串是一个伪数组

  • charAt:得到指定位置的字符
  • charCodeAt: charCodeAt() 方法返回 0 到 65535 之间的整数,表示给定索引处的 UTF-16 代码单元
  • concat: 字符串合并
  • includes: 查找字符中是否含有某个元素
  • endsWith: 判断是否中是否以某个元素结尾
  • startsWith: 判断是否中是否以某个元素开头
  • indexOf:方法返回调用它的 String 对象中第一次出现的指定值的索引,从 fromIndex 处进行搜索。如果未找到该值,则返回 -1
  • lastIndexOf:lastIndexOf() 方法返回调用String 对象的指定值最后一次出现的索引,在一个字符串中的指定位置 fromIndex处从后向前搜索。如果没找到这个特定值则返回-1
  • padStart:字符串填充由开头开始(重复填充);可传两个参数,一个为填充的目标长度,第二个为填充字符串
  • padEnd: 字符串填充由末尾开始(重复填充);可传两个参数,一个为填充的目标长度,第二个为填充字符串
  • repeat: repeat() 构造并返回一个新字符串,该字符串包含被连接在一起的指定数量的字符串的副本
  • slice:从某个位置取到某个位置;位置可以是负数;
  • substr: 从某个位置开始取,取指定的长度;位置可以是负数;
  • substring:从某个位置取到某个位置;不可以是负数;参数位置是可调换的。
  • toLowerCase 将字符串里的大写字母转为小写
  • toUpperCase 将字符串里的小写字母转为大写
  • split:分割字符串

可以寻找对应dom的地址

  1. new Number()

静态成员

  • isNaN
  • isFinite
  • isInteger:判断一个数据是否是整数
  • parseFloat: 将一个数据转换为小数
  • parseInt:将以一个数据转换为整数,直接舍去小数部分
  • parseInt、parseFloat要求参数是一个字符串,如果不是字符串,则会先转换为字符串。
  • 从字符串开始位置进行查找,找到第一个有效的数字进行转换,如果没有找到,则返回NaN,左右空白字符会忽略;parseInt,可以传入第二个参数,表示将给定的字符串,识别为多少进制。

实例成员

  • toFixed方法:会有四舍五入
  • toPrecision:以指定的精度返回一个数字字符串
  • 如果语法上,将原始类型当作对象使用时(一般是在使用属性时),JS会自动在该位置利用对应的构造函数,创建对象来访问原始类型的属性。

类:在JS中,可以认为,类就是构造函数
成员属性(方法)、实例属性(方法):表示该属性是通过构造函数创建的对象调用的。
静态属性(方法)、类属性(方法):表示该属性是通过构造函数本身调用的。

  1. var num = new Nunber(123); //数字类型对象
  2. var str = new String(‘abcd’); //字符串类型对象
  3. var bol = new Boolean(‘true’); //布尔类型对象

递归

  • 函数直接或间接调用自身,避免无限递归,无限递归会导致执行栈溢出。
  • 对比死循环

    死循环不会报错,也不会导致栈溢出
    无限递归会导致栈溢出

  • 递归要找出口,没有出口的递归就是无限递归

  1. //递归的例子 斐波拉契数列
  2. function fb(n){
  3. if(n === 1 && n === 2){
  4. return 1;
  5. }
  6. return fb(n - 1) + fb(n - 2);
  7. }
  8. fb(10); //55
  9. //阶乘
  10. function jc(n,total = 1){
  11. if(n === 0 || n === 1){
  12. return total
  13. };
  14. return jc(n - 1, n * total)
  15. };
  16. jc(5); //120
  17. //汉诺塔
  18. function hannuo(no1, no2, no3, n) {
  19. if (n === 1) {
  20. console.log(`${no1}->${no3}`);
  21. } else {
  22. hannuo(no1, no3, no2, n - 1);
  23. console.log(`${no1}->${no3}`);
  24. hannuo(no2, no1, no3, n - 1);
  25. }
  26. }
  27. hannuo('A', 'B', 'C', 5);

执行栈

任何代码的执行都必须有一个执行环境,执行环境为代码的执行提供支持

执行环境是放到执行栈中的。

每个函数的调用,都需要创建一个函数的执行环境,函数调用结束,执行环境销毁。

执行栈有相对固定的大小,如果执行环境太多,执行栈无法容纳,会报错

  1. function A(){
  2. console.log("A begin");
  3. B();
  4. console.log("A over");
  5. }
  6. function B(){
  7. console.log("B begin");
  8. C();
  9. console.log("B over");
  10. }
  11. function C(){
  12. console.log("C begin");
  13. console.log("C over");
  14. }
  15. console.log("global begin")
  16. A();
  17. console.log("global over");

尾递归

如果一个函数最后一条语句是调用函数,并且调用函数不是表达式的一部分,则该语句称为尾调用,如果尾调用是调用自身函数,则称为尾递归。

某些语言或执行环境会对尾调用进行优化,它们会理解销毁当前函数,避免执行栈空间被占用。

在浏览器执行环境中,尾调用没有优化。但在nodejs环境中有优化。

函数的实例成员

  • length属性,得到函数形参数量
  • apply方法:调用函数,同时指定函数中的this指向,参数以数组传递
  • call方法:调用函数,同时指定函数中的this指向,参数以列表传递
  • bind方法:得到一个新函数,该函数中的this始终指向指定的值。

通常,可以利用apply、call方法,将某个伪数组转换伪真数组。

  1. //改变this 指向
  2. function sayHello(a, b) {
  3. console.log(this.name, this.age);
  4. console.log(a, b);
  5. }
  6. var user1 = {
  7. name: "asfd",
  8. age: 123
  9. };
  10. var user2 = {
  11. name: "546345",
  12. age: 11
  13. };
  14. var newFunc = sayHello.bind(user1, 1, 2);
  15. newFunc();
  16. // sayHello.apply(user1, [1, 2]);
  17. // sayHello.call(user2, 1, 2);
  18. function test() {
  19. console.log(arguments);
  20. //将arguments转换为真数组
  21. var newArr = [].slice.call(arguments)
  22. console.log(newArr);
  23. }
  24. test(23, 5, 6, 2, 233, 5, 6, 7);