当前位置: 首页 > news >正文

简单易懂的JavaScript中的this指针

文章目录

  • 默认绑定 / 隐式绑定
  • 如何调整this
    • 1.用变量固定this
    • 2.箭头函数
    • 2.bind
    • 3.call/apply(一次性)

默认绑定 / 隐式绑定

要找this指针指向谁,我们首先要做的是:找到一个明确的对象,这个对象调用了含有this指针的函数,则:
谁调用函数,this指向谁。

而如果找不到,this 默认指向全局对象(浏览器中是 windowNode.js 中是 global)。
以下面几个例子说明一下:

const obj = {name: 'Alice',Hello: function() {console.log(this.name);}
};obj.Hello(); // 输出 "Alice" - this 指向 obj

这个例子中的this指针所属函数为Hello,谁调用了Hello()?是obj,所以this.name就是要在obj中找到name


const obj = {name: 'Alice',Hello: function() {console.log(name);}
};const obj1 = {};
obj1.name = 'Joan';
obj1.Hello = obj.Hello; 
obj1.Hello();// 输出 "Joan" - this 指向 obj1

这里调用Hello的对象成了obj1,自然地,name就需要找obj1中的了。


function test() {console.log(this.x);
}var obj = {};
obj.x = 1;
obj.m = test;obj.m(); // 1

在这个例子中,this指针所属函数为test, test赋值m,此时mtest指向同一个函数,那么obj调用m()就等同于调用test()this.x就是要在obj中找到那个x。其实和上一个例子本质上大差不差。

要是我们换一种方法呢?


function test() {console.log(this.x);
}
test();// 非严格模式:可能输出 undefined// 严格模式:报错(Cannot read property 'x' of undefined)

这个时候我们会发现找不到谁调用了test,这就遵循默认绑定规则,this 会默认绑定到全局对象(非严格模式)或 undefined(严格模式)。
当然,这是因为我们把test绑定到了全局,并且非要在全局没有x的情况下找出一个x。
那么如果我们不找那个x呢?


function test() {console.log(this);
}
test(); // 输出 window 对象

此时,在非严格模式下会输出window,在严格模式下,this 还是undefined
这是删掉.x的情况,但这样改显然不符合这个函数原先的想法。那么如果我们依然把this指向全局,并且在全局中给他一个x呢?


let x = 1;
function test() {console.log(this.x);
}
test(); // 1

这时候就会输出全局中的x。


let x = 1;
function test() {return this.x;
}
console.log(test());  //1

当然换一种方式也是一样的。


function test() {return this;
}
function test1(func) {func();
}test1(test())  //window

在这个例子中,test函数以参数的形式被调用,有没有那么一个明确的对象调用了test呢?我们找不到这个调用者,所以this指针指向window


其他情况:

除了上面的情况,还有一些情况可以拎出来单独说一下,我们回到第一个例子,给它额外加个定时器:

const obj = {name: 'Alice',Hello: function() {setTimeout(function(){    console.log(this.name);})}
};obj.Hello(); // undefined(非严格模式)或 报错(严格模式)

现在我们的this所属函数是setTimeout,但是是谁调用了它呢?我们找不到了。也就是说这种情况下,this 会丢失原来的绑定,默认指向全局对象。而全局中找不到name

定时器中的回调函数的this指针总是指向window对象


如何调整this

出现上面的this 丢失原来的绑定的情况不是我们希望看到的,那么有没有什么办法能按照原本的想法输出呢?

1.用变量固定this

const obj = {name: 'Alice',Hello: function() {let that = this;setTimeout(function(){    console.log(that.name);})}
};obj.Hello(); // "Alice"

我们用一个额外的变量先把this固定下来,下面再利用这个变量进行操作就可以了。

2.箭头函数

箭头函数没有自己的 this,它会捕获所在上下文的 this 值。所以我们可以这么写:

const obj = {name: 'Alice',Hello: function() {setTimeout(() => {    console.log(this.name); // this 继承 Hello 的 this(即 obj)}, 0);}
};
obj.Hello(); // 输出 "Alice"

可以想象箭头函数就是把它所在的那个函数趟平了setTimeout现在不是一个拦路石了,它变成了单纯的“一条路”,那我们直接往上走就好,现在this通往的函数就是Hello

2.bind

如果不方便用变量固定this,箭头函数也不太合适呢?
比如这种情况:

class Person {constructor(){this.name = "Alice";}getName(){return this.name;}
}let change = {name: "Joan"
}let a = new Person()
change.getName = a.getName
console.log(change.getName())  //"Joan"

这种情况下,如果我们想要this.name始终为"Alice",就可以使用bind了,bind的意义指定一个this的指定对象,可以在于返回一个始终指向该对象的值。

class Person {constructor(){this.name = "Alice";this.getName = this.getName.bind(this);}getName(){return this.name;}
}let change = {name: "Joan"
}let a = new Person()
change.getName = a.getName
console.log(change.getName()) //"Alice"

我们添加了一句代码:

this.getName = this.getName.bind(this);

为了便于理解,我们可以把它看成

function1 = function.bind(this)

意思就是,我给你一个function1 ,这个方法是我把function和一个this指向绑定到了一起,从今往后你在任何地方调用function1 ,它的指向永远固定,而回到情境中,function1是this.getName,function也是this.getName,也就是说,我们给this.getName重新赋值了一下。现在它永远指向Alice了。

为了加深理解,我们可以多看一个例子:

function Hello() {console.log(this.name);
}const person = { name: 'Bob' };const BobHello = Hello.bind(person);
BobHello();         //  输出"Bob"

这个会容易理解一点是不是?

3.call/apply(一次性)

既然提到了bind,就不得不提一下call/apply了。他们的作用相似,但有一些微妙的差别,比方说bind并不会立即执行,但这两个会,并且他们是一次性的,不能接收,如下:

function Hello() {console.log(this.name);
}const person = { name: 'Bob' };Hello.call(preson);  //  输出"Bob"
Hello.apply(preson); //  输出"Bob"
const BobHello = greet.bind(person);
BobHello();         //  输出"Bob"

而call和apply的区别如下:

  1. apply接受两个参数,第一个参数是this的指向,第二个参数是函数接受的参数,以数组的形式传入
    当参数是动态数组时,apply 比 call 更方便。
    改变this指向后原函数会立即执行,且此方法只是临时改变this指向一次
function fn(...args){console.log(this,args);
}
let obj = {myname:"张三"
}fn.apply(obj,[1,2]); // this会变成传入的obj,传入的参数必须是一个数组;
fn(1,2) // this指向window

当第一个参数为null、undefined的时候,默认指向window(在浏览器中)

fn.apply(null,[1,2]); // this指向window
fn.apply(undefined,[1,2]); // this指向window
  1. call方法的第一个参数也是this的指向,后面传入的是一个参数列表(逐个传入)
    call 通常更快,因为 apply 需要处理数组。
    跟apply一样,改变this指向后原函数会立即执行,且此方法只是临时改变this指向一次
function fn(...args){console.log(this,args);
}
let obj = {myname:"张三"
}fn.call(obj,1,2); // this会变成传入的obj,传入的参数必须是一个数组;
fn(1,2) // this指向window

同样的,当第一个参数为null、undefined的时候,默认指向window(在浏览器中)

fn.call(null,[1,2]); // this指向window
fn.call(undefined,[1,2]); // this指向window

相关文章:

  • K8S Ingress 实现AB测试、蓝绿发布、金丝雀(灰度)发布
  • 【Linux网络】认识网络
  • Vue:显示数据
  • MySQL基础之开窗函数
  • 二叉树前序与后序遍历迭代法详解:栈操作与顺序反转的巧妙结合
  • 执行apt update后报错 Unable to lock directory /var/lib/apt/lists/ 问题解决
  • Myshell与清华联合开源TTS模型OpenVoiceV2,多语言支持,风格控制进一步增强~
  • leetcode2934. 最大化数组末位元素的最少操作次数-medium
  • 【工具类】ssh,remote-ssh插件和sftp插件
  • 基于EFISH-SCB-RK3576/SAIL-RK3576的智能安检机技术方案‌
  • electron结合vue,直接访问静态文件如何跳转访问路径
  • Apache Pulsar 消息、流、存储的融合
  • Spyglass:跨时钟域同步(同步单元)
  • 编程日志5.6
  • 一文掌握六个空转数据库
  • MySQL——九、锁
  • 十个免费试用的云数据库
  • 在 Windows 中配置使用 WSL 来运行 Linux 环境,主要有以下步骤:
  • 【测试开发知识储备】之Jacoco(Java Code Coverage)
  • LeetCode100.7 接雨水
  • 互降关税后,从中国至美国的集装箱运输预订量飙升近300%
  • 陕西省安康市汉阴县县长陈永乐已任汉阴县委书记
  • 央行等印发《关于金融支持广州南沙深化面向世界的粤港澳全面合作的意见》
  • 警方通报“网约车司机偷拍女乘客”:已被行政拘留
  • 10名“鬼火少年”凌晨结队在城区飙车,警方:涉非法改装,正处理
  • 著名蒙古族音乐学者马•斯尔古愣逝世,享年86岁