this有且仅有的五种指法

码林薄雾
• 阅读 2077

重点写在前面:

我们并不知道我们写下的函数和方法是否被框架赋值过或显示绑定过而改变了this指向。以至this指向更加扑朔迷离。

this 到底指向哪里

以下如果没提及,则为严格模式。

js中作用域有两种:

  1. 词法作用域
  2. 动态作用域

词法作用域

词法作用域指在书写代码时就被确定的作用域。
看如下代码

    var value = 1;

    function foo() {
        console.log(value);
    }

    function bar() {
        var value = 2;
        foo();
    }

    bar();// 结果是1

动态作用域

动态作用域指在代码运行时才被确定的作用域。
js中只有this的作用域是动态作用域

this的五种绑定

初学js时,会想当然认为this遵循某一条规律,就像物理学那样,然而并不是。
this的绑定分为五种情况,这五种情况之间毫无规律可言。不过好在都很简单。

一. 默认绑定

当以如下形式执行一个函数时,this为默认绑定;

    func()
  • 严格模式下,this为undefined
  • 非严格模式下,this是全局对象。

与函数调用嵌套多少层如何嵌套无关

/* 全是undefined */
function printThis(){
    return this
}
var obj = {
    say(){
        console.log('obj.say',printThis())
    }
}
function funcB(){
    console.log('funcB',printThis());
    obj.say();
}
console.log('funcA',printThis())
obj.say()
funcB()

二. 隐式绑定

当以如下行驶执行一个函数时,this为隐式绑定;

a.b.func()

此时this指向前面一个对象

赋值会改变隐式绑定this的指向

  • 方法赋值给变量
class T {
    dotInvoke() {
        console.log('dotInvoke', this.sayThis())
    }
    sayThis() {
        return this
    }
    assignInvoke() {
        var sayThis = this.sayThis;
        console.log('assignInvoke', sayThis())
    }
}
var tt = new T();
tt.dotInvoke()// 指向T
tt.assignInvoke()// undefined
  • 函数被赋值成方法
function printThis(){
    return this
}
var obj = {};
obj.say = printThis;
obj.say()/* 指向obj */
  • 赋值给参数

极为常见的是回调函数的this是undefined,因为回调函数被复制给参数,参数再调用时变成了默认绑定

function asyncFun(cb){
    cb()
}
var obj = {
    callback(){
        console.log(this)
    }
}
obj.callback()/*隐式绑定 obj */
asyncFun(obj.callback);/*默认绑定 undefined */

三. 箭头函数

箭头函数会让this指向最近的函数或全局作用域

  • 与最近的函数的this指向相同
    function foo() {
        // 返回一个箭头函数
        return (a)=>{
            //this 继承自 foo()
            return this.a
        }
        ;
    }
    var obj1 = {
        a: 'obj1'
    };
    var obj2 = {
        a: 'obj2'
    }
    var arrow1 = foo.call(obj1);
    var arrow2 = foo.call(obj2);
    var arrow3 = foo();
    console.log('arrow1',arrow1())/* obj1 */
    console.log('arrow2',arrow2())/* obj2 */
    console.log('arrow3',arrow3())/* undefined,严格模式下报错 */
  • 指向全局
var printThis = ()=>this;
console.log('printThis',printThis());/* global */
  • 指向实例
class Test {
    printThis = ()=>{
        return this
    }
}
//会被babel翻译成
var test = function test() {
  var _this = this;

  this.printThis = function () {
    return _this;
  };
};

四. 显示绑定

call, apply, bind指定this指向

五. new绑定

构造函数,ES6中的class
new构造函数,new class时,this指向实例

总结

  1. 五种绑定,后面两种情况单一,前面两种会因为方法,函数被赋值而互相转化。
  2. 因为this处于动态作用域,而目前开发时又大量使用框架。我们写下的代码,并不总是由我们自己调用,而是被打包工具打包后,由框架调用。导致我们并不知道我们写下的函数和方法是否被框架赋值过或显示绑定过而改变了this指向。以至this指向更加扑朔迷离。
  3. 写完本文顿时觉得,python里指向明确的self完爆js的this。
点赞
收藏
评论区
推荐文章
巴拉米 巴拉米
4年前
bind、call、apply 区别?如何实现一个bind?
一、作用call、apply、bind作用是改变函数执行时的上下文,简而言之就是改变函数运行时的this指向那么什么情况下需要改变this的指向呢?下面举个例子var name"lucy";const obj{    name:"martin",    say:function (){        co
待兔 待兔
11个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Wesley13 Wesley13
3年前
java中多态的实现机制
多态的概念:  简单来说就是事物在运行过程中存在的不同状态,即父类或接口定义的引用变量指向子类或具体实现类的实例对象。程序调用方法在运行期才进行动态绑定,而不是引用变量的类型中定义的方法。多态存在的前提:1、存在继承关系,子类继承父类;2、子类重写父类的方法;3、父类引用指向子类对象。具体实例:1、定义一个父类:Animal
Stella981 Stella981
3年前
React组件中的函数绑定this的几种方式
在React中使用class定义组件时如果不注意this的指向问题,会带来一些麻烦。绑定this主要有下面两种方法:1\.bind()在class中定义函数,然后在构造方法中使用bind()绑定当前的组件对象。classMyComponentextendsReact.Component{constr
Wesley13 Wesley13
3年前
C++基类的析构函数定义为虚函数的原因
1:每个析构函数只会清理自己的成员(成员函数前没有virtual)。2:可能是基类的指针指向派生类的对象,当析构一个指向派生类的成员的基类指针,这时程序不知道这么办,可能会造成内存的泄露,因此此时基类的析构函数要定义为虚函数;基类指针可以指向派生类的对象(多态),如果删除该指针delete\\p,就会调用该指针指向的派生类的析构函数,而派生类
Wesley13 Wesley13
3年前
ES6新增内容(部分)
ES6新增内容(部分)一、两个声明变量的方法let、constlet:不能重复声明、有暂时性死区,不能提前访问、{}块级作用域。const:声明常量、声明之后不能被修改。二、箭头函数语法:(参数){表达式}箭头函数中this没有固定指向,一般指向宿主对象。
Wesley13 Wesley13
3年前
Java 多态
类型的检测——向上转型向下转型向上转型:父类对象的引用指向子类对象,向下转型:向上转型的基础上再次指向子类的对象1.向上转型!(https://oscimg.oschina.net/oscnet/dd0d05d39a724e781b799ff5e35b921775d.jpg)!(https://oscimg.oschina.net/o
Stella981 Stella981
3年前
JS 中的this指向问题和call、apply、bind的区别
this的指向问题一般情况下this对象指向调用函数的对象,全局环境中执行函数this对象指向window。functiona(){console.log(this);//输出函数a中的this对象}functionb(){};varc{name:"call"}
Wesley13 Wesley13
3年前
ES6 箭头函数
一、在es6中函数的定义和es5之间有明显区别。不需要关键字function来进行定义,使用来指向函数。不可以new也就是做构造函数以及没有arguments参数。箭头函数的this是在定义的时候确定指向这和es5不一样,es5是谁调用他,他就指向谁。1document.addEventListene
Wesley13 Wesley13
3年前
PHP算法之判断是否是质数
<h3质数的定义</h3<blockquote质数又称素数。一个大于1的自然数,除了1和它自身外,不能整除其他自然数的数叫做质数;否则称为合数。</blockquote<h3实现思路</h3<p循环所有可能的备选数字,然后和中间数以下且大于等于2的整数进行整除比较,如果能够被整数,则肯定不是质数,相反,就是质数。</p<h3第一种算
Wesley13 Wesley13
3年前
Java设计模式之原型模式
原型模式简介原型模式实际上不算一种设计模式,应该说是一种技巧吧。当我们需要创建与已有对象一样的对象时,我们通常可以有两种容易想到的方法,一种是将已有对象指向另外一个重新创建的对象,如//将old赋给newObjectnewObjectoldObject;这种做法是相当于newObject还是指向