首页
技术小册
AIGC
面试刷题
技术文章
MAGENTO
云计算
视频课程
源码下载
PDF书籍
「涨薪秘籍」
登录
注册
01 | 函数式vs.面向对象:响应未知和不确定
02 | 如何通过闭包对象管理程序中状态的变化?
03 | 如何通过部分应用和柯里化让函数具象化?
04 | 如何通过组合、管道和reducer让函数抽象化?
05|map、reduce和monad如何围绕值进行操作?
06 | 如何通过模块化、异步和观察做到动态加载?
07 | 深入理解对象的私有和静态属性
08|深入理解继承、Delegation和组合
09|面向对象:通过词法作用域和调用点理解this绑定
10|JS有哪8种数据类型,你需要注意什么?
11|通过JS引擎的堆栈了解闭包原理
12|JS语义分析该用迭代还是递归?
13 | JS引擎如何实现数组的稳定排序?
14 | 通过SparkPlug深入了解调用栈
15 | 如何通过哈希查找JS对象内存地址?
16 | 为什么环形队列适合做Node数据流缓存?
17 | 如何通过链表做LRU/LFU缓存?
18 | TurboFan如何用图做JS编译优化?
19 | 通过树和图看如何在无序中找到路径和秩序
20 | 算法思想:JS中分治、贪心、回溯和动态规划
21 | 创建型:为什么说Redux可以替代单例状态管理
22|结构型:Vue.js如何通过代理实现响应式编程
23 | 结构型:通过jQuery看结构型模式
24 | 行为型:通过观察者、迭代器模式看JS异步回调
25 | 行为型:模版、策略和状态模式有什么区别?
26|特殊型:前端有哪些处理加载和渲染的特殊“模式”?
27|性能:如何理解JavaScript中的并行、并发?
28|性能:通过Orinoco、Jank Busters看垃圾回收
29|网络:从HTTP/1到HTTP/3,你都需要了解什么?
30|安全:JS代码和程序都需要注意哪些安全问题?
31|测试(一):开发到重构中的测试
32|测试(二):功能性测试
33|测试(三):非功能性测试
34|静态类型检查:ESLint语法规则和代码风格的检查
35|Flow:通过Flow类看JS的类型检查
36|包管理和分发:通过NPM做包的管理和分发
37|编译和打包:通过Webpack、Babel做编译和打包
38|语法扩展:通过JSX来做语法扩展
39|Polyfill:通过Polyfill让浏览器提供原生支持
40|微前端:从MVC贫血模式到DDD充血模式
41|大前端:通过一云多端搭建跨PC/移动的平台应用
42|元编程:通过Proxies和Reflect赋能元编程
当前位置:
首页>>
技术小册>>
JavaScript进阶实战
小册名称:JavaScript进阶实战
### 09 | 面向对象:通过词法作用域和调用点理解`this`绑定 在JavaScript的编程旅程中,深入理解面向对象编程(OOP)的概念及其实现方式是迈向高级开发者行列的关键一步。而`this`关键字,作为JavaScript中最为复杂且强大的特性之一,其绑定机制直接关联到面向对象编程中对象的行为与状态管理。本章节将深入探讨如何通过词法作用域(Lexical Scoping)和调用点(Call Site)来准确理解`this`的绑定规则,从而在实战中灵活运用这些概念,编写出更加清晰、可维护的代码。 #### 一、引言:为何需要理解`this` 在JavaScript中,`this`关键字的行为并不总是直观易懂的,尤其是在不同的函数调用上下文中。理解`this`的绑定规则对于编写可预测的代码至关重要,因为它决定了函数执行时上下文(即当前对象)的引用。掌握`this`,意味着你能更有效地控制对象的行为和状态,实现更加灵活的编程模式。 #### 二、词法作用域与动态作用域 在深入探讨`this`绑定之前,有必要先区分两个作用域的概念:词法作用域(Lexical Scoping)和动态作用域(也称为运行时作用域,Dynamic Scoping)。JavaScript采用词法作用域,这意味着作用域是基于函数声明时的位置来静态确定的,与函数如何被调用无关。然而,`this`的值并不是由词法作用域决定的,而是由函数的调用方式决定的,这体现了JavaScript中的动态绑定特性。 #### 三、`this`的四种绑定规则 为了全面理解`this`的绑定机制,我们需要掌握其四种基本的绑定规则:默认绑定、隐式绑定、显式绑定和`new`绑定。 ##### 1. 默认绑定 当函数作为非方法调用时(即不是作为某个对象的属性被调用),`this`默认绑定到全局对象(在严格模式下为`undefined`)。这是最基本的绑定规则,也是初学者最容易混淆的地方。 ```javascript function foo() { console.log(this.a); } var a = 2; foo(); // 输出: 2,因为foo在全局作用域中被调用,this指向全局对象 function bar() { "use strict"; console.log(this.a); } bar(); // 抛出TypeError,因为严格模式下this为undefined,尝试访问undefined.a会抛出错误 ``` ##### 2. 隐式绑定 当函数作为某个对象的属性被调用时,`this`会隐式地绑定到该对象上。这是实现对象方法最常见的方式。 ```javascript var obj = { a: 2, foo: function() { console.log(this.a); // this指向obj } }; obj.foo(); // 输出: 2 ``` ##### 3. 显式绑定 在某些情况下,我们需要明确指定函数执行时的`this`值,这时可以使用`call()`、`apply()`或`bind()`方法来实现显式绑定。 ```javascript function foo() { console.log(this.a); } var obj = { a: 2 }; foo.call(obj); // 输出: 2,通过call显式绑定this到obj var boundFoo = foo.bind(obj); boundFoo(); // 输出: 2,bind返回一个新函数,其this永久绑定到obj ``` ##### 4. `new`绑定 使用`new`操作符调用函数时,会创建一个新对象,并将这个新对象绑定到函数调用的`this`上。同时,还会执行构造函数来初始化这个新对象。 ```javascript function Foo(a) { this.a = a; } var bar = new Foo(2); console.log(bar.a); // 输出: 2,Foo的this绑定到了新创建的对象上 ``` #### 四、理解调用点 `this`的绑定并不是在函数定义时确定的,而是在函数被调用时,根据调用点的上下文动态决定的。因此,要准确判断`this`的指向,必须关注函数的调用方式,即调用点。 - **嵌套函数中的`this`**:嵌套函数中的`this`并不会继承外层函数的`this`值,而是根据它自己的调用点来确定。 ```javascript function foo() { console.log(this.a); function bar() { console.log(this.a); // bar的this取决于其调用方式,而非foo的this } bar(); // 如果没有显式绑定,则bar的this默认为全局对象或undefined(严格模式) } var obj = { a: 2, foo: foo }; obj.foo(); // foo的this指向obj,但bar的this不会继承这个绑定 ``` - **箭头函数中的`this`**:箭头函数不绑定自己的`this`,而是捕获其所在上下文的`this`值作为自己的`this`值。这意味着在箭头函数中,`this`指向的是定义它时所在的作用域中的`this`。 ```javascript function foo() { setTimeout(() => { console.log(this.a); // 这里的this指向foo调用时的this,即obj }, 100); } var obj = { a: 2, foo: foo }; obj.foo(); // 输出: 2 ``` #### 五、实战应用与最佳实践 理解`this`的绑定规则后,我们需要在实战中灵活运用这些知识。以下是一些建议: - **尽量避免在全局作用域中定义函数**,以减少全局`this`的依赖。 - **在对象方法中谨慎使用嵌套函数**,特别是当这些嵌套函数需要访问外部`this`时。考虑使用箭头函数或显式绑定来保持`this`的一致性。 - **利用`bind`、`call`和`apply`方法**来实现`this`的显式绑定,提高代码的灵活性和可维护性。 - **在构造函数中使用`new`绑定**,确保新创建的对象能正确初始化。 - **优先考虑使用ES6的类(Class)语法**,它提供了一种更清晰、更直观的方式来定义对象和其方法,同时自动处理`this`的绑定。 #### 六、总结 `this`在JavaScript中是一个复杂但强大的特性,它允许我们根据函数的调用点动态地改变函数的执行上下文。通过深入理解词法作用域与调用点的关系,以及`this`的四种绑定规则(默认绑定、隐式绑定、显式绑定和`new`绑定),我们可以编写出更加清晰、可预测和可维护的代码。在实战中,灵活运用这些概念,结合ES6的新特性,将帮助我们更高效地实现面向对象的编程模式。
上一篇:
08|深入理解继承、Delegation和组合
下一篇:
10|JS有哪8种数据类型,你需要注意什么?
该分类下的相关小册推荐:
web前端开发性能优化实战
Javascript编程指南
剑指javascript-ES6
Javascript重点难点实例精讲(一)
Node.js 开发实战
零基础学JavaScript
经典设计模式Javascript版
深入学习前端重构知识体系
JavaScript入门与进阶
KnockoutJS入门指南
Javascript-ES6与异步编程
npm script实战构建前端工作流