Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

如何正确判断this的指向?(注意区分严格模式和非严格模式) #1

Open
YvetteLau opened this issue May 20, 2019 · 68 comments

Comments

@YvetteLau
Copy link
Owner

No description provided.

@simplast
Copy link

console.log(this)

@YvetteLau
Copy link
Owner Author

console.log(this)

不使用 console alert 等的情况下~
你可以当成这个问题,是你面试一家你非常想去的企业时,面试官问你的~

@zhangxianhui
Copy link

如何判断this 指向 就需要找到这个函数的直接调用位置 找位置之后再确定this指向
1.new 调用 直接指向new 创建的新对象
2.上下文调用 直接指定上下文
3.call .apply 指向指定的 绑定对象
4.全局函数下 指向全局
es6的箭头函数 指向外层函数的this

@zhangxianhui
Copy link

默认情况下 严格模式绑定到undefined,非严格模式 绑定全局上

@88flyfish
Copy link

this指的是当前执行环境的上下文,非严格模式的全局对象绑定在window上面,在严格模式下,全局对象将无法使用默认绑定,因此this会绑定到undefined

@shenanheng
Copy link

谁调用,this就指向谁;在严格模式下,this指向的是undeined,在非严格模式下this指向的是全局变量,通过一些特殊的方式可动态改变this,apply,call;而bind是让this再创建时就让他固定;箭头函数也可以改变this,指向的是当前全局变量

@chang229
Copy link

首先this并不会指向函数本身。
1,当函数在没有任何修饰的情况下调用,非严格模式下,this指向window,严格模式下this指向undefined。(默认绑定)
2,当函数由一个对象引导调用时,this指向该对象。(隐式绑定)
3,函数通过apply,call,bind绑定时,this指向绑定的对象。(显式绑定)
4,当函数被当做构造函数使用,又new引导调用时,this只想new创建出来的对象。(new绑定);
其优先级为:new绑定 > 显示绑定 > 隐式绑定 > 默认绑定;
箭头函数其this取决于函数定义时所在的上下文。

@jizx
Copy link

jizx commented May 20, 2019

非严格模式
1、存粹的函数调用,this指向window。
2、作为对象属性调用,this指向该对象。
3、call、apply时,this指向该第一个入参,如果是null,undefined,则指向window。
4、作为构造函数,this指向返回的新对象。
严格模式
1、函数内部的this指向 undefined。
2、箭头函数的this,同上一级的函数的this

@dashengzi66
Copy link

非严格模式
(1)作为对象方法的调用,this 指向该对象
(2)call .apply 指向指定的 绑定对象
(3)函数调用this指向window
(4)call .apply 的this指向绑定对象
(5)箭头函数的this,指向当前的外层函数
严格模式
(1)this指向的是 undefined

@chongyangwang
Copy link

非严格模式下
1.全局的this指向window
2. 函数中的this指向函数调用时所处的位置
3. 箭头函数无this指向 指向声明
4. 对象中的this,指向该对象
可以通过 new操作符 call,aplay等方式改变this指向

@xyfay536475008
Copy link

xyfay536475008 commented May 20, 2019

总得来说:

this指向执行主体

分几种情况:

  1. 普通函数执行时,如果没有执行主体,在非严格模式下指向window,在严格模式下指向undefined。
  2. 箭头函数没有this,它继承上下文的this。
  3. 构造函数里的this指向生成的实例对象。
  4. 事件绑定函数中的this指向当前操作的元素。
  5. call, apply, bind把函数中的this改为指定的执行对象。

@AILINGANGEL
Copy link

AILINGANGEL commented May 20, 2019

  • (new 绑定)当函数使用new 调用的时候,指向新创建的这个对象
  • (显示绑定)当使用call, apply时候传入的第一个参数作为this的指向,但如果传入的参数是null,就应用默认绑定规则;如果使用bind, 返回的新函数的this指向传入bind的第一个参数
  • (隐式绑定)当函数作为另一个对象的方法来调用的时候,this就指向这个对象; 这种情况下要留意绑定丢失,当把一个对象的方法作为值赋值给另一个变量或者作为参数进行传递时,就会发生绑定丢失,从而应用默认绑定规则;如果通过多个object来链式调用这个函数,以最后一个obj为准,比如a.b.fn(); fn的this指向的是b
  • (默认绑定)函数单独调用,如果函数运行在严格模式下this就绑定到undefined,非严格模式下就绑定到window对象。注意这里的严格模式是指函数内部,而不是函数上层作用域
function test(){
   'use strict' //内部
   console.log(this) // undefined
}
  • 箭头函数没有自己的this, 箭头函数的this继承了上层函数的this指向

@Liruimin2
Copy link

谁调用的this,this就指向谁,
1.在函数体中,简单调用该函数时(非显示/隐士绑定下),严格模式下this绑定到undefined,否则绑定到全局对象window/global;
2.一般构造函数new调用,绑定到新创建的对象上;
3.一般call,applly,bind方法显示调用,绑定到指定参数的对象上;
4.箭头函数中,根据外层上下文绑定的this决定this指向。

@luohong123
Copy link

如何正确判断this的指向?

一、严格模式( "use strict";)

(一) 作为obj对象时

this指向obj

(二)作为function时

this指向undefined

(三)使用apply、call、bind调用

this指向绑定对象的第一个参数

(四)箭头函数

this指向上一层函数

二、非严格模式

(一) 作为obj对象时,this指向obj

image

(二)作为function时

this指向function对象

(三)使用apply、call、bind调用

this指向目标函数,null和undefined,指向全局对象(浏览器中是window),其余值指向被new Object()包装的对象

(四)使用构造函数调用

this指向new生成的新对象

参考文章

[【面试官问:JS的this指向】](https://juejin.im/post/5c0c87b35188252e8966c78a#heading-4)

青成:我的回答有不对的地方,麻烦务必指出来,我及时改正,谢谢~

@taoyaoyaoa
Copy link

对于JS代码中没有写执行主体的情况下,非严格模式默认都是window执行的,所以this指向的是window,但是在严格模式下,没有写执行主体,this指向是undefined

@freeshineit
Copy link

MDN this

@wanglinyj
Copy link

wanglinyj commented May 20, 2019

首页一条原则是JS中的this代表的是当前行为执行的主体
1.独立函数调用,严格模式下this指向undefined,非严格模式下this指向window
2.构造函数中this指向实例
3.call、apply、bind显示绑定的this指向第一个参数。非严格模式下call的第一个参数不传或者是传入null、undefined时this指向window;严格模式下不传或者传入undefined this指向undefined,传入null时this指向null
4.箭头函数没有this,它是继承外层代码块的this,箭头函数的this不是调用时决定的,是由定义时决定的,换句话说就是:外层代码块有this,外层代码的this就是箭头函数的this,外层代码没有this,箭头函数的this就是window

@taoyaoyaoa
Copy link

taoyaoyaoa commented May 20, 2019 via email

@wubetter
Copy link

单独函数

function foo(){
	//'use strict'
	this.name = 'foo';
	console.log(this);
}
//1-a:直接调用
foo()//this->window   严格模式下:this->undefined 此处报错
//1-b:构造函数调用
var f = new foo();//this ->f对象
//1-c: call,apply
var obj = {name:'obj',xxx:'this is a test!'}
var f1 = foo.call(obj);//this->obj
var f2 = foo.apply(obj);//this->obj
//1-d bind
var bindObj = {name:'bindObj'}
var f3 = foo.bind(bindObj);
f3();//this->bindObj

对象方法中的this

var person = {
	name:'zhangsan',
	sayName:function(){
		console.log(this);
		return this.name;
	}
}
//2-a:通过 对象.方法的形式调用 this 指向调用的对象
person.sayName();// this->person
//2-b:外层变量对对象下方法的引用
var s = person.sayName;
//这时候  s 就回到我们之前说的情况1:单独函数 一样了

//无论函数是以对象方法存在还是 单独函数形式存在  我们拿到该函数的指针变量s 无论在哪里单独调用s()
//此时函数内部的 this 严格模式指向 undefined   非严格模式指向 window
//比如:
//
function bar(){
	s();//s函数内部this->window(严格模式 undefined)
}



domObj.addEventListener('click',function(){
	console.log(this);
	s();//s函数内部this->window(严格模式 undefined)
});
//这里 对dom元素设置监听事件 回调函数内部的this 指向  该dom对象
//下面的例子 更直观 跟2:对象方法中的this 调用  对象.方法名 类似
domObj.onclick = function(){
	console.log(this);
}

箭头函数

箭头函数内部不存在自己的this  ,这个this 只是定义该函数是所在的对象,而不是使用时所指的对象
var arrow = ()=>{console.log(this)}
arrow();//this->window

var person = {
	name:'zhangsan',
	sayName:function(){
		console.log(this);
		arrow();//arrow函数内部this->window
		var a1 = ()=>{console.log(this)}
		a1();//a1中this 指向  person(定义时所在的对象)
		return this.name;
	}
}
person.sayName();

目前想到的就这些场景

@taoyaoyaoa
Copy link

taoyaoyaoa commented May 20, 2019 via email

@lqzo
Copy link

lqzo commented May 20, 2019

全局环境

无论是否在严格模式下,在全局执行环境中(在任何函数体外部)this 都指向全局对象。

函数(运行内)环境

  1. 简单调用(此调用未设置this)
    非严格模式下,this 的值默认指向全局对象。
    在严格模式下,this将保持他进入执行环境时的值。如果 this 没有被执行环境(execution context)定义,那它将保持为 undefined。
    当一个函数在其主体中使用 this 关键字时,可以通过使用函数继承自Function.prototype 的 call 或 apply 方法将 this 值绑定到调用中的特定对象。(如果传递给 this 的值不是一个对象,JavaScript 会尝试使用内部 ToObject 操作将其转换为对象。)
  2. bind方法
    ES5 引入了 Function.prototype.bind。调用f.bind(someObject)会创建一个与f具有相同函数体和作用域的函数,在这个新函数中,this将永久地被绑定到了bind的第一个参数,无论此函数是如何被调用的。
  3. 箭头函数
    在箭头函数中,this与封闭词法环境的this保持一致。在全局代码中,它将被设置为全局对象。
  4. 作为对象的方法
    当函数作为对象里的方法被调用时,它们的 this 是调用该函数的对象。(此概念适用于原型链中的this和getter 与 setter 中的 this)
  5. 作为构造函数
    当一个函数用作构造函数时(使用new关键字),它的this被绑定到正在构造的新对象。(手动返回其他对象时,则绑定到返回的这个对象上)
  6. 作为一个DOM事件处理函数
    当函数被用作事件处理函数时,它的this指向触发事件的元素(一些浏览器在使用非addEventListener的函数动态添加监听函数时不遵守这个约定)。
  7. 作为一个内联事件处理函数
    当代码被内联on-event 处理函数调用时,它的this指向监听器所在的DOM元素。

this - JavaScript | MDN

@darlingyz
Copy link

1、如何正确判断this的指向?(注意区分严格模式和非严格模式)
自己想到的:
非严格模式:this 没有上下文的时候 指向 window ,有上下文的时候this在上下文指向该函数;
严格模式:this 指向 undefined 有上下文根据不同的上下文来判断this的指向;
谁调用他就指向他
函数,api的指向问题
new bind apply,call 的指向
箭头函数没有this的、
搜索得到的
eg:
function foo(){
console.log(this.a)
}
var a=0;
foo();

const obj={
	a:1,
	foo:foo
}
obj.foo()
const c=new foo()

对于直接调用 foo 来说,不管 foo 函数被放在了什么地方,this 一定是 window
对于 obj.foo() 来说,我们只需要记住,谁调用了函数,谁就是 this,所以在这个场景下 foo 函数中的 this 就是 obj 对象
对于 new 的方式来说,this 被永远绑定在了 c 上面,不会被任何方式改变 this

对于箭头函数中的this
function a() {
return () => {
return () => {
console.log(this)
}
}
}
console.log(a()()())
首先箭头函数其实是没有 this 的,箭头函数中的 this 只取决包裹箭头函数的第一个普通函数的 this。在这个例子中,因为包裹箭头函数的第一个普通函数是 a,所以此时的 this 是 window。另外对箭头函数使用 bind 这类函数是无效的。

bind,不知道大家是否考虑过,如果对一个函数进行多次 bind,那么上下文会是什么呢?

let a = {}
let fn = function () { console.log(this) }
fn.bind().bind(a)() // => ?

函数转换下

// fn.bind().bind(a) 等于
let fn2 = function fn1() {
return function() {
return fn.apply()
}.apply(a)
}
fn2()

bind多次绑定 输出的值任然是 fu 中的this永远是第一次bind决定的。所以结果是 window

let datas={name:'hello'}
function foo(){
console.log(this.name)
}
foo.bind(datas)()
如果多个规则多次出现。那么该应怎么判断this ?

首先,new 的方式优先级最高,接下来是 bind 这些函数,然后是 obj.foo() 这种调用方式,最后是 foo 这种调用方式,同时,箭头函数的 this 一旦被绑定,就不会再被任何方式所改变。

-----搜索的看的某大佬的小册,
---你好,不对的还请指教

@0uzu0
Copy link

0uzu0 commented May 20, 2019

在严格模式下,this指向是undefined;在非严格模式下,一是直接调用时,thi指向的是全局对象;二是方法调用时,this指向的是该方法的对象;三是new调用时,this指向新的对象;四是箭头函数中,this指向方法调用的对象。
但使用bind时,this指向了绑定的对象。apply和call时,this指向第一个参数

@wangxinyu123
Copy link

this

**非严格模式** this指向全局对象 window this永远指向最后调用的那个对象

也可以使用apply、call、bind改变 this 的指向 可以参考 https://juejin.im/post/59bfe84351882531b730bac2#heading-4

@0xBrooke
Copy link

补充加深理解
可执行代码分为三种:全局代码,evel中的代码,函数中的代码;
跟我们关系最密切的自然是函数代码的执行,尤其是作为回调函数。This从何而来呢?我们慢慢分析。
当控制流进入到一个函数内时需要创建一个环境用以执行代码,我们称之为执行上下文(不同于上下文),执行上下文包含词法环境、变量环境和this。前两项可以看为同一个标识符环境,仅在少有的情况会产生差异。根据ES5.1 10.4.3文档中的描述在严格模式下将直接指向thisArg;非严格模式下有如下判断:

1.如果为null或undefined,this指向window;
2.否则对thisArg进行类型检测,不是object类型时this指向toObject(thisArg)的返回值;
3.否则this指向thisArg。

用伪代码来演示如下:

if(hasStrict){
this = thisArg;
}else if(thisArg === null || thisArg === undefined){
this = window;
}else if(type(thisArg) !== object){
this = toObject(thisArg);
}else{
this = thisArg;
}

(这里的thisArg是什么将在下文叙述,暂时可理解为call方法中传递的第一个参数)

一.正常的函数调用(默认绑定);
二.作为对象属性的函数调用(隐式绑定);
三.利用函数自身的方法调用,apply,call和bind(显式绑定);
四.new操作符调用

具体请看相关参考文章链接
参考: https://www.jianshu.com/p/93cbe502cd7a

@Diamondjcx
Copy link

非严格模式下:函数中this调用:this-----》window

方法中this调用:this-----》当前对象(嗲用方法的对象)

构造函数中this调用:this------》该构造函数的实例对象

借用方法调用:改变this的指向

分析this的问题,需要明白两点:

  1.分析this是属于哪个函数

  2.分析该函数是以什么模式被调用的
只看函数是怎么被调用的,不管函数是怎么来的

严格模式中,函数调用模式内部的this为:undefined

@zhmfe
Copy link

zhmfe commented May 20, 2019

1、全局作用域中的this
ES5,ES6 不管是在严格模式还是非严格模式下的this都指向 window
2、全局作用域中函数中的this
ES5:严格模式this指向undefined;非严格模式this指向window
ES6:严格模式或非严格模式,箭头函数的this都指向window
3、对象的属性(函数中)的this
ES5:严格模式或非严格模式,this的指向都是调用这个函数的对象,
ES6:严格模式或非严格模式,this的指向是执行该对象所处的执行上下文(执行环境)
4、构造函数中的this
严格模式或非严格模式,构造函数中的this指向构造函数创建的对象实例。
5、事件处理函数中的this
严格模式或非严格模式,在事件处理函数中,this指向触发事件的目标对象。

@toyxz
Copy link

toyxz commented May 20, 2019

如何判断this?(包括讨论在严格模式和非严格模式)

先不讨论严格模式和非严格模式,注意'use strict'要要么放在文件开头,要么放在函数内部,否则不起作用

JavaScript高级程序设计中有下面这一句话:

在严格模式下,未指定环境对象而调用函数,则 this 值不会转型为 window。 除非明确把函数添加到某个对象或者调用 apply()或 call(),否则 this 值将是 undefined。

this的判断我认为一般来说有几种情况:

1、普通函数调用,则this一般指向window(node下为 global )

// 严格模式
'use strict'
function outer() {
    function inner() {
        console.log(this)  // 
    }
    inner()
}
outer()
// 非严格模式 输出 global对象或者window对象(node和browser)
// 严格模式 输出 undefined

2、对象方法中调用,则this指向这个对象

var obj={
    'name':'yy',
    'age':'18',
    'val':function(){
        'use strict'   // 严格模式和非严格模式下都是指向obj这个对象
        return this;
    }
}
console.log(obj.val()) // { name: 'yy', age: '18', val: [Function: val] }

3、箭头函数调用
箭头函数没有自己的this, 它的this是继承而来; 默认指向在定义它时所处的对象(宿主对象)

var obj={
    'name':'yy',
    'age':'18',
    'val': () => {
        console.log(this)
    },
    'other': function() {
        return () => {
            console.log(this)
        }
    }
}
obj.val() // {}  返回一个空对象
obj.other()() // { name: 'yy',age: '18',val: [Function: val],other: [Function: other] }  这是由于外部函数的this指向对应的对象

4、在异步环境或者定时器环境下,this不指向原来的环境,而是函数执行时的外层函数

严格模式下:

var obj={
    'name':'yy',
    'age':'18',
    'val': () => {
        console.log(this)
    },
    'other': function() {
        'use strict'
        setTimeout( () => {
            console.log(this)
        },0)
    },
    'another': function() {
        'use strict'
        setTimeout(function(){
            console.log(this)
        },0)
    }
}
obj.other() // obj 对象
obj.another() // 在node环境下无论是不是严格模式都返回一个Timeout对象!神奇吧!在browser下是window对象

5、当函数被重新赋值时.在严格模式下,如果是箭头函数的话,this会被赋值为箭头函数外部函数对应的那个this对象,而普通函数的话,则仍然是undefined

var obj={
    'name':'yy',
    'age':'18',
    'val': () => {
        return this;
    },
    'other': function() {
        'use strict'
        return () => {      
            console.log(this)
        }
    }
    'another': function() {
        'use strict'
        return function() {       
            console.log(this)
        }
    }
}
var fn2 = obj.other() 
fn2() // 返回obj 对象,箭头函数使得this绑定到‘other'方法对应的this对象
var fn3 = obj.another()
fn3() // undefined

6、call,apply,bind(ES5新增)绑定的,this指的是 绑定的对象

var obj={
    'name':'yy',
    'age':'18',
    'val': function() {
        console.log(this)
    }
}
obj.val()    // { name: 'yy', age: '18', val: [Function: val] }
var fn = obj.val;
fn();  // 全局对象
fn.call(obj) // { name: 'yy', age: '18', val: [Function: val] }

@coloryjl
Copy link

严格模式下:
this指向undefined

非严格模式下:
1、this指向window
2、如果是对象调用,那么指向该对象,如果是函数调用,那么指向该函数(箭头函数除外,在箭头函数中,指向外层函数)
3、如果调用了apply、call、bind函数,则指向自定义指针空间

@Timoeser
Copy link

非严格模式下:this指向window,函数内的this指向函数本身(箭头函数没有this,箭头函数的this就是外层代码的this)
严格模式下:this指向undefined
this的指向可以通过apply,call,bind函数修改,也可以在外层函数中保存上层this

@binh123
Copy link

binh123 commented May 20, 2019

this的理解
1.谁作为拥有者调用它就指向谁
2.bind谁就指向谁
3.call谁就是谁,apply谁就是谁
4.没有拥有者指向window,如果是在严格模式下(use strict) 则指向undefined

"use strict";
function debugInfo(){

	console.log(this);
}

debugInfo();

@YvetteLau
Copy link
Owner Author

this的指向可以按照以下顺序判断:

1. 全局环境中的 this

浏览器环境:无论是否在严格模式下,在全局执行环境中(在任何函数体外部)this 都指向全局对象 window;

node 环境:无论是否在严格模式下,在全局执行环境中(在任何函数体外部),this 都是空对象 {}

2. 是否是 new 绑定

如果是 new 绑定,并且构造函数中没有返回 function 或者是 object,那么 this 指向这个新对象。如下:

构造函数返回值不是 function 或 object。

function Super(age) {
    this.age = age;
}

let instance = new Super('26');
console.log(instance.age); //26

构造函数返回值是 function 或 object,这种情况下 this 指向的是返回的对象。

function Super(age) {
    this.age = age;
    let obj = {a: '2'};
    return obj;
}


let instance = new Super('hello');
console.log(instance.age); //undefined

你可以想知道为什么会这样?我们来看一下 new 的实现原理:

  1. 创建一个新对象。
  2. 这个新对象会被执行[[原型]]连接。
  3. 属性和方法被加入到 this 引用的对象中。并执行了构造函数中的方法.
  4. 如果函数没有返回其他对象,那么this指向这个新对象,否则this指向构造函数中返回的对象。
function new(func) {
    let target = {};
    target.__proto__ = func.prototype;
    let res = func.call(target);
    //排除 null 的情况
    if (res && typeof(res) == "object" || typeof(res) == "function") {
    	return res;
    }
    return target;
}

3. 函数是否通过 call,apply 调用,或者使用了 bind 绑定,如果是,那么this绑定的就是指定的对象【称为硬绑定】。

这里同样需要注意一种特殊情况,如果call,apply或者bind 传入的第一个参数值是 undefined 或者 null,那么这些值在调用时会被忽略,实际应用的是默认绑定规则。

function info(){
    console.log(this.age);
}
var person = {
    age: 20,
    info
}
var age = 28;
var info = person.info;
info.call(person);   //20
info.apply(person);  //20
info.bind(person)(); //20

如果传入了 null / undefined,严格模式下 this 的值为传入的值 null /undefined,非严格模式为全局对象(node环境为global,浏览器环境为window)

function info(){
    //node环境中:非严格模式 global,严格模式为null
    //浏览器环境中:非严格模式 window,严格模式为null
    console.log(this);
    console.log(this.age);
}
var person = {
    age: 20,
    info
}
var age = 28;
var info = person.info;
//严格模式抛出错误;
//非严格模式,node下输出undefined(因为全局的age不会挂在 global 上)
//非严格模式。浏览器环境下输出 28(因为全局的age不会挂在 window 上)
info.call(null);

4. 隐式绑定,函数的调用是在某个对象上触发的,即调用位置上存在上下文对象。典型的隐式调用为: xxx.fn()

function info(){
    console.log(this.age);
}
var person = {
    age: 20,
    info
}
var age = 28;
person.info(); //20;执行的是隐式绑定

5. 默认绑定,在不能应用其它绑定规则时使用的默认规则,通常是独立函数调用。

node环境,执行全局对象 global,浏览器环境,执行全局对象 window。

function info(){
    console.log(this.age);
}
var age = 28;
//严格模式;抛错
//非严格模式,node下输出 undefined(因为全局的age不会挂在 global 上)
//非严格模式。浏览器环境下输出 28(因为全局的age不会挂在 window 上)
info(); 

6. 箭头函数

箭头函数没有自己的this,继承外层上下文绑定的this。

let obj = {
    age: 20,
    info: function() {
        return () => {
            console.log(this.age); //this继承的是外层上下文绑定的this
        }
    }
}

let person = {age: 28};
let info = obj.info();
info(); //20

let info2 = obj.info.call(person);
info2(); //28

@Nancy0611
Copy link

1、new绑定:函数是否通过new调用,是的话this绑定的是新创建的对象
(前提是构造函数没有中没有返回对象或者function)
2、显式绑定:函数是否通过call,apply,bind调用,是的话this绑定的就是指定对象
(如果传入为null或undefined则被忽略,实际应用的是默认绑定)
3、隐式绑定:函数是否为对象所调用,是的话this绑定的是该对象,如xx.fun()
4、如果不是以上情况,就属于默认绑定,分两种情况:
严格模式:绑定到undefined
非严格模式:绑定到全局对象
注:特殊情况:箭头函数,它本身是没有this的,它的this继承于外层代码块的this

@MissNanLan
Copy link

其实大家的答案都是大同小异,我也不例外。我也差不错,我是这么记的
法则一: 对象方法中的this指向对象本身,箭头函数除外
法则二: 多层嵌套的函数中this指向等同于包含改this最近一个function的this
法则三: window环境是window,node环境是{}
法则四:箭头函数的this是在定义函数时绑定的,不是在执行过程中绑定的。简单的说,函数在定义时,this就继承了定义函数的对象,这点也对应小姐姐将的本身没有this,它的this继承与外层代码块的this

@ZhangWeiC
Copy link

我想先说一下我的理解。
es5中,在没有call,apply,bind手动改变this的指向的情况下,this指向最后调用它的对象。在非严格模式下,全局对象是window;在严格模式下,全局对象为undefined。

es6中,箭头函数中的this始终指向函数定义时的this,而非执行时,箭头函数中没有this绑定,必须通过查找作用域链来确定它的值,如果箭头函数被非箭头函数包含,则this指向的是最近一层非箭头函数的this,否则指向undefined。

call,apply,bind是可以改变this的指向的。他们第一个参数都为this指向的对象,当第一个参数为null和undefined时,this指向全局对象。call后面参数是一个个要传给函数的值;apply后面是一个要传给函数的参数组成的数组;bind和前两者不同,它在改变函数this指向之后并没有调用函数,而是需要手动调用;此外apply从第二个参数开始也是一个个要传给函数的参数值。

@KRISACHAN
Copy link

KRISACHAN commented May 20, 2019

<script>
    // 浏览器正常情况下
    console.log(this === window) // true
</script>
<script>
    // 函数调用
    a = 10;
    function fn1 () {
        console.log(this.a)
    }
    fn1() // 10

    b = 2
    console.log(this.b) //2
    function fn2 () {
        this.b = 3
    }
    fn2()
    console.log(this.b) // 3
</script>
<script>
    // 方法调用
    function test () {
        console.log(this.x)
    }
    x = 2
    var o = {}
    o.x = 1
    o.m = test
    o.m() // 1
</script>
<script>
    // 构造函数调用
    x = 2
    function test () {
        this.x = 1
    }
    var o = new test()
    console.log(x) // 2
</script>
<script>
    // apply调用
    x = 0
    function test () {
        console.log(this.x)
    }
    var o = {}
    o.x = 1
    o.m = test
    o.m.apply(o) // 1
</script>
<script>
    // 函数 apply调用
    var zz = {};
    zz.a = 1000;

    a = 10;
    function fn1 () {
        console.log(this.a)
    }
    fn1.apply(zz) // 1000

    b = 2
    console.log(this.b) //2
    function fn2 () {
        this.b = 3
    }
    fn2.apply(zz)
    console.log(this.b) // 2
</script>
<script>
      // 函数 apply调用
      var qqq = {
        a: 1
      }
      var ttt = {
        a: 2
      }
      var mmm = {
        a: 3
      }
      function fq () {
        console.log(this)
      }
      fq.bind(qqq).bind(ttt).bind(mmm)() // {a: 1}
</script>
<script>
    // forEach
    var arr = [1, 2, 3, 4]
    var newarr = [5, 6, 7, 8]
    var newnewarr = [9, 10, 11, 12]

    arr.forEach(function (e, i, a) {
        console.log(this) // newarr
    }, newarr)

    arr.forEach((e, i, a) => {
        console.log(this) // window
    }, newarr)
</script>
<script>
    // 立即执行函数
    (function () {
        console.log(this) // window
    })()

    var o = {};
    o.x = 999;

    (function () {
        console.log(this) // {x:999}
    }).apply(o)

    ;(() => {
        console.log(this) // window
    })()

    ;(() => {
        console.log(this) // window
    }).apply(o)
</script>
<script>
    'use strict'
    console.log(this === window) // true
</script>
<script>
    'use strict'
    var k = {
        a: 1,
        b: 2,
        c: 3
    }
    const fn1 = function () {
        console.log(this)
    }
    fn1() // undefined
    fn1.apply(k) // {a: 1, b: 2, c: 3}
    k.m = fn1
    k.m() // {a: 1, b: 2, c: 3, m: ƒ}
</script>
<script>
    'use strict'
    const o = {
        a: 1,
        b: 2,
        c: 3
    }
    const fn2 = () => {
        console.log(this)
    }
    fn2() // window
    fn2.apply(o) // window
    o.m = fn2
    o.m() // window
</script>
<script>
    'use strict'
    const oo = {
        d: function () {
            console.log(this)
        },
        e: () => {
            console.log(this)
        }
    }
    const ooo = {
        a: 1,
        b: 2,
        c: 3
    }
    oo.d() // {d: ƒ, e: ƒ}
    oo.e() // window
    oo.d.apply(ooo) // {a: 1, b: 2, c: 3}
    oo.e.apply(ooo) // window

    var xxx = oo.d
    var yyy = oo.e

    xxx() // undefined
    yyy() // window
</script>
<script>
    'use strict'
    // forEach
    var arr = [1, 2, 3, 4]
    var newarr = [5, 6, 7, 8]
    var newnewarr = [9, 10, 11, 12]

    arr.forEach(function (e, i, a) {
        console.log(this) // newarr
    }, newarr)

    arr.forEach((e, i, a) => {
        console.log(this) // window
    }, newarr)
</script>

简单列了 window 下的几种情况,但其实在node下的情况也类似。
其实法则总结起来就是下面几点:

  1. 无论是否在严格模式下,在全局执行环境中(在任何函数体外部) this 都指向全局对象。
  2. 简单函数调用, this 在一般模式下指向全局对象;严格模式下 this 默认为 ndefined
  3. call , bind, apply在非箭头函数下修改 this 值;箭头函数下无法修改(由于 箭头函数没有自己的this指针,通过 call() 或 apply() 方法调用一个函数时,只能传递参数),不管call , bind, apply多少次,函数的 this 永远由第一次的决定。
  4. 当函数作为对象里的方法被调用时,它们的 this 是调用该函数的对象。
  5. 如果该方法存在于一个对象的原型链上,那么this指向的是调用这个方法的对象,就像该方法在对象上一样。
  6. 当一个函数用作构造函数时(使用new关键字),它的this被绑定到正在构造的新对象。

在ECMA内, this 会调用 原生方法 ResolveThisBinding()
原生方法ResolveThisBinding使用正在运行的执行上下文的LexicalEnvironment确定关键字this的绑定。 ResolveThisBinding执行以下步骤:

  1. 设置 envRecGetThisEnvironment()
  2. 返回 envRec.GetThisBinding()

原生方法GetThisEnvironment找到当前提供关键字 this 绑定的环境记录。 GetThisEnvironment 执行以下步骤:

  1. 设置 lex 为​​正在运行的执行上下文的 LexicalEnvironment
  2. 重复以下行为:
    a. 设置 envReclex 的 环境记录;
    b. 设置 existsenvRec.HasThisBinding()
    c. 如果 exists 为真,返回出 envRec
    d.设置 outerlex 的外部环境参考值。
    e. 断言: outer 不是 null
    f. 设置 lexouter

注意:步骤2中的循环必须终止,因为列表的环境总是以全局环境这个绑定。

@ParseDark
Copy link

如何正确判断this指向

首先,我们知道js运行时, 会产生维护一个调用栈。 在js引擎逐行执行代码的时候,会动态的去对这个栈进行维护(进入一个函数,产生一个调用栈, 也就是我们说的产生一个局部作用域, 函数运行完成,退出函数, 这个栈跟着也就弹出, 把控制权回到上一层)。那说this的时候,为什么要扯调用栈呢?

这是因为,this是在运行时绑定的,而不是在编写时绑定的, 它是什么只取决于调用方式, 与函数声明的位置无关.

那问题来了,什么是调用位置?

调用位置指的是代码执行到这个地方的时候, 他的调用上下文是什么?当前环境是什么?让我们来看一个示例:

a();

function a () {
    console.log('a');
    // 在window下调用
    // 当前在a的执行环境

    b();
}

function b () {
    console.log('b');
    // 在a下调用
    // 当前在b的执行环境
    c();
}

function c () {
    // 在b下调用
    // 当前在c的执行环境
    console.log('c')
}

(这是一个调用栈的关系, 脑补一下栈的结构, 先进后出。global - a -> b -> c -> c(done) -> b(done) -> a(done) -> global)

在我们理解了调用位置后,获取调用位置。在调用过程中如何绑定this对象呢?四个规则.

四个规则:

规则1: 默认绑定

该规则优先级最低.

在全局环境下, this会被默认的指向全局对象.(浏览器的话就是window)
看一个例子:

var b = 1;
function a () {
    console.log(this.b); // 此时, this 指向了window, 那么相当于直接访问全局变量b, 所以该行等价于-》 console.log(b)的效果, 然后这就变成了作用域的问题.....
}

But, 这里有个问题是, 如果你使用了严格模式,this不会默认绑定到全局对象,而是被绑定到undefind

规则2: 隐式绑定

当我的this, 存在上下文环境, 并且该环境下this有指向的时候, 那么this, 指向该上下文.看个例子:

var student = {
    name: 'Nancy',
    getName
}

function getName () {
    console.log(this.name)
}

student.getName() // Nancy

规则3: 显式绑定

call/apply/bind绑定

啊, 这里是不是要说这三个函数的不同之处?

  1. call, 绑定第一个参数为this, 后调用, 参数不以数组的形式传入
  2. apply, 绑定第一个参数为this, 后调用,参数以数组的形式传入
  3. bind, 绑定第一个参数为this, 后不调用

这里的第二步就是显式绑定, 显示绑定时最常见的情况,也是我们预料中this的合理行为。这个不多说了。

规则4: new关键字绑定

啊, 这里是不是要说new关键字了。我记得有道面试题,问new做了什么事?

  1. 创建一个object
  2. 创建object到prototype的链接
  3. 显示绑定this到object
  4. 执行函数体
  5. 返回这个object

判定顺序:

1. 你用new关键字了吗?
    ->嗯(绑定到new后返回的对象里)
    ->2
2. 你用显式绑定了吗?
    ->嗯(...)
    ->3
3. 有没有存在上下文绑定的情况?
    ->嗯(...)
    -> 4
4. 只有默认绑定给你选了(注意严格模式下)

PS: ES6要格外注意箭头函数, 箭头函数的this指向,大家可以看一下这个issues

反正我只记住了一句话: 所有的箭头函数都没有自己的this,都指向外层。然后放一个例子:

var obj = {
    fn: x => console.log(this)
}

obj.fn() // 浏览器环境下: window

《你不知道的js》 真的是一本好书
《你不知道的js》 真的是一本好书
《你不知道的js》 真的是一本好书

啊, 果然不支持md语法.但是我存档用md啊.就这样吧.

晚安.

@tpxiang
Copy link

tpxiang commented May 21, 2019

严格模式:
在严格模式下,this指向undefined
非严格模式:
1、作为函数调用,此时this指向Window。
2、作为对象调用,this指向该对象。
3、箭头函数的this继承与外层的this。
4、函数在new中调用,则this指向新对象。

@jodiezhang
Copy link

首先this是一个对象,JavaScript中万物都是对象
1.在非严格模式下,this没有和任何上下文绑定,那么它指向全局对象是window
在严格模式下,this如果没有和任何上下文绑定,this指向undefined
2.如果this与具体的上下文绑定,那个this指向上下文所属的对象
2.1 隐式绑定
person.sayHi()
在执行sayHi()函数的过程中,如果有this,则指向person对象
但是要注意区分的是sayHi()与sayHi
到底是执行函数,还是把一个函数的引用赋给了变量,后者其实与你特意绑定的上下文无关
2.2 显示绑定
是使用Function.prototype的call(),apply(),bind()方法强行绑定一个上下文对象
2.3 new绑定
在用构造函数创建一个新对象的过程中,把构造函数中的this绑定到创建的新对象上了。
2.4 箭头函数
箭头函数中的this继承自上层的this
2.5 绑定优先级
new 绑定>显示绑定>隐式绑定>默认绑定(从全局或者undefined上找)

话说看小姐姐的例子,发现闭包中的变量不会被回收,有顺带复习了一下闭包的特性,666~~~

@noggcool
Copy link

在严格模式下(严格模式是指函数单独调用时的内部,而不是函数的上层):
函数单独调用,this指向undefined。(默认绑定)

在非严格模式下:
this指向window。 (默认绑定)

使用箭头函数:this指向外层函数调用对象
new:this指向创造出的对象本身,若有返回的是其他对象,this指向其他对象。
call和apply指向第一个参数对象 (显示绑定)
在绑定事件的元素上非箭头函数,this指向该元素(隐式绑定)

@xiterjia
Copy link

如何正确判断 this 的指向?

全局环境

无论是否在严格模式下,在全局执行环境中(在任何函数体外部)this 都指向全局对象。MDN-全局环境

// 在浏览器中, window 对象同时也是全局对象:
console.log(this === window); // true

a = 37;
console.log(window.a); // 37

this.b = "MDN";
console.log(window.b); // "MDN"
console.log(b); // "MDN"

普通函数调用

在普通函数中, 非严格模式 this 都指向全局对象, 严格模式中 thisundefined

function test() {
  console.log(this); // 全局对象
}
function testStrict() {
  "use strict";
  console.log(this); // undefined
}

箭头函数

由于箭头函数中没有 this, 所以这里的 this 是指向定义时所在的对象, 而不是使用时所在的对象 箭头函数

(() => {
  console.log(this); // 严格与非严格模式都指向 全局对象
})();

箭头函数其他注意点:

  1. 函数体内的 this 对象,就是定义时所在的对象,而不是使用时所在的对象。

  2. 不可以当作构造函数,也就是说,不可以使用 new 命令,否则会抛出一个错误。

  3. 不可以使用 arguments 对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。

  4. 不可以使用 yield 命令,因此箭头函数不能用作 Generator 函数。

对象方法调用

简单来说, 谁调用了这个方法, 谁就是 this

const cat = {
  nickname: "Tom",
  meow() {
    console.log(`${this.nickname} say meow`);
  }
};
cat.meow(); // Tom say meow

var meow = cat.meow;
meow();
/* 严格模式 */
// 报错
/* 非严格模式 */
// undefined say meow

类实例化

函数中的 this 为该构造函数创建的新对象

function Person() {
  this.name = "haha";
  console.log(this); // { name: 'haha' }
  return Object.create(null);
}
var x = new Person();
console.log(x); // {}
// 注意, 这里的 x 和 构造函数中的 this 已经没有任何关系了

引用 MDN 中 new 操作符的操作 new

  1. 创建一个空的简单 JavaScript 对象(即{});
  2. 链接该对象(即设置该对象的构造函数)到另一个对象 ;
  3. 将步骤 1 新创建的对象作为 this 的上下文 ;
  4. 如果该函数没有返回对象,则返回 this。

call/apply/bind调用

这三者都会改变除箭头函数外的函数中 this 的值, 且该值都为这三个函数的第一个参数

注:
由于 bind 会返回一个被绑定了 this 的新函数, 因为返回值为函数, 还可以再次进行这三个操作. 但是! 已返回的新函数中的 this 永久地被绑定到了第一次 bind 的第一个参数, 后面再怎么 bind/call/apply 都无法修改 this

const mouse = {
  nickname: "jerry",
  squeak() {
    console.log(`${this.nickname} squeak`);
  }
};
const dog = {
  nickname: "二哈",
  woof() {
    console.log(`${this.nickname} woof`);
  }
};
const bird = { nickname: "bird" };
const fox = { nickname: "fox" };

mouse.squeak.call(dog); // 二哈 squeak
mouse.squeak.apply(dog); // 二哈 squeak
const say = mouse.squeak;
say(); // undefined squeak
say.call(dog); // 二哈 squeak
say.apply(dog); // 二哈 squeak
say.bind(dog).call(fox); // 二哈 squeak
dog.woof
  .bind(mouse)
  .bind(bird)
  .call(fox); // jerry woof

@ivan0525
Copy link

1.当函数作为一个对象的方法被调用时,this指向该对象;
2.作为普通函数调用时,就要区分严格模式和非严格模式:
(1)在非严格模式下,this指向window对象
(2)在严格模式下,this为undefined
3.构造函数调用,构造函数中的this指向创建的对象;
4.call、apply、bind动态改变函数this指向

@ccvaille
Copy link

  • 非严格模式
    • 全局调用函数:this 指向 window 对象
    • 作为对象属性调用:this 指向对象
    • call, apply 时, this 指向第一个参数
    • 作为构造函数被调用,this 指向返回的对象
    • 箭头函数的时候, this 指向最近函数的 this
  • 严格模式 - "use strict"
    • 全局调用函数:this 指向 undefined

@RanSatious
Copy link

严格模式

  • 指向undefined

非严格模式

  • 作为普通函数调用:指向顶层对象,浏览器环境为window,node环境为global
  • 作为对象属性调用:指向对象
  • 作为构造函数调用:指向返回的对象
  • 通过call apply bind绑定上下文时,指向绑定的上下文

箭头函数

  • 不能作为构造函数调用
  • 作为普通函数或对象属性调用:指向其作用域链上第一个普通函数的this对象
  • 通过call apply bind绑定上下文时,指向绑定的上下文

@dongtaotao
Copy link

如果要判断一个运行中函数的 this 绑定, 就需要找到这个函数的直接调用位置。 找到之后
就可以顺序应用下面这四条规则来判断 this 的绑定对象。

new 调用:绑定到新创建的对象,注意:显示return函数或对象,返回值不是新创建的对象,而是显式返回的函数或对象。
call 或者 apply( 或者 bind) 调用:严格模式下,绑定到指定的第一个参数。非严格模式下,null和undefined,指向全局对象(浏览器中是window),其余值指向被new Object()包装的对象。
对象上的函数调用:绑定到那个对象。
普通函数调用: 在严格模式下绑定到 undefined,否则绑定到全局对象。

ES6 中的箭头函数:不会使用上文的四条标准的绑定规则, 而是根据当前的词法作用域来决定this, 具体来说, 箭头函数会继承外层函数,调用的 this 绑定( 无论 this 绑定到什么),没有外层函数,则是绑定到全局对象(浏览器中是window)。 这其实和 ES6 之前代码中的 self = this 机制一样。

@ght5935
Copy link

ght5935 commented May 22, 2019

his的指向在函数创建的时候是决定不了的,在调用的时候才能决定,this 是在运行期进行绑定的

分几种情况:

  • 普通函数执行时,如果没有执行主体,在非严格模式下指向window,在严格模式下指向undefined。
function a(){
    var user = "若梦";
    console.log(this.user); //undefined
    console.log(this);  //Window
}
  • 作为对象属性调用:指向对象
var o = {
    user:"若梦",
    fn:function(){
        console.log(this.user);  //若梦
    }
}
o.fn()
  • 构造函数里的this指向生成的实例对象。
function Fn(){
    this.user = "若梦";
}
var a = new Fn();
console.log(a.user); //若梦

注 当this碰到return时,会有些变化

function fn()  
{  
    this.user = '若梦';  
    return {};  
}
var a = new fn;  
console.log(a.user)  //undefined
function fn()  
{  
    this.user = '若梦';  
    return function(){};
}
var a = new fn;  
console.log(a.user)  //undefined
function fn()  
{  
    this.user = '若梦';  
    return 1;
}
var a = new fn;  
console.log(a.user); //若梦
function fn()  
{  
    this.user = '若梦';  
    return undefined;
}
var a = new fn;  
console.log(a.user)  //若梦

如果返回值是一个对象,那么this指向的就是那个返回的对象,如果返回值不是一个对象那么this还是指向函数的实例。

  • 事件绑定函数中的this指向当前操作的元素。
  • call, apply, bind把函数中的this改为指定的执行对象。
  • 箭头函数没有this,它继承上下文的this。

@MissWXiang
Copy link

函数中的this指向和当前函数在哪定义和在哪执行的没有任何的关系。
1.非严格模式:
(1)自执行函数中的this永远都是window。
(2)给元素的某个行为绑定一个方法,当行为触发的时候,执行绑定的方法,此时方法中的this是当前元素,即谁调用指向谁。
(3)方法执行,看方法名前面是否有“.”,有的话,“.”前面是谁this就是谁,没有的话this就是window。
(4)在构造函数中,函数体中的this.xxx = xxx中的this是当前类的一个实例
(5)使用call/apply来改变this的指向(一旦遇到call/apply上述四条都没用了)
2.严格模式:
(1)只执行函数中的this永远是undefined
(2)方法执行,看方法名前面是否有“.”,有的话,“.”前面是谁this就是谁,没有的话this是undefined
(微信名:RUN)

@tanxchen
Copy link

谁调用了函数,this就指向谁,this即函数的调用者。

  • 全局环境
    • 无论是否在严格模式下,在全局执行环境中(在任何函数体外部)this 都指向全局对象。
  • 函数(运行内)环境
    • 在函数内部,this的值取决于函数被调用的方式。
  • 在严格模式下
    • 如果 this 没有被执行环境(execution context)定义,那它将保持为 undefined。

修改this指向的方式:

  • call:
    • fun.call(thisArg, arg1, arg2, ...)
  • apply:
    • func.apply(thisArg, [argsArray])
  • bind:
    • function.bind(thisArg[, arg1[, arg2[, ...]]])
  • 箭头函数:
    • =>

经典练习题:

function Foo () {
  getName = function () { alert(1) }
  return this
}
Foo.getName = function () { alert(2) }

Foo.prototype.getName = function () { alert(3) }

var getName = function () { alert(4) }

function getName () { alert(5) }

//输入的值
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();
答案
Foo.getName(); //2
getName(); //4
Foo().getName(); //1
getName();//1
new Foo.getName(); //2
new Foo().getName(); //3
new new Foo().getName(); //3

@qinmeishuying
Copy link

无论是否在严格模式下,在全局执行环境中(在任何函数体外部) this 都指向全局对象。
简单函数调用, this 在一般模式下指向全局对象;严格模式下 this 默认为 ndefined 。
call , bind, apply在非箭头函数下修改 this 值;箭头函数下无法修改(由于 箭头函数没有自己的this指针,通过 call() 或 apply() 方法调用一个函数时,只能传递参数),不管call , bind, apply多少次,函数的 this 永远由第一次的决定。
当函数作为对象里的方法被调用时,它们的 this 是调用该函数的对象。
如果该方法存在于一个对象的原型链上,那么this指向的是调用这个方法的对象,就像该方法在对象上一样。
当一个函数用作构造函数时(使用new关键字),它的this被绑定到正在构造的新对象。

@ITHcc
Copy link

ITHcc commented May 25, 2019

  • 非严格模式
    对象中指向指向该对象
    全局函数中指向window
    apply,call,bind指向第一个参数中
    上下文调用指向上下文
    箭头函数指向当前的外层函数
  • 严格模式
    全局函数指向undefined

@DazhiFe
Copy link

DazhiFe commented May 26, 2019

this对象是在运行时基于函数的执行环境绑定的,所以要正确判断this的指向,要看它是在什么场景下使用。

全局环境

无论是否在严格模式下,在全局执行环境中(在任何函数体外部),this都执行全局对象

// 在浏览器中, window 对象同时也是全局对象:
console.log(this === window); // true

this.a = 10;
console.log(window.a); // 10

函数(运行内)环境

在函数内部,this的指向取决于函数被调用的方式。

1.简单调用

1.1 非严格模式:

this默认指向全局对象。

function f1() {
  return this;
}

f1() === window; // true

1.2 严格模式:

this默认指向undefined

function f2() {
  "use strict";  // 这里是严格模式
  return this;
}

f2() === undefined;  // true

2.函数通过call()apply()调用

当一个函数在其主体中使用this关键字时,可以通过使用函数继承自Function.prototypecallapply方法将this的值绑定到调用中的特定对象。

// 将一个对象作为call和apply的第一个参数,this会被绑定到这个对象。

var obj = {
  a: 'Custom'
}

var a = 'Global';

function whatsThis(arg) {
    return this.a;  // this的值取决于函数的调用方式
}

whatsThis();  // 'Global'
whatsThis.call(obj);  // 'Custom'
whatsThis.apply(obj); // 'Custom'

call()apply还可以有其他用法,再看一个例子:

function add(c, d) {
    return this.a + this.b + c + d;
}

var o = {
    a: 1,
    b: 3
}

// 第一个参数是作为`this`使用的对象
// 后续参数作为参数传递给函数调用
add.call(o, 5, 7);  // 1 + 3 + 5 + 7 = 16

// 第一参数也是作为`this`使用的对象
// 第二个参数是一个数组,数组里的元素用作函数调用中的参数
add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34

使用call()apply()函数的时候要注意,如果传递给this的值不是一个对象,JavaScript会尝试使用内部ToObject操作将其转换为对象。

3.bind方法

ECMAScript5引入了Function.prototype.bind。调用f.bind(someObject)会创建一个与f具有相同函数体和作用域的函数,但是在这个新函数中,this将永久的被绑定到了bind的第一个参数,无论这个函数是如何被调用的。

function f() {
    return this.a;
}

var g = f.bind({
    name: 'Jack'
});

console.log(g()); // Jack

// bind只生效一次
var h = g.bind({
    name: 'Tom'
});

console.log(h()); // Jack

var o = {
    a: 37,
    f: f,
    g: g,
    h: h
}

console.log(o.f(), o.g(), o.h()); // 37, Jack, Jack

4.作为对象的方法

当函数作为对象里的方法被调用时,它们的this是调用该函数的对象。

下面的例子中,当o.f()被调用时,函数内的this将绑定到o对象。

var o = {
    prop: 30,
    f: function () {
        return this.prop;
    }
}

console.log(o.f()); // 30

请注意:这样的行为,根本不受函数定义方式或位置的影响。

在前面的例子中,我们在定义对象o的同时,将函数内联定义为成员f。但是,我们也可以先定义函数,然后再将其附属到o.f。这样做会导致相同的行为:

var o = {
    prop: 30
}

function independent() {
    return this.prop;
}

o.f = independent;

console.log(o.f()); // 30

这表明函数是从of成员调用的才是重点。

同样,this的绑定只受最靠近的成员的引用的影响。在下面这个例子中,我们把一个方法g当做对象o.b的函数调用。在这次执行期间,函数中的this将指向o.b。事实证明,这与他是对象o的成员没有多大关系,最靠近的引用才是最重要的。

o.b = {
    g: independent,
    prop: 42
}

console.log(o.b.g()); // 42

还有一点需要注意的,匿名函数的执行环境具有全局性,因此其this对象通常指向window。看下面的例子:

var name = "The Window";

var obj = {
    name: "My Object",
    getNameFunc: function () {
        return function () {
            return this.name;
        }
    }
}

alert(obj.getNameFunc()());  // "The Window"(在非严格模式下)

这边如果我们想让返回值为My Object,可以怎么做呢?把外部作用域的this对象保存在一个闭包能够访问的变量里,就可以让比闭包访问该对象了,看下面的例子:

var name = "The Window";

var obj = {
    name: "My Object",
    getNameFunc: function () {
        var that = this;
        return function () {
            return that.name;
        }
    }
}

alert(obj.getNameFunc()());  // "My Object"

5.作为构造函数

当一个函数用作构造函数时(使用new关键字),它的this被绑定到正在构造的新对象。

注意:虽然构造器返回的默认值是this所指的那个对象,但它仍可以手动返回其他的对象(如果返回值不是一个对象,则返回this对象)。

function C() {
    this.a = 30;
}

var o = new C();
console.log(o.a); // 30

function C2() {
    this.a = 30;
    return { a: 38 };
}

var o2 = new C2();
console.log(o2.a); // 38

6.箭头函数

引入箭头函数有两个方面的作用:更简短的函数并且不绑定this。

所以箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this

还有一句话,我觉得非常重要:就是箭头函数中的this是在定义函数的时候绑定的,而不是在执行函数的时候绑定。

我们看个例子:

var age = 40;

var o2 = {
  age: 30,
  f1: () => {
    return this.age;
  }
}

console.log(o2.f1()); // 40

如果是普通的函数,这边是输出30才对,但是由于箭头函数中的this是定义时候绑定的,就是this是继承自父执行上下文中的this,比如这里的箭头函数本书所在的对象为obj,而obj的父执行上下文是window,因此这里的this.age实际上表示的是window.age,所以输出40

箭头函数有几个使用注意点

  • 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
  • 不可以当做构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
  • 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用reset参数替代。
  • 不可以使用yield命令,因此箭头函数不能用作Generator函数。

this使用注意点

1.避免多层this

由于this的指向是不确定的,所以切勿在函数中包含多层的this

var o = {
    f1: function () {
        console.log(this);
        
        var f2 = function () {
            console.log(this);
        }
    }
}

o.f1();
// Object
// Window

上面代码包含两层this,结果运行后,第一层指向对象o,第二层指向全局对象,因为实际执行的是下面的代码:

var temp = function () {
    console.log(this);
}

var o = {
    f1: function () {
        console.log(this);
        var f2 = temp();
    }
}

一个解决办法就是在第二层改用一个指向外层this的变量。

var o = {
    f1: function () {
        console.log(this);
        var that = this;
        var f2 = function () {
            console.log(that);
        }
    }
}

o.f1();
// Object
// Object

2.避免数组处理方法中的this

数组的mapforEach方法,允许提供一个函数作为参数。这个函数内部不应该使用this

var o = {
    v: 'hello',
    p: ['a1', 'a2'],
    f: function () {
        this.p.forEach(function (item) {
            console.log(this.v + ' ' + item);
        })
    }
}

o.f();
// undefined a1
// undefined a2

上面代码中,forEach方法的回调函数中的this,其实是指向window对象,因此取不到o.v的值。原因跟上一段的多层this是一样的,就是内层的this不指向外部,而是指向顶层对象。

解决这个问题的一种方法是,跟上面一个一样,使用中间变量固定this

另一种方法是将this当作forEach方法的第二个参数,固定它的运行环境。

var o = {
  v: 'hello',
  p: [ 'a1', 'a2' ],
  f: function () {
    this.p.forEach(function (item) {
      console.log(this.v + ' ' + item);
    }, this);
  }
}

o.f();
// hello a1
// hello a2

3.避免回调函数中的this

回调函数中的this往往会改变指向,最好避免使用。

var o = new Object();
o.f = function () {
    console.log(this === o);
}

// jQuery的写法
$('#button').on('click', o.f);

上面代码中,点击按钮以后,控制台会显示false。原因是此时this不再指向o对象,而是指向按钮的DOM对象,因为f方法是在按钮对象的环境中被调用的。

参考

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/this#%E8%AF%AD%E6%B3%95

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/Arrow_functions

http://es6.ruanyifeng.com/#docs/function#%E7%AE%AD%E5%A4%B4%E5%87%BD%E6%95%B0

https://wangdoc.com/javascript/oop/this.html

@niusz
Copy link

niusz commented May 27, 2019

首先this并不会指向函数本身。
1,当函数在没有任何修饰的情况下调用,非严格模式下,this指向window,严格模式下this指向undefined。(默认绑定)
2,当函数由一个对象引导调用时,this指向该对象。(隐式绑定)
3,函数通过apply,call,bind绑定时,this指向绑定的对象。(显式绑定)
4,当函数被当做构造函数使用,又new引导调用时,this只想new创建出来的对象。(new绑定);
其优先级为:new绑定 > 显示绑定 > 隐式绑定 > 默认绑定;
箭头函数其this取决于函数定义时所在的上下文。

课代表啊!

@riluocanyang
Copy link

1、通过属性形式调用,this指向该调用的对象,比如obj.a(),this指向obj
2、通过call、apply,bind形式调用,this指向第一个参数。比如obj.a.call({x:1}),this指向{x:1}
3、通过new 形式调用,this指向new出来的对象。
4、除了箭头函数,其他情况下,this在严格模式下,为undefined,在非严格模式下,this指向window。
5、箭头函数中,this取决于函数定义时所在的上下文。

@plane-hjh
Copy link

如何正确判断this的指向?

首先我们要清楚this的指向确定是发生在调用的时候的,而不是声明this的时候。
this的指向总共可以分为五种:

  • 1、默认绑定(严格/非严格模式)
  • 2、隐式绑定
  • 3、显式绑定
  • 4、new绑定
  • 5、箭头函数绑定

一、默认绑定(严格/非严格模式)

  1. 非严格模式下,this在普通函数中被调用,this会指向全局的window。
// 非严格模式下
function func() {
    console.log(this);
}
func();  // window
  1. 严格模式下,this在普通函数中被调用,this会指向undefined。
// 严格模式下
function func() {
    "use strict";
    console.log(this);
}
func();  // undefined

二、隐式绑定

当函数引用有上下文对象时,隐式绑定规则会把函数中的this绑定到这个上下文对象。对象属性引用链中只有上一层或者说最后一层在调用中起作用,this指向了这个上下文对象。

function func() {
    console.log(this.a);
}

var a = 5;
var obj = {
    a: 10,
    func: func
}

obj.func();  // 10

但是在隐式绑定某些情况下的this会指向window或者undefined。(即类似默认绑定)

function func() {
    console.log(this.a);
}

var a = 5;
var obj = {
    a: 10,
    func: func
}

var func1 = obj.func;

obj.func();  // 10

// func1在调用的时候没有绑定到任何的上下文对象中,所以应用的是默认绑定。
func1();  // 5

三、显式绑定

通过call 或者 apply方式。第一个参数是一个对象,相当于在调用函数时将这个函数的this绑定到这个对象。因为直接指定this的绑定对象,称之为显示绑定。

function func() {
    console.log( this.a );
}

var obj = {
    a: 2
};

func.call( obj ); // 2  调用foo时强制把foo的this绑定到obj上
func.apply( obj ); // 2 同上

四、new绑定

使用new来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。

  1. 创建一个新对象。
  2. 这个新对象会被执行[[Prototype]]连接。
  3. 这个新对象会绑定到函数调用的this。
  4. 如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象。
function Func(a) {
    this.a = a;
}

var bar = new Func(2); 
console.log( bar.a ); // 2

五、箭头函数绑定

箭头函数有两个方面的作用:更简短的函数并且不绑定this。
箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this。

var a = 20;
function func() {
    var a = 10;
    return () => {
        console.log(this.a)
    }
}

var func1 = func();
func1();  // 20 箭头会从它的上一层func继承this,上一层的this是window,所以是20。如果上一层是箭头函数那会继续往上找。

@EcSunshine
Copy link

EcSunshine commented Jun 9, 2019

一般来讲,谁调用它,this就指向谁;
实际上:1. 如果是在全局作用于中,不管是严格模式还是非严格模式下this指向window, 在node环境下
this指向空对象;
2.如果是通过new绑定,并且构造函数中没有返回function或者是object,那么this就指向new实
例化出来的这个对象,反之this指向构造函数中返回的这个对象。
3.通过call,apply,bind绑定的 this指向其所绑定的对象。
4.隐式绑定 想xxx.func()这种。
5.默认绑定 就是正常函数调用 func() 非严格模式下 node环境指向global, 浏览器环境 指向
window,严格模式指向 undefind。
6.箭头函数中不会创建自己的this,它继承外层所绑定的上下文

@chenjiake
Copy link

①在浏览器下的this默认指向window对象(非严格模式),(严格模式)this为undefined(默认)
②普通函数中的this指向调用它的对象,比如func在array或object中被调用,则指向array或object(隐式)
③箭头函数始本身没有this,始终指向其定义时的this上下文(同非严格和严格模式情况)(隐式)
④构造函数中的this指向构造函数实例化得到的对象(排除构造函数自定义return对象)(new绑定)
⑤普通函数用bind、call、apply绑定this,箭头函数不行(显式)
⑥事件绑定时,哪个dom元素触发事件,this就指向哪个dom元素,需要注意的是如果在事件回调函数中有个默认window调用的普通函数的执行,该函数中的this指向window。

@Sonirjodxpro
Copy link

法则一: 对象方法中的this指向对象本身,箭头函数除外
法则二: 多层嵌套的函数中this指向等同于包含改this最近一个function的this
法则三: window环境是window,node环境是{}
法则四:箭头函数的this是在定义函数时绑定的,不是在执行过程中绑定的。简单的说,函数在定义时,this就继承了定义函数的对象,这点也对应小姐姐将的本身没有this,它的this继承与外层代码块的this

我觉得第四点 任何函数的this 在函数定义时就已经指定好了。而且我觉得this的指向问题牵扯到作用域链的问题。箭头函数为何this指向外层,因为箭头函数本身无法形成作用域。只能向上查找。

@jin2gh
Copy link

jin2gh commented Aug 17, 2019

this的指向可以按照以下顺序判断:

1. 全局环境中的 this

浏览器环境:无论是否在严格模式下,在全局执行环境中(在任何函数体外部)this 都指向全局对象 window;

node 环境:无论是否在严格模式下,在全局执行环境中(在任何函数体外部),this 都是空对象 {}

2. 是否是 new 绑定

如果是 new 绑定,并且构造函数中没有返回 function 或者是 object,那么 this 指向这个新对象。如下:

构造函数返回值不是 function 或 object。

function Super(age) {
    this.age = age;
}

let instance = new Super('26');
console.log(instance.age); //26

构造函数返回值是 function 或 object,这种情况下 this 指向的是返回的对象。

function Super(age) {
    this.age = age;
    let obj = {a: '2'};
    return obj;
}


let instance = new Super('hello');
console.log(instance.age); //undefined

你可以想知道为什么会这样?我们来看一下 new 的实现原理:

  1. 创建一个新对象。
  2. 这个新对象会被执行[[原型]]连接。
  3. 属性和方法被加入到 this 引用的对象中。并执行了构造函数中的方法.
  4. 如果函数没有返回其他对象,那么this指向这个新对象,否则this指向构造函数中返回的对象。
function new(func) {
    let target = {};
    target.__proto__ = func.prototype;
    let res = func.call(target);
    //排除 null 的情况
    if (res && typeof(res) == "object" || typeof(res) == "function") {
    	return res;
    }
    return target;
}

3. 函数是否通过 call,apply 调用,或者使用了 bind 绑定,如果是,那么this绑定的就是指定的对象【称为硬绑定】。

这里同样需要注意一种特殊情况,如果call,apply或者bind 传入的第一个参数值是 undefined 或者 null,那么这些值在调用时会被忽略,实际应用的是默认绑定规则。

function info(){
    console.log(this.age);
}
var person = {
    age: 20,
    info
}
var age = 28;
var info = person.info;
info.call(person);   //20
info.apply(person);  //20
info.bind(person)(); //20

如果传入了 null / undefined,严格模式下 this 的值为传入的值 null /undefined,非严格模式为全局对象(node环境为global,浏览器环境为window)

function info(){
    //node环境中:非严格模式 global,严格模式为null
    //浏览器环境中:非严格模式 window,严格模式为null
    console.log(this);
    console.log(this.age);
}
var person = {
    age: 20,
    info
}
var age = 28;
var info = person.info;
//严格模式抛出错误;
//非严格模式,node下输出undefined(因为全局的age不会挂在 global 上)
//非严格模式。浏览器环境下输出 28(因为全局的age不会挂在 window 上)
info.call(null);

4. 隐式绑定,函数的调用是在某个对象上触发的,即调用位置上存在上下文对象。典型的隐式调用为: xxx.fn()

function info(){
    console.log(this.age);
}
var person = {
    age: 20,
    info
}
var age = 28;
person.info(); //20;执行的是隐式绑定

5. 默认绑定,在不能应用其它绑定规则时使用的默认规则,通常是独立函数调用。

node环境,执行全局对象 global,浏览器环境,执行全局对象 window。

function info(){
    console.log(this.age);
}
var age = 28;
//严格模式;抛错
//非严格模式,node下输出 undefined(因为全局的age不会挂在 global 上)
//非严格模式。浏览器环境下输出 28(因为全局的age不会挂在 window 上)
info(); 

6. 箭头函数

箭头函数没有自己的this,继承外层上下文绑定的this。

let obj = {
    age: 20,
    info: function() {
        return () => {
            console.log(this.age); //this继承的是外层上下文绑定的this
        }
    }
}

let person = {age: 28};
let info = obj.info();
info(); //20

let info2 = obj.info.call(person);
info2(); //28

你好,我这边在全局环境下定义了一个函数,log this的指向,在浏览器环境下log的是window(没问题),但是在node环境下,log的是 Objectglobal;出现{}的时候是,node环境下,obj.fn(),fn为箭头函数;这两者的指向应该是一样的,但是log的结果却不相同,是node环境的问题吗?

@sywee
Copy link

sywee commented Oct 21, 2021

谁调用的this,this就指向谁, 1.在函数体中,简单调用该函数时(非显示/隐士绑定下),严格模式下this绑定到undefined,否则绑定到全局对象window/global; 2.一般构造函数new调用,绑定到新创建的对象上; 3.一般call,applly,bind方法显示调用,绑定到指定参数的对象上; 4.箭头函数中,根据外层上下文绑定的this决定this指向。

为什么下面代码保存在一个 js 文件里,一个 html 引入这个 js 文件,然后 chrome 最新版浏览器打开这个本地的 html 文件后, this 还是指向了 window 对象?
"use strict";
console.log(this);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests