有关函数的知识点

目录

函数的介绍

函数的作用

函数声明

1.函数声明

2.函数表达式

3.函数声明提前

 函数的内部属性

1.arguments

如何将类数组转换为数组

2.this

显示函数绑定

IIFE

作用域

作用域链

函数调用

1. call(执行环境对象,实参列表)

2. apply(执行环境对象,实参列表数组)

3. bind(执行环境对象)(实参列表)

回调函数

闭包


函数的介绍

定义:函数实际上是对象,每个函数都是Function类型的实例。

函数的作用:

  1. 功能的封装,直接调用,代码复用率高
  2. 构建对象的模板(构造函数)

函数声明

1.函数声明

function 函数名(形参列表){
//函数体
}

2.函数表达式

var 函数名=function(形参列表){
    //函数体
}

3.函数声明提前

函数会提升到当前作用域的最顶部,优先级高于var.

// 函数也是有变量声明提前的
function add(a,b){
    var result=a+b;
    return result;
}
var total=add(1,2);
foo(); //函数声明提升到代码的最前面,可以直接调用函数
function foo(){
    console.log('你好呀');  //你好呀
    // return;  
    // console.log('2');
    // return之后的语句不执行
    // 如果没有返回的内容,则在写代码的时候不关注返回值
    // 没有return:代码执行到大括号

}
// 变量声明提前
console.log(a);   //undefined
var a='你好呀';
console.log(a);   //你好呀

 函数的内部属性

只有在函数内部才能访问的属性,this也可以在函数外部进行。

1.arguments

argumrnts是一个类数组对象,里面包含着传入函数的所有参数,该对象有一个名为callee的指针,指向拥有这个arguments对象的函数。

function foo(a,b){
    //当传递的实参个数超过形参的个数的时候,不会报错,所有的实参都会
    // 保存在arguments里  arguments类数组
    console.log(arguments[3]);//6
    console.log(a,b);//1,2
    // arguments.callee  函数名
    // return   foo(n-1)*n 等价于 arguments.callee(n-1)*n
}
foo(1,2,5,6,7,8);
// foo.length获取一个函数形参的长度
console.log(foo.length);  //2

callee 属性是 arguments 对象的一个成员,仅当相关函数正在执行时才可用。

callee 属性的初始值就是正被执行的 Function 对象。

如何将类数组转换为数组

function test(a, b, c) {
    console.log(arguments.length); //5
    // 如何将类数组对象转换为数组 3种方法
    // 1.Array.from();
    console.log(Array.from(arguments)); //[1,2,3,4,5]
    // 2.Array.prototype.slice.call(arguments, 0)
    console.log(Array.prototype.slice.call(arguments, 0)); //[1,2,3,4,5]
    // 3....拓展运算符
    let arr = [...arguments];
    console.log(arr);//[1,2,3,4,5]
}
test(1,2,3,4,5);

2.this

  1. 在方法中,this表示该方法所属的对象。
  2. 如果单独使用,this表示全局对象。
  3. 在函数中,this表示全局对象。
  4. 在事件中,this表示接受事件的元素。
  5. 在显式函数绑定时,我们可以自己决定this的指向。

在方法中的this

// 谁调用了这个方法 this就指向谁
// 在方法中,this表示该方法所属的对象
var person={
    name:'lisi',
    sayName:function(){
        console.log(this.name);  //lisi
    }
}

person.sayName();

单独使用this

单独使用this,则它指向全局对象。

1. 在浏览器中,window就是该全局对象。

<script>
        console.log(this);  //window
        
    </script>

2. 在node中,指向的是一个{}。

var x=this;
console.log(x);  //{}

函数中使用this(默认)

在函数中,函数的所属者默认绑定在this上。

1. 在浏览器中,window就是该全局对象。

<script>
        function foo() {
            console.log(this);  //window
        }
        foo();

    </script>

2. 在node中,指向的就是global对象。

function foo() {
    console.log(this);  //global
}
foo();

事件中的this

this指向了接收事件的HTML元素:

<button onclick="this.style.display='none'"> 点我后我就消失了 </button>

关于this案例

// 案例一
var o = {
    a: 10,
    b: {
        a: 12,
        fn: function () {
            console.log(this.a);  //12
            console.log(this);      //{ a: 12, fn: [Function: fn] }
            // b对象调用了fn(),所有this指向b对象
        }
    }
}

o.b.fn();

// 案例二
var o = {
    a: 10,
    b: {
        fn: function () {
            console.log(this.a); //undefined
            // b对象调用fn(),fn里面没有a,所有返回undefined
            console.log(this);//{ fn: [Function: fn] }
        }
    }
}

o.b.fn();

// 案例三
var point = {
    x: 0,
    y: 0,
    moveTo: function (x, y) {
        var moveX = function (x) {
            // console.log(this);  //global
            // 因为MoveX(x)前面没有实例对象调用,所有this指向全局变量
            this.x = x;

        };
        var moveY = function (y) {
            this.y = y;
        }

        moveX(x);
        moveY(y);
    }
};

point.moveTo(1, 1);
console.log(point.x);//0
console.log(point.y);//0
console.log(x);//1
console.log(y);//1

// 案例四
var point = {
    x: 0,
    y: 0,
    moveTo: function (x, y) {
        var that = this;
        console.log(this); //{ x: 0, y: 0, moveTo: [Function: moveTo] }
        // 内部函数
        var moveX = function (x) {
            that.x = x;
            // this.x = x;
        };
        // 内部函数
        var moveY = function (y) {
            that.y = y;
            // this.y = y;
        }
        moveX(x);
        moveY(y);
    }
};
point.moveTo(1, 1);
console.log(point.x); //1
console.log(point.y); //1
console.log(x);// 报错
console.log(y);//报错

显示函数绑定

apply和call可以决定this的指向。

var person1={
    sayName:function(a){
        console.log(this.name+a); //lisi123
    }
}

var person2={
    name:'lisi'
}

//person2作为参数调用person1.sayName方法时,this将指向person2,即它是person1的方法
person1.sayName.call(person2,123);

IIFE

IIFE: Immediately Invoked Function Expression,意为立即调用的函数表达式,也就是说,声明函数的同时立即调用这个函数。

作用

  • 页面加载完成后只执行一次的设置函数。
  • 将设置函数中的变量包裹在局部作用域中,不会泄露成全局变量。

写法

//第一种
(function(形参){
     函数体内容
})(实参);

//第二种
(function(形参){
     函数体内容
}(实参));
function foo() {
    console.log(123);
}
foo();
// 如果后面跟的是立即执行函数,前面的结束语句必须要加分号
// 立即执行函数
(function (a) {
    console.log(456 + a);  //579
})(123)

// 返回的是一个布尔值,然后进行取反
var res = !function () {
    return '123'; //false
    // return '';  //true
}()
console.log(res);

//对于数字返回的是原来的结果,非数字返回NaN
var res = +function (a) {
    return 'null'; //NaN
    // return 123;  //123

}('456')
console.log(res);

// 对于数字返回的是正负符号相反,非数字返回NaN
var res = -function (a) {
    return 'null'; //NaN
    // return 123;  //-123

}('456')
console.log(res);

// 对于数字返回的是正负符号相反在减1,非数字返回-1
var res = ~function (a) {
    // return 'null'; //-1
    return 123;  //-124

}('456')
console.log(res);

//返回的结果是undefined
var res = function () {

}()
console.log(res); //undfined

案例

for (var i = 0; i < 5; i++) {
    function a() {
        console.log(i); //5
        //因为 i 是全局作用域,当循环结束后 i 的值是5
    }
}
a();

for (var i = 0; i < 6; i++) {
    (function (i) {
        console.log(i); //0,1,2,3,4,5
        // js中调用函数传递参数都是值传递,所有当立即执行函数时,
        // 首先会把参数i的值赋值一份,然后再创建函数作用域来执行
        // 函数,循环5次就会创建5个作用域,所有每个输出访问的都是
        // 不同作用域的i 的值。
    })(i)
}

作用域

函数作用域:函数中声明的变量,会成为函数的局部变量。

全局作用域:函数外声明的变量,会成为全局变量。

// es5中没有块级作用域
{
    var a=10;
}

console.log(a); //10
var a = 10;
function foo() {
//当函数内部有变量a时,会产生局部作用域,外部全局作用域中的a不会对函数内部造成影响
//如果把函数内部的变量a注释掉时,函数内部的a输出的就是全局作用域中的a
  console.log(a);  //undefined
    var a = 100;
  console.log(a);   //100
  function fn() {
    console.log(a);  //undefiined
    var a = 200;
    console.log(a);   //100
  }
  fn()
}
foo()

作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突。

作用域链

自由变量:当前作用域没有定义的变量,就是自由变量。想要得到自由变量,要到创建这个函数的父级作用域寻找,没有就一直向上级祖先元素寻找。

var a = 100
function fn() {
    var b = 200
    console.log(a) // 这里的a在这里就是一个自由变量  // 100
    console.log(b)
}
fn()

案例

var a=10;
function fn(){
    var b=10;
function bar(){
console.log(a+b);  //20;
//父级作用域中有两个b可以选择,创建这个函数的父级作用域优先
}

    return bar;

}

var x=fn(),  //执行fn() 返回的是bar
b=200;
x();   //执行x,就是执行bar函数

函数调用

函数声明好之后并不会直接运行,需要进行调用才能进行。 

  • 函数名(实参列表);

(修改this指向问题,怎么修改执行环境上下文对象/this)

  • 函数名.call(执行环境对象,实参列表)
  • 函数名,apply(执行环境对象,实参列表数组)
  • 函数名.bind(执行环境对象)(实参列表) //第一次调用返回的是修改后的函数
var obj = {
    name: 'zhangsan',
    sayName: function () {
        console.log(this.name);  //undefined,指向global
    }
}

var b = obj.sayName;
console.log(b);  //[Function;sayName]
b(); //执行sayName
obj.sayName();  //zhangsan

以下案例就是让this指向obj。

1. call(执行环境对象,实参列表)

调用call方法,this就会执行环境对象。

var obj = {
    name: 'zhangsan',
    sayName: function () {
        console.log(this.name);  //zhangsan

    }
}

var b = obj.sayName;
b.call(obj);  

还可以传递多个实参

var obj = {
    name: 'zhangsan',
    sayName: function (a, b) {
        console.log(this.name);  //zhangsan
        console.log(a, b);   //5 你好

    }
}

var b = obj.sayName;
b.call(obj, 5, '你好');  

2. apply(执行环境对象,实参列表数组)

apply和call方法比较相似,也可以改变this的指向。

var obj = {
    name: 'zhangsan',
    sayName: function () {
        console.log(this.name);  //zhangsan
    }
}

var b = obj.sayName;
b.apply(obj);  

apply可以有多个参数,但第二个参数必须是数组

var obj = {
    name: 'zhangsan',
    sayName: function (a,b) {
        console.log(this.name);  //zhangsan
        console.log(a,b);   //10,20
    }
}

var b = obj.sayName;
b.apply(obj, [10,20]);  

注意:如果call和apply的第一个参数是null,那么this在node环境下指向的是global对象,在HTML中指向的是window对象。

var obj = {
    name: 'zhangsan',
    sayName: function () {
        console.log(this); //global

    }
}

var b = obj.sayName;
b.apply(null);  

3. bind(执行环境对象)(实参列表)

var obj = {
    name: 'zhangsan',
    sayName: function () {
        console.log(this.name); //zhangsan

    }
}

var b = obj.sayName;
b.bind(obj);
// 代码没有被打印,这就是bind和call、apply方法的不同,实际上bind返回的是一个修改过后的函数
// 新建一个变量c来接收bind修改后的函数
var c = b.bind(obj);
console.log(c); //[Function: bound sayName]
// 执行c
c();

bind也有多个参数,并且参数可以执行的时候再次添加,但要注意的是,参数是按照形参的顺序进行的。

var obj = {
    name: 'zhangsan',
    sayName: function (a, b) {
        console.log(this.name); //zhangsan
        console.log(a, b);  //1 你好
    }
}

var b = obj.sayName;
b.bind(obj);
// 代码没有被打印,这就是bind和call、apply方法的不同,实际上bind返回的是一个修改过后的函数
// 新建一个变量c来接收bind修改后的函数
var c = b.bind(obj, 1, '你好');
console.log(c); //[Function: bound sayName]
// 执行c
c();

总结:call和apply都是改变上下文中的this并立即执行这个函数,bind方法可以让对应的函数想什么时候调用就什么时候调用,并且可以将参数在执行的时候添加,这是它们的区别,根据自己的实际情况来选择使用。

回调函数

主函数的事先做完,回头再调用传进来的那个函数(函数可以作为参数)。

// 定义主函数,回调函数作为参数
function A(B) {
    B();
    console.log('我是函数A');
}
// 定义回调函数
function B() {
    // 模拟异步操作 -> 延时操作
    setTimeout(() => {
        console.log('我是函数B');
    }, 2000);
}
// 调用主函数,将函数B传进去
A(B)

回调函数的作用:回调函数一般都在用在耗时的操作上,因为主函数不用等待回调函数执行完,可以接着执行自己的代码。(函数也可作为返回值)

闭包

闭包(一种特殊的对象)就是指有权访问另一个函数作用域中的变量的函数。

闭包参数的三个必要条件

  1. 函数嵌套函数
  2. 内部函数引用了外部函数中的数据(属性、函数)
  3. 参数和变量不会被回收

待定条件:一个函数return另一个函数

最常见的闭包方式

function fun() {
    var a = 1, b = 2;
    function A() {
        return a + b;
    }

    return A;
}

console.log(fun()()); //3

变量始终保持在内存中。

function f1() {
    var n = 999;
    nAdd = function () {
        n += 1;
    }
    function f2() {
        console.log(n);
    }
    return f2;
}

var result = f1();
result(); //999
nAdd();
result();//1000

f1是f2的父函数,而f2(f2实际上就是result)被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <script>
    var name = 'window-name'
    var obj = {
      name: 'obj-name',
      say: function () {
        return function () {
          console.log(this.name); //window-name
        }
      }
    }
    var x = obj.say()
    console.log(x); //ƒ () { console.log(this.name);}
    x()
    console.log(this); //window
  </script>
</body>

</html>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值