【 js 基础 】【读书笔记】关于this
this 关键字是 Javascript 中很特别的一个关键字,被自动定义在所有函数的作用域中。this提供了一种更优雅的方式隐式“传递”一个对象的引用。今天就来说说 this 的指向问题。
this 是在运行时,也就是说函数被调用时进行绑定,而不是在编写时进行绑定,它的上下文取决于函数调用时的各种条件。this 的绑定和函数声明的位置没有任何关系,只取决于函数在哪里被调用。
一、调用位置
所谓 调用位置 就是 函数被调用的位置。找到这个位置并不是很简单的事情,因为有的时候,真正的调用位置会被隐藏起来。
这里要做的就是分析调用栈,即为了到达执行位置所调用的所有函数,我们关心的调用位置就在当前正在执行的函数的前一个调用中。
举个例子:
function test(){
// 当前调用栈是 test
// 因此当前调用位置是 全局作用域 console.log("test"); bar(); // bar的调用位置
} function bar(){
// 当前的调用栈是 test -> bar
// 因此当前的调用位置在 test 中 console.log("bar");
foo(); // foo的调用位置
} function foo(){
// 当前的调用栈 test -> bar -> foo
// 因此调用位置在 bar 中 console.log("foo");
} test(); // test的调用位置
那么知道调用位置之后,我们来看看 它是如何决定 this 的绑定对象的。
二、绑定规则
1、默认绑定
首先要介绍的是最常用的函数调用类型:独立函数调用。可以把这条规则看作是无法应用其他规则的默认规则。
例子:
function test(){
console.log(this.a);
}
var a = 2;
test(); //
声明在全局作用域中的变量(比如var a=2)就是全局对象的一个同名属性,即浏览器环境下的 window.a=2;
接下来,我们可以看到调用 test() 时,this.a 被解析成了全局变量 a,为什么呢?因为在这个例子中,test()是直接使用不带任何修饰的函数引用进行调用的,应用了默认规则,this 指向了全局对象。
这里要注意一个问题,如果使用了严格模式(strict mode),则不能将全局对象用于默认绑定,因此 this 会绑定到 undefined。
例子:
function test(){
"use strict";
console.log(this.a);
}
var a = 2;
test(); //TypeError:this is undefined
但是这里有一个非常重要的细节。虽然 this 的绑定规则完全取决于调用位置,但是只有 test() 运行在非 strict mode 下时,默认绑定才能绑定到全局对象;在严格模式下调用 test() 则不影响默认绑定。
例子:
function test(){
console.log(this.a)
}
var a =2;
(function(){
"use strict";
foo(); //
})()
比较一下上面的两个例子,发现不同 strict mode 中 this 应用默认绑定的差别。
2、隐式绑定
另一条需要考虑的规则是调用位置是否有上下文,或者说是否被某个对象拥有或者包含。
例子:
function test(){
console.log(this.a)
} var obj = {
a:2,
test:test
}; obj.test(); //
当 test() 被调用时,它的前面加上了对 obj 的引用。当函数引用上下文对象时,隐式绑定规则会把函数调用中的 this 绑定到这个上下文对象。在本例中, test() 调用时 this 被绑定到 obj,因此 this.a 即 obj.a。
在隐式绑定中需要注意两点:
a、对象属性引用链中只有上一层或者说最后一层在调用中起作用。
例子:
function test(){
console.log(this.a)
} var obj = {
a:2,
test:test
}; var obj_1 = {
a:4,
obj:obj
} obj_1.obj.test(); //
b、隐式丢失。隐式绑定的函数丢失绑定对象,即应用默认绑定,从而把 this 绑定到全局对象上或者是 undefined(如果是严格模式)。
例子:
function test(){
console.log(this.a)
} var obj = {
a:2,
test:test
}; var foo = obj.test; var a = 4; foo(); //
这里的 foo 实际上 引用的是 test 函数本身,当在执行 var foo = obj.test; 时,就把 obj.test 对 test 的引用地址复制给了 foo,也就是说 foo 和 obj.test 指向同一个 引用地址。所以 调用 foo 其实是一个不带任何修饰的函数调用,应用了默认绑定。关于复制的引用这里要是还是不是很明白 可以看一下,http://www.cnblogs.com/lijiayi/p/jsdeeepcopy.html 中的第一部分基本类型 和 引用类型。
3、显示绑定
这里主要就是指通过 call() 、apply() 的函数调用,实现的绑定方式。
它们的第一个参数是一个对象,是给 this 准备的,接着在调用函数时将其绑定到 this。因为你可以直接指定 this 的绑定对象,所以就叫显示绑定。
例子:
function test(){
console.log(this.a)
} var obj = {
a:2
}; test.call(obj) //
通过 test.call() ,我们可以在调用 test 时强制把它的 this 绑定到 obj 上。
这里多说一点:如果你传入了一个原始值(字符串类型,布尔值型或者数字类型)来当作 this 的绑定对象,这个原始值会被转换成它的对象形式(也就是 new String() 、new Boolean() 或者 new Number() )。这通常被称为 “装箱”。
在显示绑定中需要注意一点:
显示绑定中 call apply 或者 bind 中传入 null 或者 undefined ,这些值在调用时会被忽略,实际应用的是默认绑定。
例子:
function test(){
console.log(this.a)
}
var a = 2;
test.call(null); //
你或许会想,我怎么会传个 null ?
一种非常常见的做法是 使用 apply 来 “展开” 数组,并当作参数传入一个函数。
例子:
function test(a,b){
console.log("a:"+ a + ",b:" + b)
}
foo.apply(null,[2,3]); // a:2,b:3
另一种做法 用bind 可以对参数进行柯里化(预先设置一些参数)
例子:
function test(a,b){
console.log("a:"+ a + ",b:" + b)
} var bar = test.bind(null,2);
bar(3) // a:2,b:3
以上两种方法都需要传入一个参数当作 this 的绑定对象,但如果函数并不关系 this 的话,你仍然需要一个占用值,这时 null 可能是一个不错的选择。
4、new 绑定
使用 new 来调用函数,或者说是发生构造函数调用时,会自动执行下面的操作。
a、创建(或者说构造)一个全新的对象。
b、这个新对象会被执行 [[prototype]] 连接。
c、这个新对象会绑定到函数调用的 this。
d、如果函数没有返回其他对象,那么 new 表达式中的函数会自动返回这个新对象。
例子:
function test(a){
this.a = a
} var bar = new test(2);
console.log(bar.a); //
使用 new 来调用 test() 时 ,我们会构造一个新对象并把它绑定到 test() 调用中的 this 上。
三、优先级
现在你已经知道 this 绑定的四条规则,那么当某个调用位置可以应用多个规则怎么办?
在这里我们说一下,上面四条规则的优先级:
new 绑定 > 显示绑定 > 隐式绑定 > 默认绑定
总结:当我们在碰到 this 的时候先找到调用位置,然后按照以下顺序应用调用规则:
1、函数是否在 new 中调用,即应用的 new 绑定?如果是的话 this 绑定的是新创建的对象。
var bar = new test();
2、函数是否通过 call 和 apply 调用,即应用 显示绑定?如果是的话 this 绑定的是指定的对象。
test.call(obj)
3、函数是否在某个上下文中调用,即应用 隐式绑定? 如果是的话 this 绑定的是那个上下文对象。
obj.test()
4、如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到 undefined ,否则绑定到全局对象。
test()
四、es6 中的 this
在 es 6 中 使用了一种无法应用上面这些规则的特殊函数类型:箭头函数。
箭头函数不是使用 function 关键字定义的,而是使用被称为 “胖箭头” 的操作符 => 定义的。箭头函数不使用 this 的四种标准规则,而是根据外层(函数或者全局)作用域来决定 this。
例子:
function test(){
return (a)=>{
console.log(this.a)
}
} var obj1 = {
a:2
} var obj2 = {
a:4
} var bar = test.call(obj1);
bar.call(obj2); // 2,不是4!
test 内部创建的箭头函数会捕获调用时 test() 的 this。由于 test 的 this 绑定到了 obj 1,bar(引用箭头函数) 的 this 也会绑定到 obj 1 ,箭头函数的绑定无法被修改(new也不行)。箭头函数会继承外层函数调用的 this 绑定。
引用并感谢:
你不知道的JavaScript(上卷) (炒鸡推荐大家看)
【 js 基础 】【读书笔记】关于this的更多相关文章
- JS基础知识笔记
2020-04-15 JS基础知识笔记 // new Boolean()传入的值与if判断一样 var test=new Boolean(); console.log(test); // false ...
- 【js】【读书笔记】廖雪峰的js教程读书笔记
最近在看廖雪峰的js教程,重温了下js基础,记下一些笔记,好记性不如烂笔头嘛 编写代码尽量使用严格模式 use strict JavaScript引擎是一个事件驱动的执行引擎,代码总是以单线程执行 执 ...
- handlebars.js基础学习笔记
最近在帮学校做个课程网站,就有人推荐用jquery+ajax+handlebars做网站前端,刚接触发现挺高大上的,于是就把一些基础学习笔记记录下来啦. 1.引用文件: jquery.js文件下载:h ...
- 两万字Vue.js基础学习笔记
Vue.js学习笔记 目录 Vue.js学习笔记 ES6语法 1.不一样的变量声明:const和let 2.模板字符串 3.箭头函数(Arrow Functions) 4. 函数的参数默认值 5.Sp ...
- js高程读书笔记(1-3章)
一.js简介 js是一种专为与网页交互而设计的脚本语言,由以下三个不同的部分组成: 1.ECMAScript,由ECMA-262(它规定了语言的这些组成部分:语法,类型,语句,关键字,保留字,操作符, ...
- JS高程读书笔记-第一、二章-内附在线思维导图和quizlet卡片
之前在kindle上买了高程,今天又到了纸质的<JavaScript语言精粹>,<高性能JavaScript>,<JavaScipt设计模式>,开始读书之旅啦. 我 ...
- javascript-智能社-JS基础A笔记
JavaScript基础A JavaScript组成 ECMA : 全称ECMAScript,解释器.计算机语言的翻译 DOM:全称Document Object Model,赋予了JS操作HTML的 ...
- Javascript进阶篇——(JS基础语法)笔记整理
根据慕课网学习整理到一起的笔记,把东西整理到一起看起来比较方便 什么是变量字面意思:变量是可变的量:编程角度:变量是用于存储某种/某些数值的存储器.我们可以把变量看做一个盒子,盒子用来存放物品,物品可 ...
- 两万字Vue.js基础学习笔记(二)
Vue.js学习笔记(二) 4.模块化开发 ES6模块化的导入和导出 我们使用export指令导出了模块对外提供的接口,下面我们就可以通过import命令来加载对应的这个模块了 首先,我们需要在HTM ...
- node.js 基础学习笔记3 -express
1.工作原理 当通过app.js建立的服务器时,会看到一个简单的页面.返回页面时,浏览器会向服务器发送请求.app会解析请求的路径,调用相应的逻辑,调用对应的视图模板,传递对象数值,最终生成HTML页 ...
随机推荐
- drf-视图的理解
1. 类视图 写视图的步骤: 1. 数据库查询, 2. 构建序列化器, 进行序列化操作, 返回数据 一. 两大基类 >1 APIView (以常规的方法实现get po ...
- SQLServer 在Visual Studio的2种连接方法
一.Sql Server 在Visual Studio的连接有两种方法: (1)本地计算机连接; string s = "Data Source=计算机名称;initial Catalog= ...
- 在没有任何投票节点情况下将从节点转换为Primary节点脚本
cfg={ "_id": "rs01", "version": 2, "protocolVersion": Number ...
- [Leetcode]134.加油站
这一题是贪心不是模拟 是贪心不是模拟 是贪心不是模拟! 如果用模拟的做法会比较慢,也失去了做这一题的趣味了. 模拟的方法很简单,就是每一个加油站都做起点模拟一遍,试一下能不能完成一圈,能完成一圈就保存 ...
- 跨站脚本攻击(xss)理解
一 概念 攻击者不直接攻击受害者,而是利用受害者登陆的网站中的漏洞,对受害者进行攻击. 二 危害 由于js本身的限制,并不能直接对用户的电脑造成侵害,但是可以: 1. 获取用户的storage,c ...
- Scanner的概述与String类的构造和使用_DAY12
1:Scanner的概述(理解) 1)Scanner是JDK5以后出现的方便我们从键盘接受数据的类. 2)Scanner的构造格式: Scanner sc = new Scanner(System.i ...
- flume常用组件
Flume组件 1. Source NetCat Source:绑定的端口(tcp.udp),将流经端口的每一个文本行数据作为Event输入: type:source的类型,必须是netcat. ...
- 执行bin/hdfs haadmin -transitionToActive nn1时出现,Automatic failover is enabled for NameNode at bigdata-pro02.kfk.com/192.168.80.152:8020 Refusing to manually manage HA state的解决办法(图文详解)
不多说,直接上干货! 首先, 那么,你也许,第一感觉,是想到的是 全网最详细的Hadoop HA集群启动后,两个namenode都是standby的解决办法(图文详解) 这里,nn1,不多赘述了.很简 ...
- php的explode()和implode()方法
php 中,字符串与数组互转 拆分字符串 到数组 explode() - -(其他语言中的 split) 将数组连接成字符串 implode() <?php $test = ' ...
- Redis Cluster高可用集群在线迁移操作记录
之前介绍了redis cluster的结构及高可用集群部署过程,今天这里简单说下redis集群的迁移.由于之前的redis cluster集群环境部署的服务器性能有限,需要迁移到高配置的服务器上.考虑 ...