【ES6】了解作用域,this的指向是什么(总结记忆)

6 篇文章 0 订阅

文章仅作为总结记忆,如需理解具体产生原因,请点击相关文章阅读模块查看其中文章。

如果你试图理解this是什么?

需要了解的知识点有:
1.作用域
2.静态作用域
3.动态作用域

this的作用域就是动态作用域,动态作用域在执行时确认,所以只能在执行阶段才能决定this变量的作用域。

它是非常特殊的关键词标识符,在每个函数的作用域中被自动创建。

this的默认指向是全局对象

为了方便区分记忆,这里认为this的默认指向是非严格模式下的默认指向,严格模式下的指向作为非默认情况在下文提及。

以下列举了常见的环境下为全局对象的那些值,this在非严格模式下的默认指向是他们

浏览器环境:

windowselfframes(他们的值都是全局对象,一般我们认为的this默认指向是window

Node环境:

global

worker环境:

self

通用:

globalThis(IE不兼容)

this的非默认指向

1. 使用严格模式的情况

'use strict'
var name2 = 'name2'
console.log(this.name2)
// undefined

⭐ 严格模式下,全局环境下的this指向默认为undefined

2. 使用了let、const声明顶层对象,class构造顶层函数的情况

顶层对象的属性与全局变量挂钩,被认为是JavaScript语言最大的设计败笔之一。😅
ES6为了改变这一点,一方面规定,为了保持兼容性,var命令和function命令声明的全局变量,依旧是顶层对象的属性;另一方面规定,let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性
也就是说,从ES6开始,全局变量将逐步与顶层对象的属性脱钩。

使用letconst在全局声明变量,使用class在全局构造函数时,生成的顶层对象不再绑定到全局对象上,此时用this获取这些变量得到undefined

// 非严格模式
let name2 = 'window2';
const name3 = 'window3';
let doSth2 = function(){
    console.log(this === window);
    console.log(this.name2);
    console.log(this.name3);
}
doSth2() // true, undefined, undefined

以上例子中的name2name3doSth2不属于window,所以this.name2undefined

function hh () {}
console.log('hh:', this.hh)
class hhh {}
console.log('hhh:', this.hhh)
//  hh: ƒ hh () {}
//  hhh: undefined

以上例子中hhhclass构造,不属于window,所以this.hhhundefined

3. 对象的函数写法Object.keyName()的this指向

这种调用函数的写法!写法!写法!导致了this指向的变换。
箭头函数除外,因为箭头函数没有自己的this

let doSth = function(){
    console.log(this.name);
}
var student = {
    name: '若川',
    doSth: doSth,
    other: {
        name: 'other',
        doSth: doSth,
    }
}
student.doSth(); // '若川'
student.other.doSth(); // 'other'

使用student.doSth()的方式调用doSth函数,this指向student

var studentDoSth = student.doSth;

studentDoSth(); // 'window'

使用studentDoSth()的方式调用doSth函数,this指向studentDoSth所处的环境window

⭐ 根据调用函数时使用的对象中函数Object.keyName()的这种写法,将keyName函数中的this指向了Object对象

⭐ 或者通俗的说,让keyName函数的this成为Object,变成了console.log(Object.name)

4. 使用了call,apply、bind修改函数中的this指向

// 'use strict';
function hh () {}
function h () {
    console.log('hh:', this.hh)
}
h.call(undefined);
// hh: ƒ hh () {}

⭐ 非严格模式下,call()apply()bind()()方法第一个参数是undefined或者nullthis是指向window

'use strict';
function hh () {}
function h () {
    console.log('hh:', this.hh)
}
h.call(undefined);
// Uncaught TypeError: Cannot read properties of undefined (reading 'hh')

⭐ 严格模式下,call()apply()bind()()方法第一个参数是undefined或者nullthis一定指向第一个参数,为undefined

var doSth = function(name){
    console.log(this);
    console.log(name);
}
doSth.call(2, '若川'); // Number{2}, '若川'
var doSth2 = function(name){
    'use strict';
    console.log(this);
    console.log(name);
}
doSth2.call(2, '若川'); // 2, '若川'

⭐ 非严格模式下,call()apply()bind()()方法第一个参数是原始值(数字,字符串,布尔值)this会指向该原始值的自动包装对象。

⭐ 严格模式下,call()apply()bind()()方法第一个参数是原始值(数字,字符串,布尔值)this会指向该原始值。

4. 构造函数中的this

function Student(name){
    this.name = name;
    console.log(this); // {name: 'nname'}
    // 相当于返回了
    // return this;
}
var result = new Student('nname');

使用new操作符调用函数,会自动执行以下步骤。

  • 创建了一个全新的对象。
  • 这个对象会被执行[[Prototype]](也就是__proto__)链接。
  • 生成的新对象会绑定到函数调用的this
  • 通过new创建的每个对象将最终被[[Prototype]]链接到这个函数的prototype对象上。
  • 如果函数没有返回对象类型Object(包含Functoin, Array, Date, RegExg, Error),那么new表达式中的函数调用会自动返回这个新的对象。

利用上文的new Student() 翻译一下:

  1. 创建一个新{}

  2. {}.__proto__ = Student.prototype或者{}.[[Prototype]] = Student.prototype

  3. function Student(){ this === {__proto__: Student.prototype}}

  4. function Student(){ return this}(*没有返回其他自定义对象类型Object的情况下)

  5. s1 = this

    看文章面试官问:JS的this指向看到一个例子:

    function Student(name){
        this.name = name; // 第一个this
    }
    var s1 = new Student('若川');
    Student.prototype.doSth = function(){
        console.log(this.name); // 第二个this
    }
    s1.doSth(); // '若川'
    

    文中第一个this第二个this的产生方式不同,第一个this由构建函数这种构建方法产生,第二个thiss1.doSth()的对象中函数这种写法产生,可以理解为将s1this给了doSth

    function Student(name){
    	console.log(this) // 第一个this
        this.name = name;
    }
    var s1 = new Student('若川');
    Student.prototype.doSth = function(){
        console.log(this) // 第二个this
    }
    const sdosth = s1.doSth
    sdosth()
    
    // Student {}
    // Window {window: Window, self: Window, document: document, name: '', location: Location, …}
    

    换一种调用方法,第二个this就改变了

5. 箭头函数中的this

1、没有自己的this、super、argumentsnew.target绑定。

2、不能使用new来调用。
3、没有原型对象。
4、不可以改变this的绑定。
5、形参名称不能重复。

⭐ 如果箭头函数被非箭头函数包含,则this绑定的是最近一层非箭头函数this,否则this的值则被设置为全局对象

function foo() {
  setTimeout(() => {
    console.log('id:', this.id);
  }, 100);
}

var id = 21;

foo.call({ id: 42 });
// id: 42

⭐ 箭头函数导致函数内部的this指向是固定的总是指向函数定义生效时所在的对象,相比之下,普通函数的this指向是可变的。

var value = 1;
var result = (() => this.value).bind({value: 2})();
console.log(result); // 1

⭐ 试图使用call,apply、bind修改箭头函数中的this是无效的

6.DOM事件处理函数中的this

DOM事件处理函数中的this,默认指向绑定此事件的DOM

例如:addEventerListenerattachEventonclick事件

一些浏览器,比如IE6~IE8下使用attachEventthis指向是window

attachEvent——兼容:IE7、IE8;不兼容firefox、chrome、IE9、IE10、IE11、safari、opera
addEventListener——兼容:firefox、chrome、IE、safari、opera;不兼容IE7、IE8

<button class="button">onclick</button>
<ul class="list">
    <li>1</li>
    <li>2</li>
    <li>3</li>
</ul>
<script>
    var button = document.querySelector('button');
    button.onclick = function(ev){
        console.log(this);
        console.log(this === ev.currentTarget); // true
    }
    var list = document.querySelector('.list');
    list.addEventListener('click', function(ev){
        console.log(this === list); // true
        console.log(this === ev.currentTarget); // true
        console.log(this);
        console.log(ev.target);
    }, false);
</script>

需要注意下内联事件处理函数中的this

<button class="btn1" onclick="console.log(this === document.querySelector('.btn1'))">点我呀</button>
<button onclick="console.log((function(){return this})());">再点我呀</button>

第一个是button本身,所以是true,第二个是window
这里跟严格模式没有关系。 当然我们现在不会这样用了,但有时不小心写成了这样,也需要了解。

7.优先级

以上非默认情况同时出现时,对this指向的影响是:

new 调用 > call、apply、bind 调用 > 对象上的函数调用 > 普通函数调用 。

总结记忆

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

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

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

DOM事件函数:一般指向绑定事件的DOM元素,但有些情况绑定到全局对象(比如IE6~IE8attachEvent)。

一定要注意,有些调用可能在无意中使用普通函数绑定规则。 如果想“ 更安全” 地忽略 this 绑 定, 你可以使用一个对象, 比如 ø = Object.create(null), 以保护全局对象。

相关文章阅读:

ES6(一)—— 作用域

面试官问:JS的this指向

ES6 let和const命令

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值