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

简介: 大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新......github:https://github.com/Daotin/Web微信公众号:Web前端之巅博客园:http://www.cnblogs.com/lvonve/CSDN:https://blog.csdn.net/lvonve/在这里我会从 Web 前端零基础开始,一步步学习 Web 相关的知识点,期间也会分享一些好玩的项目。

大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新......

在这里我会从 Web 前端零基础开始,一步步学习 Web 相关的知识点,期间也会分享一些好玩的项目。现在就让我们一起进入 Web 前端学习的冒险之旅吧!

一、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

三、闭包

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 的原型对象中,所以其他的伪数组是不能使用的。方法二成功。

目录
相关文章
|
6月前
|
前端开发 JavaScript 开发者
JavaScript:无处不在的Web语言
JavaScript:无处不在的Web语言
|
6月前
|
前端开发 JavaScript 开发者
JavaScript:构建动态Web的核心力量
JavaScript:构建动态Web的核心力量
|
10月前
|
前端开发 算法 API
构建高性能图像处理Web应用:Next.js与TailwindCSS实践
本文分享了构建在线图像黑白转换工具的技术实践,涵盖技术栈选择、架构设计与性能优化。项目采用Next.js提供优秀的SSR性能和SEO支持,TailwindCSS加速UI开发,WebAssembly实现高性能图像处理算法。通过渐进式处理、WebWorker隔离及内存管理等策略,解决大图像处理性能瓶颈,并确保跨浏览器兼容性和移动设备优化。实际应用案例展示了其即时处理、高质量输出和客户端隐私保护等特点。未来计划引入WebGPU加速、AI增强等功能,进一步提升用户体验。此技术栈为Web图像处理应用提供了高效可行的解决方案。
|
6月前
|
JavaScript 前端开发 开发者
JavaScript:驱动现代Web的核心引擎
JavaScript:驱动现代Web的核心引擎
|
6月前
|
JavaScript 前端开发 物联网
JavaScript:驱动现代Web的核心引擎
JavaScript:驱动现代Web的核心引擎
|
9月前
|
JSON JavaScript 前端开发
JavaScript入门干货:蓝桥杯Web组分章学习笔记(基于蓝桥云课《JavaScript基础入门》)
这是一份详尽的JavaScript学习笔记,涵盖基础到进阶内容。包括变量、运算符、数组、字符串操作,DOM/BOM事件处理,内置对象(如Array、Date、Math)用法,JSON格式解析,以及函数作用域与闭包等核心概念。同时深入探讨值类型和引用类型的差异、异常处理机制,并介绍函数高级特性如call/apply/bind方法、递归及arguments对象。代码按章节分点整理,注释细致,适合初学者系统掌握JavaScript编程知识。
167 2
|
前端开发 JavaScript Java
JavaScript闭包深入剖析:性能剖析与优化技巧
JavaScript 闭包是强大而灵活的特性,广泛应用于数据封装、函数柯里化和事件处理等场景。闭包通过保存外部作用域的变量,实现了私有变量和方法的创建,提升了代码的安全性和可维护性。然而,闭包也可能带来性能问题,如内存泄漏和执行效率下降。为优化闭包性能,建议采取以下策略:及时解除对不再使用的闭包变量的引用,减少闭包的创建次数,使用 WeakMap 管理弱引用,以及优化闭包结构以减少作用域链查找的开销。在实际开发中,无论是 Web 前端还是 Node.js 后端,这些优化措施都能显著提升程序的性能和稳定性。
317 70
|
11月前
|
存储 JavaScript 前端开发
|
自然语言处理 JavaScript 前端开发
当面试官再问我JS闭包时,我能答出来的都在这里了。
闭包(Closure)是前端面试中的高频考点,广泛应用于函数式编程中。它不仅指函数内部定义的函数,还涉及内存管理、作用域链和垃圾回收机制。闭包可以让函数访问其外部作用域的变量,但也可能引发内存泄漏等问题。通过合理使用闭包,可以实现模块化、高阶函数和回调函数等应用场景。然而,滥用闭包可能导致代码复杂度增加、调试困难以及潜在的性能问题。为了避免这些问题,开发时应谨慎处理闭包,避免不必要的嵌套,并及时清理不再使用的变量和监听器。
523 16
当面试官再问我JS闭包时,我能答出来的都在这里了。