js进阶二(apply\call、bind、闭包、函数也是对象概念)

本文深入解析JavaScript中的apply、call、bind函数如何改变this指向及其实现原理,同时探讨闭包的特性与应用场景,包括数据缓存、延迟执行和封装私有变量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

apply\call

apply和call可以改变this的指向

function f1(x, y) {
    console.log("结果是:" + (x + y) + this);
    return "10000";
  }
  f1(10, 20);//函数的调用
  window.f1(10, 20);


  var r1 = f1.apply(null, [1, 2]);//此时f1中的this是window
  console.log(r1);

  var r2 = f1.call(null, 1, 2);//此时f1中的this是window
  console.log(r2);

  var obj = {
    age: 10,
    sex: "男"
  };

  window.f1.apply(obj, [10, 20]);
  window.f1.call(obj, 10, 20);

输出如下:

结果是:30[object Window]
结果是:30[object Window]

结果是:3[object Window]
10000

结果是:3[object Window]
10000

结果是:30[object Object]
结果是:30[object Object]

我们在来看一个例子

function Person(age,sex) {
      this.age=age;
      this.sex=sex;
    }
    //通过原型添加方法
    Person.prototype.sayHi=function (x,y) {
      console.log("您好啊:"+this.sex);
      return 1000;
    };
    var per=new Person(10,"男");
    per.sayHi();

    console.log("==============");
    function Student(name,sex) {
      this.name=name;
      this.sex=sex;
    }
    var stu=new Student("小明","人妖");
    var r1=per.sayHi.apply(stu,[10,20]);
    var r2=per.sayHi.call(stu,10,20);

    console.log(r1);
    console.log(r2);

输出如下:

您好啊:男
==============
您好啊:人妖
您好啊:人妖
1000
1000

函数也是对象

    function f1() {
      console.log(this+":====>调用了");
    }
    //f1是函数,f1也是对象
    console.dir(f1);
    //对象调用方法,说明,该对象中有这个方法
    f1.apply();
    f1.call();
    console.log(f1.__proto__==Function.prototype);

输出如下:

ƒ f1()
[object Window]:====>调用了
[object Window]:====>调用了
true

bind

复制了一份的时候,把参数传入到了f1函数中,x===>10,y===>20,null就是this,默认就是window
bind方法是复制的意思,参数可以在复制的时候传进去,也可以在复制之后调用的时候传入进去
apply和call是调用的时候改变this指向
bind方法,是赋值一份的时候,改变了this的指向



  function f1(x, y) {
    console.log((x + y) + ":=====>" + this.age,this);
  }

  var ff = f1.bind(null);
  ff(10, 20);

  //person对象
  function Person() {
    this.age = 1000;
  }
  Person.prototype.eat = function () {
    console.log("这个是吃");
  };
  var per = new Person();
  //传递person实例对象
  var ff = f1.bind(per, 10, 20);
  ff();

结果输出如下:

30:=====>undefined Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
30:=====>1000 Person {age: 1000}

匿名函数返回值

function File(name, size, time) {
      this.name = name;//电影名字
      this.size = size;//电影大小
      this.time = time;//电影的上映时间
    }
    var f1 = new File("jack.avi", "400M", "1997-12-12");
    var f2 = new File("tom.avi", "200M", "2017-12-12");
    var f3 = new File("xiaosu.avi", "800M", "2010-12-12");
    var f4 = new File("xiaosu.bvi", "800M", "2010-12-12");
    var arr = [f3,f4,f1, f2, ];

    function fn(attr) {
      //函数作为返回值
      return function getSort(obj1, obj2) {
        if (obj1[attr] > obj2[attr]) {
          return 1;
        } else if (obj1[attr] == obj2[attr]) {
          return 0;
        } else {
          return -1;
        }
      }
    }

    var ff = fn("name");

    //函数作为参数
    arr.sort(ff);
    for (var i = 0; i < arr.length; i++) {
      console.log(arr[i].name + "====>" + arr[i].size + "===>" + arr[i].time);
    }

输出如下:

jack.avi====>400M===>1997-12-12
tom.avi====>200M===>2017-12-12
xiaosu.avi====>800M===>2010-12-12
xiaosu.bvi====>800M===>2010-12-12

闭包

闭包可以缓存数据

 //普通的函数
  function f1() {
      var num = 10;
      num++;
      return num;
    }
    console.log(f1());
    console.log(f1());
    console.log(f1());

    //函数模式的闭包
    function f2() {
      var num = 10;
      return function () {
        num++;
        return num;
      }
    }
    var ff = f2();
    console.log(ff());//11
    console.log(ff());//12
    console.log(ff());//13

输出如下:

11
11
11
11
12
13

我们来看另外一个例子

function count() {
    var arr = [];
    for (var i=1; i<=3; i++) {
        arr.push(function () {
            return i * i;
        });
    }
    return arr;
}

var results = count();
var f1 = results[0];
var f2 = results[1];
var f3 = results[2];

console.info(f1())
console.info(f2())
console.info(f3())

输出均为16

原因就在于返回的函数引用了变量i,但它并非立刻执行。等到3个函数都返回时,它们所引用的变量i已经变成了4,因此最终结果为16。

返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。



function count() {
    var arr = [];
    for (var i=1; i<=3; i++) {
        arr.push((function (n) {
            return function () {
                return n * n;
            }
        })(i));
    }
    return arr;
}


var results = count();
var f1 = results[0];
var f2 = results[1];
var f3 = results[2];

console.info(f1())
console.info(f2())
console.info(f3())

输出1 4 9

闭包不仅可以为了返回一个函数然后延迟执行
在面向对象的程序设计语言里,比如Java和C++,要在对象内部封装一个私有变量,可以用private修饰一个成员变量。

在没有class机制,只有函数的语言里,借助闭包,同样可以封装一个私有变量。我们用JavaScript创建一个计数器:

function create_counter(initial) {
    var x = initial || 0;
    return {
        inc: function () {
            x += 1;
            return x;
        }
    }
}

它用起来像这样:

var c1 = create_counter();
c1.inc(); // 1
c1.inc(); // 2
c1.inc(); // 3

var c2 = create_counter(10);
c2.inc(); // 11
c2.inc(); // 12
c2.inc(); // 13

在返回的对象中,实现了一个闭包,该闭包携带了局部变量x,并且,从外部代码根本无法访问到变量x。换句话说,闭包就是携带状态的函数,并且它的状态可以完全对外隐藏起来。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值