萌新小白基础理解篇之 this 关键字

前言

  早在我们前几篇文章中,就有出现过 this ,但是我们一直没有详细解释 this 是什么,this 可以出现在哪,this 的用法又是如何?那这篇文章我们一起来看看吧!

一、为什么要有this?

  this 是 js 中的一个关键字,它提供了一种更优雅的方式隐式的传递一个对象的引用,可以让代码更简洁易于复用,js 关键字是内置好的,拥有特殊语法含义的词,不能作为变量名,函数名,还有if,else,for等等关键字。我们来看一段代码感受一下。

js 复制代码
 function identify(context) {
   return context.name.toUpperCase()  //.toUpperCase()  让小写全转化为大写 
 }

 function speek(context) {
   var greeting = 'hello, I am ' + identify(context)
   console.log(greeting);
 }

 var me = {
   name: 'tom'
 }

 speek(me)  

  当代码运行到14行时带来speek()函数的调用,把me作为实参传进去,此时运行speek()函数又带来了identify() 的调用,将me作为实参传进去,返回得到大写的 TOM ,console.log(greeting)得到 hello,I am TOM。

  如果用 this 我们可以怎么写

js 复制代码
function identify() {
  return this.name.toUpperCase()
}

function speek() {
  var greeting = 'hello, I am ' + identify.call(this)
  console.log(greeting);
}

var me = {
  name: 'tom'
}

speek.call(me)

  我们可以看到,上述结果是相同的,函数 speekidentify 不再接收 context 参数。

  • 使用 this 关键字直接访问调用上下文中的属性(如 this.name)。
  • 调用时,通过 .call(me) 显式绑定 this 指向目标对象。下文会详细解释.call()用法
  • 逻辑链条变为:对象 → 绑定为 this → 函数内部直接通过 this 访问

  它提供了一种更优雅的方式隐式的传递一个对象的引用,可以让代码更简洁易于复用。

二、this 可以出现在哪?

  • 1.全局 (this === window
  • 2.函数体内

  理论上,this 可以出现在任何地方,如果出现在全局,那么 统一代指 的是window,所以我们主要区分函数体内的 this 代指的是哪个,this 用在不同的地方,代指的内容是不一样的。

this 可以出现在块级作用域但是毫无意义

三、 this的绑定规则

1.默认绑定 --- 当函数独立调用时,函数中的 this 指向 window
js 复制代码
var a = 1   //  ===window:{a:1}  等于往window里面增加了 a为1 

function foo(){
    console.log(this.a)  // 1
}

function bar () {
    var a = 2
    foo() //独立调用
}

bar()

   this 如果出现在全局,那么它代指 window , 此时 this 出现在foo函数内,但是这个foo函数是被独立调用 的,那么此时 this 依旧指向 windowconsole.log(this.a) 为 1,什么叫独立调用呢? 独立调用 = 函数名直接加括号执行,没有任何对象或上下文"牵着"它。

2.隐式绑定 --- 当一个函数被一个上下文对象所拥有并被该对象调用,那么函数中的 this 指向该对象
js 复制代码
var a = 1   //  ===window:{a:1}  等于往window里面增加了 a为1 

function foo(){
    console.log(this.a)   //3
}

function bar () {
    var a = 2
    foo() //独立调用
}

bar()

var test = {
    a : 3,
    foo :foo  //引用函数
}

test.foo()  //隐式绑定

  我们可以看到 前面的 foo() 就是单独的函数名+括号的形式 , 后面的为 test.foo() ,打个比方,就像你一个人逛街和你女朋友牵着你逛街的区别,你一个人逛街就叫独立调用,有女朋友牵着就不叫独立调用,此处我们称之为隐式绑定 ,而此时 this 指向的对象 就是 test ,所以此时 console.log(this.a) 为3

3.隐式丢失 --- 当一个函数被多层对象调用,函数的 this 指向最近的对象
js 复制代码
function foo(){
    console.log(this.a)
}
var obj = {
    a:1, 
    foo : foo   //key :value  ,key 的名字可以随便取, 但 value 不可以随便
}
var oo = {
    a : 2,
    foo : obj
}

oo.foo.foo()  //this 指向 obj

  我们来捋一捋这个代码的逻辑,v8运行这段代码,运行到13行前,知道有一个 foo函数 ,有一个obj 对象 ,一个 oo 对象 ,当运行到13行时,有函数的调用,才开始读取它们的内容,那代码是从左往右执行,先读取 oo.foo ,那v8就要去oo里面找这个 foo 是什么,我们可以看到此时的 foo 值为 obj 对象,那就相当于 obj.foo() , 在去obj中 找 foo 是什么,此时 foo 的值 为foo 函数 ,然后() 开始foo函数的调用,所以相当于是 obj 调用了这个函数,此时 this 指向 obj ,也即当一个函数被多层对象调用,函数的 this 指向最近的对象。

4.显示绑定 --- 强行''掰弯'' this 指向一个对象 (三种方法)
  • fn.call(obj, x, y)

  • fn.apply(obj, x,y)

  • fn.bind(obj, x, y)()

js 复制代码
function foo(x,y){
    console.log(this.a, x+y)
}

var A = {
    a : 1
}

foo() //独立调用 指向 window
foo.call(A,1,2)  //this 指向A  传递参数 1,2
foo.apply(A,[2,3])  // this 指向A 传入参数2,3 
foo.bind(A,1,2)() //this 指向A,传入参数1,2 

.call( obj, x, y) : 让 this 强行指向 A,可以逐个传递参数 (较为零散的方式传递参数)
.apply( obj, x,y ) : 让 this 强行指向 A,以数组的模式逐个传递参数 (较为集中的方式传递参数)
.bind( obj, x, y)() : 让 this 强行指向 A,但是执行完后一定会返回一个函数出来 ,并且要把它触发掉 ,也是零散的传递参数 ,也可以 const bar 来接收 返回的函数 再调用触发,可以分开传参

scss 复制代码
const bar = foo.bind(obj,x,y)   const bar = foo.bind (obj,x)   const bar = foo.bind (obj) 
bar()                            bar(y)                        bar(x,y)
5.new 绑定 --- new 的原理会导致函数的 this 指向实例对象
js 复制代码
function Person(){
    // var obj = {}      //1
    //Person.call(obj)   //2
    this.name = '杰哥'    //3   等同于  obj.name = '杰哥'
    // obj.__proto__ = Person.prototype    //4
    //return obj         //5
}

const p = new Person()  //此时的 p = obj
console.log(p)   // {name : 杰哥}

  我们在万物皆对象那篇文章中有讲到过 new 的工作原理,但当时并没有详细解释 this 所以表述其实并不准确,new 的具体工作原理应该是这样

  • 创建一个空对象 即 var obj = {}

  • 让函数体的 this 强行指向 实例对象 即 Person.call(obj)

  • 运行函数内的代码逻辑

  • 让对象的原型等于函数的原型 即 obj.proto = Person.prototype

  • 返回这个对象 即 return obj

四、箭头函数

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

js 复制代码
var bar = function(){     //函数表达式

}
bar()

var baz = (x,y) => {     //函数表达式
   
}

  如果不用到 this ,两种写法都是可以的,但如果用到 this 那我们需要注意一下了

js 复制代码
function foo(){
    var fn = () =>{   //箭头函数没有 this 这个概念
        this.a = 2
    }
    fn()
}

var obj = {
    a : 1,
    bar:foo
}
obj.bar()
console.log(obj)

  由于箭头函数没有 this 这个概念,写在箭头函数中的 this,也是它外层那个非箭头函数的,所以此时 this 是 foo的 ,而foo是通过obj.bar()调用的,所以 foo 的 this 指向 obj 对象,console.log(obj) 得到 { a : 1, bar : foo }
箭头函数不可以被new调用 (new的第二步无法执行,用了就会报错)

(如有补充,请大佬指点)

相关推荐
爱读源码的大都督6 小时前
Claude Code源码分析(三):为什么系统提示词中需要有tools呢?
前端·人工智能·后端
爱勇宝6 小时前
Claude Code 被曝暗藏“隐形检测”代码:封代理不是最可怕的,可怕的是你根本不知道它在干什么
前端·后端·程序员
小牛不牛的程序员6 小时前
我用 Claude Code 半天撸完了一个完整网站,AI 编程到底提升了多少效率?
前端
东风破_6 小时前
JavaScript 面试常考的字符串算法:从反转字符串到回文判断
前端·javascript
巴勒个啦6 小时前
D3.js 入门实战:用力导向图可视化项目依赖关系
javascript
ITOM运维行者6 小时前
从零搭建企业级服务器监控体系:踩坑实录与架构设计
前端·后端
monologues6 小时前
深入 Vue 3 源码:响应式系统的精妙设计与编译优化
前端
hunterandroid6 小时前
Paging 3 分页:从手动分页到声明式加载
前端
用户4099322502126 小时前
Vue状态管理入门第四章:组合式store和SSR风险
前端·vue.js·后端
Csvn7 小时前
CSS :has() 选择器实战:没有它之前我们写了多少冗余 JS
前端·css