又一难点!JavaScript中的this关键字

this的介绍

为了让对象中的函数有能力访问对象中自己的属性,this可显著的提升代码质量,减少上下文参数的传递

举个例子看看:

js 复制代码
let obj = {
    myname: "TT",
    age: 18,
    say: function () {
        console.log(this.myname);
    }
}
obj.say()//TT

在这里的this指向了obj

这个例子可能并不太能说明this的好处,于是接着看:

js 复制代码
function identify(context) {
    return context.name.toUpperCase();//将name属性转换为大写
}
function speek(context){
    var greeting = "你好我是" + identify(context);//调用identify函数转换name属性
    console.log(greeting);
}
var p = {
    name : 'nike'
}
speek(p);

会发现参数context需要到处传递,避免了context的污染,可以使用this进行取代

js 复制代码
function identify() {
    return this.name.toUpperCase();//将name属性转换为大写
}
function speek(){
    var greeting = "你好我是" + identify.call(this);
    console.log(greeting);
}
var p = {
    name : "nike"
}
speek.call(p);

将参数context完全取代了,也让代码更加容易看得懂了,但是也会产生疑问,那this到底如何指代的呢??

this的指向

this的指向通过其绑定的方式来确定,其绑定形式具有以下四种:

  1. 默认绑定:当一个函数独立调用时,不带任何修饰符的调用,该函数的this指向全局对象window
  2. 隐式绑定:当一个函数被某个对象所拥有,或者函数被某个上下文对象调用时该函数中的this指向该上下文对象
    • 隐式丢失:当一个函数被多个函数调用时,this指向最近的那个对象
  3. 显式绑定:当一个函数独立调用时,使用call、apply、bind等方法,this指向指定的对象
  4. new绑定:this指向实例对象 //当一个函数通过new关键字调用时,this指向新创建的对象

默认绑定

当一个函数独立调用时,不带任何修饰符的调用,该函数的this指向全局对象window

js 复制代码
var a = 2;
function foo(){
    var a = 1;
    function bar(){
        console.log(this.a); 
    }
    bar() // 在foo的作用域里生效 在全局这个词法的词法作用域里生效
}
foo()//独立调用

在该函数中的bar()foo()都为独立调用,不带任何修饰符的调用,因此,即使bar()foo的作用域里产生,但依然在全局这个词法的词法作用域里生效

js 复制代码
var a = 1;
function foo(){
    var a = 2;
    function bar(){
        var a = 3;
        function baz(){
            console.log(this.a); 
        }
        baz()// 在bar的作用域里生效 在全局这个词法词法作用域里生效
    }
    bar() // 在foo的作用域里生效 在全局这个词法词法作用域里生效
}
foo()

同理,无论如何嵌套关系,只要是独立调用那么this就是指向全局(即window

this指向分析

函数在哪个词法作用域下生效,函数中的this就指向哪(简述就是只要是默认绑定,this一定指向window)。

隐式绑定

当一个函数被某个对象所拥有,或者函数被某个上下文对象引用时该函数中的this指向该上下文对象

js 复制代码
var obj = {
    a: 2,
    foo: function(){
        console.log(this.a);  //this指向了obj
    }
}
obj.foo()

其中obj.foo(),函数foo被上下文对象obj所拥有,于是该函数中的this指向该上下文对象

js 复制代码
var obj = {
    a: 1,
    foo:foo //函数声明 foo()执行结果,是引用
}
function foo() {
    console.log(this.a);//1 this指向obj 隐式绑定
}
obj.foo()

在对象obj中函数foo被上下文对象obj引用因此该函数中的this指向该上下文对象

在这里需要注意一点:在JavaScript中,函数的调用和引用是两个不同的概念:

  1. 函数调用

    • 函数调用是指执行一个函数,这通常通过函数名后跟一对圆括号()实现。
    • 调用函数时,会创建一个新的执行上下文,并将其推入执行栈。
    • 函数调用可以传递参数给函数,函数内部可以使用这些参数执行特定的操作。
    • 函数调用的结果(如果有返回值的话)可以被赋值给变量或用于其他操作。
  2. 函数引用

    • 函数引用是指将函数赋值给一个变量或将函数作为参数传递给另一个函数。
    • 引用函数时,并没有执行该函数,只是将函数的引用(内存地址)存储在变量中。
    • 引用的函数可以在任何时候被调用,只要它在作用域内。
    • 函数引用允许函数作为一等公民(first-class citizen)在JavaScript中使用,即函数可以像任何其他值一样被存储、传递和操作。
js 复制代码
var obj = {
    a: 1,
    foo:foo()//函数调用 foo()执行结果,不是引用
}
function foo() {
    console.log(this.a);//undefined this指向window
}
obj.foo//这一行压根没用 

而这一段代码为函数的调用,和引用不同它相当于:

js 复制代码
var obj = {
    a: 1,
    foo:console.log(this.a);
}

因此这里的this不属于隐式绑定,this指向了全局

隐式丢失

当一个函数被多个函数调用时,this指向最近的那个对象

即拥有就近原则

js 复制代码
var obj = {
    a: 1,
    foo:foo
}
var obj2 = {
    a: 2,
    obj:obj
}
obj2.obj.foo()

在这里this指向obj,函数被多个对象嵌套,存在隐式绑定,而这时候this拥有就近原则,指向最近的引用对象

显式绑定

当一个函数独立调用时,使用call、apply、bind等方法,this指向指定的对象

js 复制代码
var obj = {
    a: 1,
}
function foo(x,y){
    console.log(this.a,x + y);
}
foo.call(obj,1,2)//call会把foo中的this掰弯到obj中
foo.apply(obj,[1,2])//apply会把foo中的this掰弯到obj中
const bar = foo.bind(obj,1,2)
bar()

const bar = foo.bind(obj)
bar(1,2)//也有就近原则,bind优先

call会把foo中的this掰弯到obj

applycall差不多,但是区别为,apply传递其他的参数给foo时是通过数组[]的形式进行传递的。

bind返回一个函数 ,定义一个bar接收该函数,然后进行调用,这时候就会有多个调用方法

一、(通过bind传参)

js 复制代码
var bar = foo.bind(obj, 2, 3);
bar();

二、(通过bar传参,函数bar中也可以设置参数)

js 复制代码
var bar = foo.bind(obj);
bar(2, 3);

三、(bind和bar结合传参,双方都含有参数,会全部选取)

js 复制代码
var bar = foo.bind(obj, 2);
bar(3);

四、(bind和bar结合传参,但存在多出的参数,会根据就近原则,优先选取bind)

js 复制代码
var bar = foo.bind(obj, 2, 3);
bar(4);

new绑定

this指向实例对象,即当一个函数通过new关键字调用时,this指向新创建的对象

js 复制代码
function Person(){
    //new 的五个步骤
    //let obj = {
    //    name:'Tom'
    //}
    //Person.call(obj)
    //obj___proto__ = Person.prototype
    //return obj
    this.name = 'Tom';
}
let p = new Person() //{name:'Tom'}

Person构造函数内部,this指的是新创建的p对象,this.name = 'Tom';这行代码将pname属性设置为'Tom'

箭头函数

箭头函数中没有this这个机制,写在箭头函数中的this,那也是外层非箭头函数的

那什么是箭头函数呢??

js 复制代码
function foo(){}
var bar = function(){}
const baz = () => {}

前一、二种我们都已经熟悉,第三种便是箭头函数,它和前两种的作用一样都是创建了个函数

接下来看看关于箭头函数的this指向

js 复制代码
var obj ={
    a:1,
    baz:baz
}
var baz = () => {
    console.log(this.a);
}
obj.baz();

很明显这是个隐式绑定,没有问题,指向的是obj中的a,得到1

接下来改动一下

js 复制代码
var obj = {
    a:1,
    foo: function(){
        const fn = () => {
            console.log(this.a);
        }
        fn()//独立调用指向全局
    }
}
obj.foo();//1

对象obj中有一个函数foo,函数foo中又创建了个函数fn,而在函数foo中独立调用了函数fn,那么可以判断fn中的this属于默认绑定指向全局理应输出undefined,但箭头函数中没有this的概念,fn不承认这个this于是这个this向外跑找到了最近的函数foo,便属于foothis

于是乎相当于foo中创建了this,而又存在隐式绑定,因此this指向的是foo的上下文对象obj,输出为1

总结

this的指向通过其绑定的方式来确定

  1. 默认绑定:当一个函数独立调用时,不带任何修饰符的调用,该函数的this指向全局对象window
  2. 隐式绑定:当一个函数被某个对象所拥有,或者函数被某个上下文对象调用时该函数中的this指向该上下文对象
    • 隐式丢失:当一个函数被多个函数调用时,this指向最近的那个对象
  3. 显式绑定:当一个函数独立调用时,使用call、apply、bind等方法,this指向指定的对象
  4. new绑定:this指向实例对象 //当一个函数通过new关键字调用时,this指向新创建的对象

还存在箭头函数没有this机制,在遇见时要注意它的存在,将它的this跳出一直找到第一个承认this的,再判断this的指向

相关推荐
莹雨潇潇4 分钟前
Docker 快速入门(Ubuntu版)
java·前端·docker·容器
Jiaberrr12 分钟前
Element UI教程:如何将Radio单选框的圆框改为方框
前端·javascript·vue.js·ui·elementui
Tiffany_Ho1 小时前
【TypeScript】知识点梳理(三)
前端·typescript
安冬的码畜日常2 小时前
【D3.js in Action 3 精译_029】3.5 给 D3 条形图加注图表标签(上)
开发语言·前端·javascript·信息可视化·数据可视化·d3.js
太阳花ˉ2 小时前
html+css+js实现step进度条效果
javascript·css·html
小白学习日记3 小时前
【复习】HTML常用标签<table>
前端·html
john_hjy3 小时前
11. 异步编程
运维·服务器·javascript
风清扬_jd3 小时前
Chromium 中JavaScript Fetch API接口c++代码实现(二)
javascript·c++·chrome
丁总学Java3 小时前
微信小程序-npm支持-如何使用npm包
前端·微信小程序·npm·node.js