You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
classHero{constructor(heroName){this.heroName=heroName;}dialogue(){console.log(`I am ${this.heroName}`)}}constbatman=newHero("Batman");batman.dialogue();
thisArg:在 fun 函数运行时指定的 this 值。需要注意的是,指定的 this 值并不一定是该函数执行时真正的 this 值,如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的自动包装对象。
argsArray:一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 fun 函数。如果该参数的值为null 或 undefined,则表示不需要传入任何参数。从ECMAScript 5 开始可以使用类数组对象。浏览器兼容性请参阅本文底部内容。
如上概念 apply 类似.区别就是 apply 和 call 传入的第二个参数类型不同。
call 的语法为:
fun.call(thisArg[,arg1[,arg2[, ...]]])
需要注意的是:
调用 call 的对象,必须是个函数 Function
call 的第一个参数,是一个对象。 Function 的调用者,将会指向这个对象。如果不传,则默认为全局对象 window。
第二个参数开始,可以接收任意个参数。每个参数会映射到相应位置的 Function 的参数上。但是如果将所有的参数作为数组传入,它们会作为一个整体映射到 Function 对应的第一个参数上,之后参数都为空。
apply 的语法为:
Function.apply(obj[,argArray])
需要注意的是:
它的调用者必须是函数 Function,并且只接收两个参数
第二个参数,必须是数组或者类数组,它们会被转换成类数组,传入 Function 中,并且会被映射到 Function 对应的参数上。这也是 call 和 apply 之间,很重要的一个区别。
Function.prototype.NealApply=function(context,args){context=context||window;args=args||[];//给context新增一个独一无二的属性以免覆盖原有属性constkey=Symbol();context[key]=this;//这里的 this 是函数context[key](...args);}
其实这个时候我们用起来已经有效果了。
这个时候我们已经执行完了,我们需要将结果返回,并且清理自己产生的垃圾
Function.prototype.NealApply=function(context,args){context=context||window;args=args||[];//给context新增一个独一无二的属性以免覆盖原有属性constkey=Symbol();context[key]=this;//这里的 this 是 testFunconstresult=context[key](...args);// 带走产生的副作用deletecontext[key];returnresult;}varname='Neal'functiontestFun(...args){console.log(this.name,...args);}consttestObj={name:'Nealyang'}testFun.NealApply(testObj,['一起关注',':','全栈前端精选']);
前言
【THE LAST TIME】一直是我想写的一个系列,旨在厚积薄发,重温前端。
也是给自己的查缺补漏和技术分享。
欢迎大家多多评论指点吐槽。
讲道理,这篇文章有些拿捏不好尺度。准确的说,这篇文章讲解的内容基本算是基础的基础了,但是往往这种基础类的文章很难在啰嗦和详细中把持好。文中道不到的地方还望各位评论多多补充指正。
THE LAST TIME 系列
This
相信使用过 JavaScript 库做过开发的同学对
this
都不会陌生。虽然在开发中this
是非常非常常见的,但是想真正吃透this
,其实还是有些不容易的。包括对于一些有经验的开发者来说,也都要驻足琢磨琢磨~ 包括想写清楚 this 呢,其实还得聊一聊 JavaScript 的作用域和词法This 的误解一:this 指向他自己
通过运行上面的代码我们可以看到,
foo
函数的确是被调用了十次,但是this.count
似乎并没有加到foo.count
上。也就是说,函数中的this.count并不是foo.count。This 的误解二:this 指向他的作用域
另一种对this的误解是它不知怎么的指向函数的作用域,其实从某种意义上来说他是正确的,但是从另一种意义上来说,这的确是一种误解。
明确的说,this不会以任何方式指向函数的词法作用域,作用域好像是一个将所有可用标识符作为属性的对象,这从内部来说他是对的,但是JavaScript代码不能访问这个作用域“对象”,因为它是引擎内部的实现
全局环境中的 This
既然是全局环境,我们当然需要去明确下
宿主环境
这个概念。简而言之,一门语言在运行的时候需要一个环境,而这个环境的就叫做宿主环境
。对于 JavaScript 而言,宿主环境最为常见的就是 web 浏览器。如上所说,我们也可以知道环境不是唯一的,也就是 JavaScript 代码不仅仅可以在浏览器中跑,也能在其他提供了
宿主环境
的程序里面跑。另一个最为常见的就是Node
了,同样作为宿主环境
,node
也有自己的JavaScript
引擎:v8
.this
等价于window
对象var
声明一个变量等价于给this
或者window
添加属性var
或者let(ECMAScript 6)
,你就是在给全局的this
添加或者改变属性值REPL
来执行程序,那么this
就等于global
this
并不指向global
而是module.exports
为{}
REPL
执行一个脚本文件,用var声明一个变量并不会和在浏览器里面一样将这个变量添加给thisnode
环境里,用REPL运行脚本文件的时候,如果在声明变量的时候没有使用var
或者let
,这个变量会自动添加到global
对象,但是不会自动添加给this
对象。如果是直接执行代码,则会同时添加给global
和this
这一块代码比较简单,我们不用码说话,改为用图说话吧!
函数、方法中的 This
当一个函数被调用的时候,会建立一个活动记录,也成为执行环境。这个记录包含函数是从何处(call-stack)被调用的,函数是 如何 被调用的,被传递了什么参数等信息。这个记录的属性之一,就是在函数执行期间将被使用的this引用。
函数中的 this 是多变的,但是规则是不变的。
你问这个函数:”~~老妹~~~ oh,不,函数!谁点的你?“
”是他!!!“
那么,this 就指向那个家伙!再学术化一些,所以!一般情况下!this不是在编译的时候决定的,而是在运行的时候绑定的上下文执行环境。this 与声明无关!
记住上面说的,
谁点的我!!!
=>foo()
=windwo.foo()
,所以其中this 执行的是 window 对象,自然而然的打印出来 2.虽然这位 xx 被点的多了。。。但是,我们只问点他的那个人,也就是
ojb2
,所以this.a
输出的是 42.构造函数中的 This
还是如上文说到的,this,我们不看在哪定义,而是看运行时。所谓的构造函数,就是关键字
new
打头!谁给我 new,我跟谁
其实内部完成了如下事情:
call、apply、bind 中的 this
在很多书中,call、apply、bind 被称之为 this 的强绑定。说白了,谁出力,我跟谁。那至于这三者的区别和实现以及原理呢,咱们下文说!
上面的
dialogue.call(hero)
等价于dialogue.apply(hero)``dialogue.bind(hero)()
.其实也就是我明确的指定这个 this 是什么玩意儿!
箭头函数中的 this
箭头函数的 this 和 JavaScript 中的函数有些不同。箭头函数会永久地捕获 this值,阻止 apply或 call后续更改它。
箭头函数内的 this值无法明确设置。此外,使用 call 、 apply或 bind等方法给 this传值,箭头函数会忽略。箭头函数引用的是箭头函数在创建时设置的 this值。
箭头函数也不能用作构造函数。因此,我们也不能在箭头函数内给 this设置属性。
class 中的 this
虽然 JavaScript 是否是一个面向对象的语言至今还存在一些争议。这里我们也不去争论。但是我们都知道,类,是 JavaScript 应用程序中非常重要的一个部分。
类通常包含一个 constructor , this可以指向任何新创建的对象。
不过在作为方法时,如果该方法作为普通函数被调用, this也可以指向任何其他值。与方法一样,类也可能失去对接收器的跟踪。
构造函数里的 this指向新创建的 类实例。当我们调用 batman.dialogue()时, dialogue()作为方法被调用, batman是它的接收器。
但是如果我们将 dialogue()方法的引用存储起来,并稍后将其作为函数调用,我们会丢失该方法的接收器,此时 this参数指向 undefined 。
出现错误的原因是JavaScript 类是隐式的运行在严格模式下的。我们是在没有任何自动绑定的情况下调用 say()函数的。要解决这个问题,我们需要手动使用 bind()将 dialogue()函数与 batman绑定在一起。
this 的原理
我们都说,this指的是函数运行时所在的环境。但是为什么呢?
![](https://camo.githubusercontent.com/7db3550e3ceecf7a1043c1f75fa181d48da09ed1d53291c05955d73a29ed41c8/68747470733a2f2f696d672e616c6963646e2e636f6d2f7466732f5442313055684f68713637674b306a535a4648585861396a5658612d3431312d3237312e706e67)
我们都知道,JavaScript 的一个对象的赋值是将地址赋值给变量的。引擎在读取变量的时候其实就是要了个地址然后再从原地址读出来对象。那么如果对象里属性也是引用类型的话(比如
function
),当然也是如此!而JavaScript 允许函数体内部,引用当前环境的其他变量,而这个变量是由运行环境提供的。由于函数又可以在不同的运行环境执行,所以需要个机制来给函数提供运行环境!而这个机制,也就是我们说到心在的 this。this的初衷也就是在函数内部使用,代指当前的运行环境。
obj.foo()
是通过obj
找到foo
,所以就是在obj
环境执行。一旦var foo = obj.foo
,变量foo
就直接指向函数本身,所以foo()
就变成在全局环境
执行.总结
小试牛刀
call & applay
上文中已经提到了
call
、apply
和bind
,在MDN
中定义的apply
如下:语法:
如上概念
apply
类似.区别就是 apply 和 call 传入的第二个参数类型不同。call 的语法为:
需要注意的是:
apply 的语法为:
需要注意的是:
记忆技巧:apply,a 开头,array,所以第二参数需要传递数据。
核心理念
借!
对,就是借。举个栗子!我没有女朋友,周末。。。额,不,我没有摩托车🏍,周末的时候天气很好,想出去压弯。但是我有没有钱!怎么办呢,找朋友借用一下啊~达到了目的,还节省开支!
放到程序中我们可以理解为,某一个对象没有想用的方法去实现某个功能,但是不想浪费内存开销,就借用另一个有该方法的对象去借用一下。
说白了,包括 bind,他们的核心理念都是借用方法,已达到节省开销的目的。
应用场景
Object.prototype.toString
用来判断类型再合适不过,尤其是对于引用类型来说。简易版继承
bind
bind 和 call/apply 用处是一样的,但是
![](https://camo.githubusercontent.com/c9f7797e1ee36145d105a2a51ec1c9a57dd7b0750005ea01e106b1c0eeaa8c38/68747470733a2f2f696d672e616c6963646e2e636f6d2f7466732f5442312e7636536a613631674b306a535a466c585858444b4658612d3335312d3133372e706e67)
bind
会**返回一个新函数!不会立即执行!**而call/apply
改变函数的 this 并且立即执行。应用场景
上述代码也是一个经典的面试题,具体也不展开了。
说道 this 丢失问题,应该最常见的就是 react 中定义一个方法然后后面要加
bind(this)
的操作了吧!当然,箭头函数不需要,这个咱们上面讨论过。手写实现
apply
其实这个时候我们用起来已经有效果了。
执行结果就是上方的截图。
一上来不说优化是因为希望大家把精力放到核心,然后再去修边幅! 罗马不是一日建成的,看别人的代码多牛批,其实也是一点一点完善出来的。
道理是这么个道理,其实要做的优化还有很多,这里我们就把 context 的判断需要优化下:
别的优化大家可以添加各种的用户容错。比如对第二个参数的类数组做个容错
call
丐版实现:
bind
bind的实现讲道理是比 apply 和call 麻烦一些的,也是面试频考题。因为需要去考虑函数的拷贝。但是也还是比较简单的,网上也有很多版本,这里就不具体展开了。具体的,咱们可以在群里讨论~
最后
别忘记了上面 this 的考核题目啊,同学,该交卷了!
参考链接
The text was updated successfully, but these errors were encountered: