从零开始学 Web 之 JS 高级(三)apply与call,bind,闭包和沙箱

一、apply 和 call 方法

apply 和 call 都可以改变调用其的函数或方法中的 this 指向。

不同的是传入参数时,apply 有两个参数,第二个参数是数组;call 从第二个参数开始是调用其的函数的所有参数。

使用方法: 1、apply的使用语法: 函数名.apply(对象,[参数1, 参数2,... ]); 方法名.apply(对象,[参数1, 参数2,... ]); 2、call的使用语法: 函数名.call(对象,参数1, 参数2,... ); 方法名.call(对象,参数1, 参数2,... );

1、函数调用apply和call

function f1(x, y) {
  console.log(x+y +this); // 这里面的this是window
  return x+y;
}

var r1 = f1.apply(null, [10,20]); // 打印30 window,传入的是null,所以this指向还是window
console.log(r1); // 30
var r2 = f1.call(null, 10,20);// 打印30 window
console.log(r2); // 30
//函数改变 this 的指向
var obj = {};

var r1 = f1.apply(obj, [10,20]); // 打印30 window,传入的是Obj,所以this指向是Obj
console.log(r1); // 30
var r2 = f1.call(obj, 10,20);// 打印30 Obj
console.log(r2); // 30

2、方法调用apply和call

// 方法改变 this 的指向
    function Person(age) {
        this.age = age;
    }
    Person.prototype.eat = function () {
        console.log(this.age); // this 指向实例对象
    };

    function Student(age) {
        this.age = age;
    }

    var per = new Person(18);
    var stu = new Student(20);

    per.eat.apply(stu); // 打印 20
    per.eat.call(stu); // 打印 20

由于 eat 方法已经指向了 Student 了,所以打印 20,而不是 18.

问题:我们知道函数也是对象,函数可以调用 apply 和 call 方法,但是这两个方法并不在这个函数这个对象的实例函数中,那么在哪里呢?

解答:所有的函数都是 Function 的实例对象,而 apply 和 call 就在 Function 构造函数的原型对象中。


二、bind方法

bind 是复制的意思,也可以改变调用其的函数或方法的 this 指向,参数可以在复制的时候传进去,也可以在复制之后调用的时候传进去。

使用语法: 1、函数名.bind(对象, 参数1, 参数2, ...); // 返回值是复制的这个函数 2、方法名.bind(对象, 参数1, 参数2, ...); // 返回值是复制的这个方法

1、函数调用 bind

function f1(x, y) {
    console.log(x + y + this);
}
// 1.参数在复制的时候传入
var ff = f1.bind(null,10,20); // 这只是复制的一份函数,不是调用,返回值才是
ff();

// 2.参数在调用的时候传入
var ff = f1.bind(null); // 这只是复制的一份函数,不是调用,返回值才是
ff(10,20);

2、方法调用 bind

function Person(age) {
    this.age = age;
}

Person.prototype.eat = function () {
    console.log(this.age); // this 指向实例对象
};

function Student(age) {
    this.age = age;
}

var per = new Person(18);
var stu = new Student(20);

var ff = per.eat.bind(stu);
ff(); // 20

bind和call,apply的区别:[update:2018.07.26]

bind绑定this的指向之后,不会立即调用当前函数,而是将函数返回。 而call,apply绑定this指向后会立即调用。

如果我们在不知道什么时候会调用函数的时候,需要改变this的指向,那么只能使用bind。

比如:在定时器中,我们想改变this的指向,但是又不能立即执行,需要等待2秒,这个时候只能使用bind来绑定this。

setInterval(function(){
    // ...
}.bind(this), 2000);

三、闭包

1、闭包的概念

有一个函数 A 中有一个函数或者对象 B,那么函数或者对象 B 可以访问函数 A 中的数据,那么函数 A 的作用域就形成了闭包。

2、闭包的模式

函数模式的闭包:函数中包含函数。

对象模式的闭包:函数中包含对象。

3、闭包的作用

缓存数据,延长作用域链。

4、闭包的优缺点

也是缓存的数据,导致在闭包的范围内一直起作用。

5、闭包的应用

缓存数据,函数中的数据,外面可以使用。

如果想要缓存数据,就把这个数据放在外层的函数和里层的函数之间。这样不停的调用里层函数,相当于外层函数里的数据没有得到及时释放,就相当于缓存了数据。

// 函数闭包
function A() {
    var num = 10;
    return function () {
        return num++;
    }
}

var func = A();
console.log(func());
console.log(func());
console.log(func());
// 对象闭包
function A() {
    var num = 10;
    return {
        age: num++
    };
}
var func = A();
console.log(func.age);

四、沙箱

沙箱:一小块的真实环境,里面发生的事情不会影响到外面。相同的操作,相同的数据都不会和外面发生冲突。

作用:避免命名冲突。

比如:自调用函数里面就相当于一个沙箱环境。

(function (){
        
}());

五、区分伪数组和真数组

// 真数组
    var arr = [10,20,30];
    // 伪数组
    var obj = {
        0:10,
        1:20,
        2:30,
        length: 3
    };
    // 真数组的访问
    for(var i=0; i<arr.length; i++) {
        console.log("真数组的访问:"+arr[i]);
    }
    // 伪数组的访问
    for(var j=0; j<obj.length; j++) { // 错误:对象中没有length方法
        console.log("伪数组的访问:"+obj[j]);
    }

方法一、使用 length 来区分 这样看起来,真数组和伪数组就没法区别了。 但是真数组的长度 length 可以改变,伪数组不可以,貌似可以区分了。 但是,你还记得有个 arguement 这个伪数组(对象)的 length 是可以改变的,方法一区分失败。 方法二、使用数组的方法 forEach 来鉴别 因为每个数组都是 Array 的实例对象,而 forEach 在 Array 的原型对象中,所以其他的伪数组是不能使用的。方法二成功。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏TungHsu

这或许是对小白最友好的python入门了吧——14,遍历字典

同列表元组一样,字典也能够遍历,但是和列表元组不一样的是,字典遍历分三个部分:遍历键值对、遍历键、遍历值 首先我们新建一个字典: score = {"engl...

27230
来自专栏C/C++基础

C++学习知识点

答:多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。 C++中,实现多态有...

9120
来自专栏java一日一条

java提高篇之关键字static

在Java中并不存在全局变量的概念,但是我们可以通过static来实现一个“伪全局”的概念,在Java中static表示“全局”或者“静态”的意思,用来修饰成员...

10120
来自专栏大闲人柴毛毛

稳扎稳打JavaScript(三)——创建对象的几种方式

有了前面两篇文章的基础后,我们来谈一谈在JS中创建对象的几种方式。 建议大家先预习下先前的两篇博客: 稳扎稳打JavaScript(一)——作用域链 ...

45340
来自专栏noteless

-1-2 java 面向对象基本概念 封装继承多态 变量 this super static 静态变量 匿名对象 值传递 初始化过程 代码块 final关键字 抽象类 接口 区别 多态

面向对象把功能逻辑封装到类本身,用对象去调用功能 持有数据,结构更加自然,也更符合人们的思维习惯

10610
来自专栏racaljk

正则表达式

\ 将下一个字符标记为一个特殊字符、或一个原义字符、或一个向后引用、或一个八进制转义符。例如,“\n”匹配字符“n”。“\\n”匹配一个换行符。序列“\\...

11350
来自专栏java初学

final和static关键字

369110
来自专栏锦小年的博客

Python学习笔记3.2-python内置函数大全

学习python不可避免的首先要了解python的内置函数,熟悉了这些以后可以给编程带来很大的方便。 1、数学运算类 函数名 函数功能 备注 abs...

27990
来自专栏Java帮帮-微信公众号-技术文章全总结

14.Java集合案例

Java 实例 - 数组转集合 以下实例演示了使用 Java Util 类的 Arrays.asList(name) 方法将数组转换为集合: ArrayToC...

40170
来自专栏文武兼修ing——机器学习与IC设计

JavaScript入门笔记(6)标准对象面向对象编程

标准对象 正则对象 正则表达式是一种处理文本信息的神器,在JavaScript中可以方便的使用正则对象对文本进行处理。JavaScript中声明正则对象可以使用...

33080

扫码关注云+社区

领取腾讯云代金券