《你不知道的JavaScript》整理(二)——this
最近在读一本进阶的JavaScript的书《你不知道的JavaScript(上卷)》,这次研究了一下“this”。
当一个函数被调用时,会创建一个活动记录(执行上下文)。
这个记录会包含函数在哪里被调用(调用栈)、函数的调用方法、传入的参数等信息。
this就是记录的其中一个属性,会在函数执行的过程中用到。
this既不指向函数自身也不指向函数的作用域。
this实际上是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用。
一、调用位置
调用位置就在当前正在执行的函数的前一个调用中,源码查看。
function baz() {// 当前调用栈是:baz// 因此,当前调用位置是全局作用域console.log("baz");bar(); // <-- bar 的调用位置 }function bar() {// 当前调用栈是 baz -> bar// 因此,当前调用位置在 baz 中console.log("bar");foo(); // <-- foo 的调用位置 }function foo() {// 当前调用栈是 baz -> bar -> foo// 因此,当前调用位置在 bar 中console.log("foo"); } baz(); // <-- baz 的调用位置
二、绑定规则
你必须找到调用位置,然后判断需要应用下面四条规则中的哪一条。
1)默认绑定
最常用的函数调用类型:独立函数调用。可以把这条规则看作是无法应用其他规则时的默认规则。
function foo() {console.log(this.a); } var a = 2; foo(); // 2
2)隐式绑定
隐式绑定的规则是调用位置是否有上下文对象,或者说是否被某个对象拥有或者包含。
function foo() {console.log(this.a); } var obj = {a: 2,foo: foo }; obj.foo(); // 2
但有时候会出现隐式丢失。
function foo() {console.log(this.a); } var obj = {a: 2,foo: foo }; var bar = obj.foo; // 函数 var a = "oops, global"; // bar(); // "oops, global"
虽然bar是obj.foo的一个引用,但是实际上,它引用的是foo函数本身。
因此此时的bar()其实是一个不带任何修饰的函数调用,应用了默认绑定。
3)显式绑定
使用函数的call(..)和apply(..)方法。
function foo() {console.log(this.a); } var obj = {a: 2 }; foo.call(obj); // 2
在很多库中经常能看到bind方法,这是一种硬绑定,一种显式的强制绑定,下面是一种bind实现。
function foo(something) {console.log(this.a, something);return this.a + something; } // 简单的辅助绑定函数 function bind(fn, obj) {return function() {return fn.apply(obj, arguments);}; } var obj = {a: 2 }; var bar = bind(foo, obj); var b = bar(3); // 2 3 console.log(b); // 5
4)new绑定
使用new来调用函数,或者说发生构造函数调用时,会自动执行下面的操作:
1. 创建(或者说构造)一个全新的对象。
2. 这个新对象会被执行[[原型]]连接。
3. 这个新对象会绑定到函数调用的this。
4. 如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象。
function foo(a) {this.a = a; } var bar = new foo(2); console.log(bar.a); // 2
三、优先级
默认绑定的优先级是四条规则中最低的。
1)显式绑定优先级比隐式绑定要更高
function foo() {console.log(this.a); } var obj1 = {a: 2,foo: foo }; var obj2 = {a: 3,foo: foo };obj1.foo(); // 2 obj2.foo(); // 3 obj1.foo.call(obj2); // 3 obj2.foo.call(obj1); // 2
2)new绑定比隐式绑定优先级高
function foo(something) {this.a = something; } var obj1 = {foo: foo };obj1.foo(2); console.log(obj1.a); // 2var bar = new obj1.foo(4); console.log(obj1.a); // 2 console.log(bar.a); // 4
3)new绑定会修改显示绑定中this
function bind(fn, obj) {return function() {return fn.apply(obj, arguments);}; } function foo(something) { this.a = something; }var obj1 = {}; var bar = foo.bind(obj1); //bar被硬绑定到obj1上 bar(2); console.log( obj1.a ); // 2var baz = new bar(3); // console.log( obj1.a ); // 没有把obj1.a 修改为3,还是为2 console.log( baz.a ); // 3
4)判断this
1. 函数是否在new中调用(new绑定)?如果是的话this绑定的是新创建的对象。
var bar = new foo()
2. 函数是否通过call、apply(显式绑定)或者硬绑定调用?如果是的话,this绑定的是指定的对象。
var bar = foo.call(obj2)
3. 函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this绑定的是那个上下文对象。
var bar = obj1.foo()
4. 如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到undefined,否则绑定到全局对象。
var bar = foo()
四、绑定例外
1)被忽略的this
如果你把null或者undefined作为this的绑定对象传入call、apply或者bind。
这些值在调用时会被忽略,实际应用的是默认绑定规则。
function foo() {console.log(this.a); } var a = 2; foo.call(null); // 2
2)间接引用
你有可能(有意或者无意地)创建一个函数的“间接引用”。
在这种情况下,调用这个函数会应用默认绑定规则。
function foo() {console.log(this.a); } var a = 2; var o = { a: 3, foo: foo }; var p = { a: 4 }; o.foo(); // 3 (p.foo = o.foo)(); // 2
赋值表达式p.foo = o.foo的返回值是目标函数的引用,因此调用位置是foo()而不是p.foo()或者o.foo()。
3)软绑定
如果可以给默认绑定指定一个全局对象和undefined以外的值。
那就可以实现和硬绑定相同的效果,同时保留隐式绑定或者显式绑定修改this的能力。
可以通过一种被称为软绑定的方法来实现我们想要的效果。
Function.prototype.softBind = function(obj) {var fn = this;// 捕获所有 curried 参数var curried = [].slice.call(arguments, 1);var bound = function() {return fn.apply((!this || this === (window || global)) ? obj : this, curried.concat.apply(curried, arguments));};bound.prototype = Object.create(fn.prototype);return bound; };
function foo() {console.log("name: " + this.name); } var obj = { name: "obj" }, obj2 = { name: "obj2" }, obj3 = { name: "obj3" };var fooOBJ = foo.softBind( obj ); fooOBJ(); // name: obj <---- 应用了软绑定 obj2.foo = foo.softBind(obj); obj2.foo(); // name: obj2 fooOBJ.call( obj3 ); // name: obj3 setTimeout( obj2.foo, 10 );// name: obj <---- 应用了软绑定
软绑定版本的foo()可以手动将this绑定到obj2或者obj3上。
但如果应用默认绑定,则会将this绑定到obj。
《你不知道的JavaScript》整理(二)——this相关推荐
- 你不知道的JavaScript(二)
第三章 原生函数 JS有很多原生函数,为基本的数据类型值提供了封装对象,String,Number,Boolean等.我们可以通过{}.call.toString()来查看所有typeof返回obje ...
- 《你不知道的JavaScript上卷》知识点整理与读书笔记
各位路过的的大佬.求关注.求点赞.谢谢 第一部分 作用域和闭包 第1章 作用域是什么 1.1编译原理 1.2理解作用域 1.3作用域嵌套 1.5异常 第2章 词法作用域 2.1词法阶段 2.2欺骗词法 ...
- JS闭包—你不知道的JavaScript上卷读书笔记(二)
关于闭包,初学者会被绕的晕头转向,在学习的路上也付出了很多精力来理解. 让我们一起来揭开闭包神秘的面纱. 闭包晦涩的定义 看过很多关于闭包的定义,很多讲的云里雾里,晦涩难懂.让不少人以为闭包是多么玄乎 ...
- 读书笔记-你不知道的JavaScript(上)
本文首发在我的个人博客:http://muyunyun.cn/ <你不知道的JavaScript>系列丛书给出了很多颠覆以往对JavaScript认知的点, 读完上卷,受益匪浅,于是对其精 ...
- 你不知道的JavaScript 上卷 Part1
这篇博客躺在我的草稿箱里有一阵子了,差点给遗忘了哈哈. 前言 最近开始喜欢读一些书,从书中找答案,在阅读中查漏补缺. 记得小学初中时候最爱看书了,如今却不知怎的,习惯性从网络中摄取知识,搜 ...
- 【你不知道的JavaScript上卷】——作用域与闭包
原文: [你不知道的JavaScript上卷]--作用域与闭包 JS语言万变不离其宗,其中最常用.最重要的也就是常用的几个大概念.数据类型.作用域.原型链.闭包.this指针.异步,不同的人理解不一样 ...
- 微信小程序坐标位置接口使用整理(二)地图接口
微信小程序坐标位置接口使用整理(二) 微信小程序中实现更多地图服务,可以使用腾讯地图Api. 以下几个场景是使用地图Api调用. 官方Api:微信小程序JavaScript SDK | 腾讯位置服务 ...
- 《你不知道的 JavaScript》上卷之作用域和闭包
<你不知道的 JavaScript>是一个前端学习必读的系列,让不求甚解的JavaScript开发者迎难而上,深入语言内部,弄清楚JavaScript每一个零部件的用途.这本书介绍了该系列 ...
- Deep Learning(深度学习)学习笔记整理(二)
本文整理了网上几位大牛的博客,详细地讲解了CNN的基础结构与核心思想,欢迎交流 [1]Deep learning简介 [2]Deep Learning训练过程 [3]Deep Learning模型之: ...
- 精读《你不知道的javascript》中卷
前言 <你不知道的 javascript>是一个前端学习必读的系列,让不求甚解的JavaScript开发者迎难而上,深入语言内部,弄清楚JavaScript每一个零部件的用途.本书< ...
最新文章
- pci串口驱动安装失败_TSC TTP-243E Pluse装LTP并口驱动无法安装
- IHelloWorldService
- 我的编程能力从什么时候开始突飞猛进
- 均值定理最大值最小值公式_如何理解中值定理?三大微分中值定理的几何意义及其证明介绍。...
- Composite(组合)--对象结构型模式
- error: stray '\343' in program 问题解决
- sql 修改字段 删除字段操作
- 项目部署,环境搭建(pip/ makefile)
- Java中的静态变量、静态方法问题
- win10 UWP RSS阅读器
- 一文搞定权限设计模型(RBAC,ABAC)超详细图文解析
- C语言中关于中文字符的存储及相关探索
- html ol标签用罗马数字,HTML重点标签总结
- mysql analyze_技术分享 | MySQL EXPLAIN ANALYZE
- 软件测试面试经常会被问到的三大问题(面试常考)
- 小勇个人理财 怎么用
- 力扣 2104. 子数组范围和
- Debain8 Tomcat8 JDK8 实现SSL双向验证
- 【转】为什么需要异步
- <MySQL>表中添加和删除字段
热门文章
- P4213 【模板】杜教筛(杜教筛)题解
- Aoto Deploy Project 1.0 (rebuild version)
- Visual Studio(Year)编辑器调试在IIS发布的Web程序出现错误的解决
- [Java] 获取当前Project所在的路径
- 百度编辑器ueditor字体添加
- A2D JS框架 - loadScript实现
- LLBL Gen 元数据编程 LLBL Gen Meta-data Programming
- linux平台 oracle 数据库 安装文档
- 约束理论学习随笔(1)
- 关于data.table中i, j, by都为数字的理解