说说这个this啊

当一个函数被调用时,会创建一个活动记录(也称为执行上下文)。这个记录会包含调用栈,函数的调用方法,传入的参数等信息。this就是记录的其中一个属性,会在函数调用时用到。因此this是在运行时进行绑定的,并不在编写时绑定,他的上下文取决于函数调用时的各种条件。

默认绑定

12345

var a = 2;function A(){ console.log(this.a)}A(); //2

上述代码中foo()不带任何修饰函数引用进行调用的,因此只能使用默认绑定,无法应用其他规则。 像这种独立函数调用是最常见的方式。值得一提的是在严格模式下,全局对象无法使用默认绑定,this绑定的是undefined。

隐式绑定

话不多说,先上代码

12345678910

var a = 2;function A(){ console.log(this.a);}var obj = { a:3, A:A}obj.A(); //3

在本例中,函数A被作为obj的A属性的值,严格来说,obj并不包含A函数,但是它保有对A函数的引用,当obj调用A方法时,隐式绑定规则将this绑定到了obj上,所以this.a就是obj.a。 接下来稍微改一下代码:

1234567891011121314

var a = 2;function A(){ console.log(this.a);}function B(fn){ fn()}var obj = { a:3, A:A}B(obj.A); //2

在本例中,obj.A作为参数传递给了函数B,其实函数A最终被调用的位置是在fn(),此时的fn是没有函数修饰的,上述代码相当于

123456789101112

var a = 2;function A(){ console.log(this.a);}var obj = { a:3, A:A}var C=obj.A; //敲重点C(); //2

其实参数传递相当于隐式赋值,C保存的是对函数A的引用,这其实跟obj没什么关系。这种情况常被称作为隐式丢失。 最近在刷题时,有看到这么几段代码:

123456

var a = 1;setTimeout(function(){ var a = 2; console.log(this.a);},1000);//otherthing

当时看到这段代码,立即想到了eventLoop,当执行到setTimeout时,先将回调函数注册,1000ms后将函数推入事件队列,然后检查主线程即调用栈是否为空,如果为空,将队列里的函数按照先入先出原则push到调用栈,想了这么多呢,我发现我确实想多了,this在回调里,ojbk,输出1。 由此想到开发时遇到的情况,在使用第三方库的时候偶尔会用到此类函数,

12345

//Action为第三方封装的函数Action(function(){ //.... console.log(this);//这一行是自己意淫的},selector);

这种情况下,this又绑定的是谁呢?我不禁这样问自己。/吐口水 /吐口水,其实前面说回调里的this绑定的全局对象,有点武断,文章刚开始,就说了,这取决于函数调用时的各种条件。如果Action是这么封装的:

1234

function Action(callback , arg){ //... callback()}

无可厚非,this绑定的是全局对象,但是如果:

12345

function Action(callback , selector){ //... var dom = document.queryselector(selector); dom.callback();}

那么此时的this无疑就是dom。 后来逛掘金时,又看到这段代码:

123456789101112131415

var a = 1;var obj = { a: 2, B: function(){ var a = 3; (function(){ var a = 4; console.log(this.a); } )(); }}obj.B();

在这段代码里,其实匿名自执行函数和回调函数this绑定的都是全局对象(非严格模式),因为它们都是不加修饰的函数调用,应用的时默认绑定规则(我好像该写在上一个篇幅,嘤嘤嘤(’qAq’)),想当初我还沿着作用域链去查找this绑定的是谁呢,不得不说《你不知道的javascript》,真不愧是神书。

显式绑定

硬绑定

在js中,可以通过一些方法来改变this的指向,在《你不知道的js》里被称为硬绑定,这些方法有apply,call,bind。

  • apply

此方法会执行函数。apply方法只能接受两个参数,一个是this指向的上下文对象,另一个是传递给函数的参数列表(可选),这个参数列表是以数组的形式。

12345678

var n = 2;var obj = { n:1}function getN(){ console.log(this.n);}getN.apply(obj);

  • call

此方法会执行函数。call方法和apply方法的使用唯一的差别就是参数的差别,call第一个参数和apply 一样是this指向的上下文对象,但是要传给函数的参数,是单个传过去的,形如 fun.call(obj,arg1,arg2,arg3…)

  • bind

此方法不会执行函数,而是返回一个新的函数,这个新的函数被指定了 this 的上下文,后面的参数是执行函数需要传入的参数。 方法比较简单就不再举例。

new

如果函数或者方法调用之前带有关键字new,它就构成构造函数调用,也就是new绑定。 构造函数通常不使用return关键字,它们通常初始化新对象,当构造函数的函数体执行完毕时,它会显式返回。在这种情况下,构造函数调用表达式的计算结果就是这个新对象的值

12345

function A(){ this.a = 2;}var test = new A();console.log(test);//{a:2}

如果构造函数使用return语句但没有指定返回值,或者返回一个原始类型的值,那么这时将忽略返回值,同时使用这个新对象作为调用结果

123456

function A(){ this.a = 2; return;}var test = new A();console.log(test);//{a:2}

如果构造函数显式地使用return语句返回一个对象,那么调用表达式的值就是这个对象

1234567

var obj = {a:1};function A(){ this.a = 2; return obj;}var test = new A();console.log(test);//{a:1}

尽管有时候构造函数看起来像一个方法调用,它依然会使用这个新对象作为this。也就是说,在表达式new o.m()中,this并不是o

12345678

var obj = { a: function(){ return this; }}var o = new obj.a();console.log(o === obj) //falseconsole.log(o.constructor === obj.a) //true

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏深度学习思考者

一文搞懂C/C++中指针那些事(上篇)

一 指针变量 1.间接存取        指针变量的值为地址;普通变量的值为数据;其中“*”为指针运算符。&是地址操作符,用来引用一个内存地址。通过在变量名...

20610
来自专栏星回的实验室

js重修课[四]:函数

函数有两种定义方法:定义表达式如var f = function(){};和声明语句如function f(){}。须知在变量提前这一现象中,声明语句可被提前,...

862
来自专栏西安-晁州

js数组去重

对于如下对象数组 [{id: 0, name: "name1"}, {id: 1, name: "name2"},{id: 1, name: "name2"},...

2330
来自专栏我爱编程

Day7函数式编程3/3

装饰器 由于函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数。 >>> def now(): ... print('2018...

3697
来自专栏Esofar 开发日记

JavaScript权威指南 - 对象

JavaScript对象可以看作是属性的无序集合,每个属性就是一个键值对,可增可删。 JavaScript中的所有事物都是对象:字符串、数字、数组、日期,等等...

822
来自专栏Golang语言社区

【Go 语言社区】Go语言类型转换

类型转换是一种可变从一种数据类型转换成另一种数据类型。例如,如果要存储一个long值转成一个简单的整数,那么可以强制类型转换long为int。可以从一种类型使用...

36114
来自专栏破晓之歌

JAVA入门1 原

下载地址:http://www.oracle.com/technetwork/java/javase/downloads/jdk9-downloads-3848...

1312
来自专栏我爱编程

Day10面向对象高级编程1/3

使用slots 正常情况下,当我们定义了一个class,创建了一个class的实例后,我们可以给该实例绑定任何属性和方法,这就是动态语言的灵活性。 class...

3255
来自专栏程序生活

Python中defaultdict用法

1976
来自专栏数据结构笔记

数据结构(四):栈的应用之表达式求值

用户从控制台输入一个数学表达式(所有输入均合法),数学表达式只包含四则运算,程序需输出表达式对应的结果,如:

742

扫码关注云+社区