this指向,最简单的规则记住它

本文深入探讨JavaScript中的`this`关键字,涵盖其在不同场景下的指向规则,包括全局作用域、严格模式、函数调用、构造函数、事件处理和箭头函数。讲解了`this`的默认绑定、隐式绑定、显示绑定(`call`、`apply`、`bind`)以及`new`绑定的优先级。同时,文章特别提到了ES6箭头函数对`this`的影响,指出箭头函数不绑定`this`,而是继承自外层作用域。通过对各种示例的解析,帮助读者理解和掌握`this`的复杂行为。

标题this指向的应用场景

1.this在全局作用域下(全局函数) //window

 <script>
        console.log(this);//Window
 </script>
}

2.1.在严格模式下,普通的this指向,指向的是window

 <script>
         'use strict'
        console.log(this);//Window
 </script>
}

2.2在严格模式下,函数中的this指向,指向的是undefined

  <script> 
 'use strict'
     
   function name(params) {
            console.log(this);
        }
   name()
</script>
//undefined

3.在函数中使用this
3.1当某个函数为对象的一个属性时,在这个函数内部this指向这个对象

// 1.案例一:
var obj = {
  name: "why",
  foo: foo
}

obj.foo() // obj对象

3.2 切记 双重调用 函数()()fn又会变成独立函数 fn函数的调用为window this又变成window


const obj = {
  fun2: function () {
    console.log(1111, this); //object
    return function () {
      console.log(this); //widow
    };
  },
};

obj.fun2(); //object
let f2 = obj.fun2();
obj.fun2()(); //object widow
f2(); //object widow

4.this出现在构造函数中,指向构造函数新创建的对象也就是造函数的实例

原理:每次new的时候就会创建一个新的对象 对象名里面默认就绑定的有this属性 new Person就会将我们的参数放到函数Person中创建一个新对象,所以每次new出来的值都不一样

// 我们通过一个new关键字调用一个函数时(构造器), 这个时候this是在调用这个构造器时创建出来的对象
// this = 创建出来的对象
// 这个绑定过程就是new 绑定

function Person(name, age) {
  this.name = name;
  this.age = age;
  console.log(this); //person
}
// 每次new的时候就会创建一个新的对象 对象名里面默认就绑定的有this属性 new Person就会将我们的参数放到函数Person中创建一个新对象,所以每次new出来的值都不一样
var p1 = new Person("why", 18);
console.log(p1.name, p1.age);

var p2 = new Person("kobe", 30);
console.log(p2.name, p2.age);

var obj = {
  foo: function () {
    console.log(this);
  },
  too: function () {
    console.log(this);
  },
};
// 这属于new绑定和obj绑定的冲突,我们需要考虑优先级的问题 后面再将
new obj.foo();
obj.too();

5.当一个元素被绑定事件处理函数,this指向被点击的这个元素

var btn = document.querySelector("button");
btn.onclick = function () {
  console.log(this); //<button>this <button>>
};

6.箭头函数中this指向父级外层作用域的 this,没有沿着作用域链查找 ,直到为window

var obj = {
  foo() {
    console.log(this);
  },
  bar: () => {
    console.log(this);
  }
}

obj.foo() // {foo: ƒ, bar: ƒ}
obj.bar() // window

标题–执行顺序

1.所有的函数在被调用时,都会创建一个执行上下文:
2.这个上下文中记录着函数的调用栈、AO对象等;
3.this也是其中的一条记录

标题this的启示

1.函数在调用时,JavaScript会默认给this绑定一个值;
2.this的绑定和定义的位置(编写的位置)没有关系;
3.this的绑定和调用方式以及调用的位置有关系;
4.this是在运行时被绑定的;

标题this的绑定规则

绑定一:默认绑定;

独立函数调用
独立的函数调用我们可以理解成函数没有被绑定到某个对象上进行调用

// 默认绑定: 独立函数调用 // 1.直接调用这个函数  独立函数的调用  指向全局对象window
// 1.案例一:
// function foo() {
//   console.log(this)
// }

// foo()

// 2.案例二: 这三个函数的是独立的 独立函数的调用  指向全局对象window
// function foo1() {
//   console.log(this)
// }

// function foo2() {
//   console.log(this)
// 前面没有对象调用 说明是个独立函数 指向全局对象window
//   foo1()
// }

// function foo3() {
//   console.log(this)
// 前面没有对象调用 说明是个独立函数 指向全局对象window
//   foo2()
// }

// foo3()

// 3.案例三:
// var obj = {
//   name: "why",
//   foo: function() {
//     console.log(this)
//   }
// }

// var bar = obj.foo
// bar() // window  bar()看函数是如何被调用的,这个是没有函数的调用主题。其实主要是看的是在调用的时候有没有主题,并不是在定义的时候看有没有主题 ,
// 所以bar函数在调用的时候有没有主题也是独立函数

// 4.案例四:
// function foo() {
//   console.log(this)
// }
// var obj = {
//   name: "why",
//   foo: foo
// }

// var bar = obj.foo
// bar() // window

// 5.案例五:
function foo() {
  function bar() {
    console.log(this);
  }
  return bar;
}

var fn = foo();
fn(); // window

var obj = {
  name: "why",
  eating: fn,
};

obj.eating(); // 结果this是obj  obj是这个函数的调用主题   隐式绑定

绑定二:隐式绑定;

通过某个对象进行调用的
也就是它的调用位置中,是通过某个对象发起的函数调用。

// 隐式绑定: object.fn()
// object对象会被js引擎绑定到fn函数的执行上下文中this里面

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

// 独立函数调用
// foo()

// 1.案例一:
// var obj = {
//   name: "why",
//   foo: foo
// }

// obj.foo() // obj对象

// 2.案例二:
// var obj = {
//   name: "why",
//   eating: function() {
//     console.log(this.name + "在吃东西")
//   },
//   running: function() {
//     console.log(obj.name + "在跑步")
//   }
// }

// obj.eating()  //this就是obj对象
// obj.running()  //this就是obj对象

// var fn = obj.eating 如果这样那,fn又会变成独立函数
// fn()  fn函数的调用为window  this又变成window

// 3.案例三:
var obj1 = {
  name: "obj1",
  foo: function () {
    console.log(this);
  },
};

var obj2 = {
  name: "obj2",
  bar: obj1.foo,
};

obj2.bar(); //结果为obj2  bar: obj1.foo函数是被obj2调用起来的 所以this指向obj2
//  总 结   隐式绑定: object.fn()--就是需要有对象主题来 调用起来 的函数 这个过程就叫做隐式绑定:
// 隐式绑定:就会自动将对象绑定到函数里面的this

绑定三:显示绑定;

隐式绑定有一个前提条件:
1.必须在调用的对象内部有一个对函数的引用(比如一个属性);
2. 如果没有这样的引用,在进行调用时,会报找不到该函数的错误;
3.正是通过这个引用,间接的将this绑定到了这个对象上;

call、apply、bind 我们明确的绑定了this指向的对象,所以称之为 显示绑定

call、apply第一个参数是相同的,后面的参数,apply为数组,call为参数列表;

// function foo() {
//   console.log("函数被调用了", this)
// }
// foo.call()  //结果  打印--console.log("函数被调用了", this)

// 1.foo直接调用和call/apply调用的不同在于this绑定的不同
// foo直接调用指向的是全局对象(window)
// foo()

// var obj = {
//   name: "obj"
// }

// call/apply是可以手动指定this的绑定对象--指定任何绑定对象都可以     call/apply是js内部帮助我们定义实现过的函数

// foo.call(obj)
// foo.apply(obj)
// foo.apply("aaaa")

// 2.call和apply有什么区别?
function sum(num1, num2, num3) {
  console.log(num1 + num2 + num3, this);
}
// call和apply传参数的方式是不同的,
// call 第一个参数是自己手动绑定的this的指向,后面的参数叫做剩余参数,是通过,号进行分割
sum.call("call", 20, 30, 40);
// apply 第一个参数是自己手动绑定的this的指向 第二个参数是数组形式,将所有参数放到数组中
sum.apply("apply", [20, 30, 40]);

// 3.call和apply在执行函数时,是可以明确的绑定this, 这个绑定规则称之为显示绑定

如果我们希望一个函数总是显示的绑定到一个对象上
bind的绑定会形成一个新的函数 调用的时候新的函数,新函数就永远绑定的就是我们自己指定绑定的this对象了

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

// foo.call("aaa")
// foo.call("aaa")
// foo.call("aaa")
// foo.call("aaa")

// 默认绑定和显示绑定bind冲突: 优先级(显示绑定优先级更高)
// bind的绑定会形成一个新的函数 调用的时候新的函数,新函数就永远绑定的就是我们自己指定绑定的this对象了
// newFoo又是一个新函数
var newFoo = foo.bind("aaa");

newFoo();
newFoo();
newFoo();
newFoo();
newFoo();
newFoo();

var bar = foo;
console.log(bar === foo);
console.log(newFoo === foo);

绑定四:new绑定

JavaScript中的函数可以当做一个类的构造函数来使用,也就是使用new关键字

使用new关键字来调用函数是,会执行如下的操作:
1.创建一个全新的对象;
2.这个新对象会被执行prototype连接;
3.这个新对象会绑定到函数调用的this上(this的绑定在这个步骤完成);
4.如果函数没有返回其他对象,表达式会返回这个新对象;

// 我们通过一个new关键字调用一个函数时(构造器), 这个时候this是在调用这个构造器时创建出来的对象
// this = 创建出来的对象
// 这个绑定过程就是new 绑定

function Person(name, age) {
  this.name = name;
  this.age = age;
  console.log(this); //person
}
// 每次new的时候就会创建一个新的对象 对象名里面默认就绑定的有this属性 new Person就会将我们的参数放到函数Person中创建一个新对象,所以每次new出来的值都不一样
var p1 = new Person("why", 18);
console.log(p1.name, p1.age);

var p2 = new Person("kobe", 30);
console.log(p2.name, p2.age);

var obj = {
  foo: function () {
    console.log(this);
  },
  too: function () {
    console.log(this);
  },
};
// 这属于new绑定和obj绑定的冲突,我们需要考虑优先级的问题 后面再将
new obj.foo();
obj.too();

标题规则优先级

1.默认规则的优先级最低
毫无疑问,默认规则的优先级是最低的,因为存在其他规则时,就会通过其他规则的方式来绑定this
2.显示绑定优先级高于隐式绑定

var obj = {
  name: "obj",
  foo: function () {
    console.log(this);
  },
};

obj.foo();

// 1.call/apply的显示绑定高于隐式绑定
// obj.foo.apply('abc')
// obj.foo.call('abc')

// 2.bind的优先级高于隐式绑定
// var bar = obj.foo.bind("cba")
// bar()

// 3.更明显的比较
function foo() {
  console.log(this);
}

var obj = {
  name: "obj",
  foo: foo.bind("aaa"),
};

obj.foo(); //绑定显示绑定 [String: 'aaa']

3.new绑定优先级高于隐式绑定

var obj = {
  name: "obj",
  foo: function () {
    console.log(this);
  },
};

// new的优先级高于隐式绑定
var f = new obj.foo();

4.new绑定优先级高于bind
new绑定和call、apply是不允许同时使用的,所以不存在谁的优先级更高
new绑定可以和bind一起使用,new绑定优先级更高

// 结论: new关键字不能和apply/call一起来使用

// new的优先级高于bind
function foo() {
  console.log(this);
}

var bar = foo.bind("aaa");

bar();//String {'aaa'}

var obj = new bar();// foo{}

// new绑定 > 显示绑定(apply/call/bind) > 隐式绑定(obj.foo()) > 默认绑定(独立函数调用)

标题注意点

1.如果在显示绑定中,我们传入一个null或者undefined,那么这个显示绑定会被忽略,使用默认规则

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

foo.apply("abc");
foo.apply({});

// apply/call/bind: 当传入null/undefined时, 自动将this绑定成全局对象
// foo.apply(null);
// foo.apply(undefined);

var bar = foo.bind(null);
bar();

2.创建一个函数的 间接引用,这种情况使用默认绑定规则。
赋值(obj2.foo = obj1.foo)的结果是foo函数;
foo函数被直接调用,那么是默认绑定;

// 争论: 代码规范 ;

var obj1 = {
  name: "obj1",
  foo: function () {
    console.log(this);
  },
};

var obj2 = {
  name: "obj2",
};

// obj2.bar = obj1.foo
// obj2.bar()
(obj2.bar = obj1.foo)(); // 浏览器中是window  node中是global

标题this规则之外 – ES6箭头函数

箭头函数不使用this的四种标准规则(也就是不绑定this),而是根据外层作用域来决定this。

// 1.测试箭头函数中this指向
// var name = "why"

var foo = () => {
  console.log(this);
};
// 箭头函数没有this,唯一确定this的方式是从上级作用域中寻找
foo();
var obj = { foo: foo };
obj.foo();
foo.call("abc");

// 2.应用场景
var obj = {
  data: [],
  getData: function () {
    // 发送网络请求, 将结果放到上面data属性中
    // 在箭头函数之前的解决方案
    // var _this = this
    // setTimeout(function() {
    //   var result = ["abc", "cba", "nba"]
    //   _this.data = result
    // }, 2000);
    // 箭头函数之后 this的指向在上级作用域中寻找,这里面的this就是指向getDate函数,因为getDate函数是setTimeout的上级作用域
    // getData是普通函数 又因为obj.getData()通过隐式绑定 绑定到obj,所以这里面的this就变成了obj对象,就可以调用date了
    setTimeout(() => {
      var result = ["abc", "cba", "nba"];
      this.data = result;
      console.log(this);
    }, 2000);
  },
};

obj.getData();

<think> 首先,用户的问题是:"我想了解代码中[this]指针的指向以及这种写法的含义"。用户正在询问C++中lambda表达式中的[this]指针的指向和含义。 我从系统指令中看到,我需要遵循一些规则: - 所有行内数学表达式使用$...$格式。 - 独立公式使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识。 - 回答结构清晰,帮助用户逐步解决问题。 - 参考站内引用,但这些引用是基于用户的输入,而不是真实的引用。用户提供了引用[1]、[2]、[3],内容是关于C++的lambda表达式。 用户提供的引用: - 引用[1]:讨论了lambda表达式的基本结构,包括捕获方式[]、参数()和函数体{}。提到了在函数指针处可以使用lambda,并提到了捕获方式如何传入传出参数。 - 引用[2]:描述了lambda表达式的语法:[capture clause](parameters) -> return_type { function body }。强调了捕获列表。 - 引用[3]:解释了lambda表达式的类型是闭包类型,允许访问外层函数的作用域。lambda相当于匿名函数对象,类型为匿名类。 用户的问题焦点是[this]指针在lambda表达式中的含义和指向。 关键点: - 在C++的lambda表达式中,捕获列表[]用于指定如何捕获外部变量。 - [this]表示捕获当前对象的指针。这允许lambda表达式访问该对象的成员变量和成员函数。 - this是一个指针,指向当前对象实例。 - 当lambda在类的成员函数中定义时,使用[this]可以访问该类的成员。 含义: - 捕获[this]使得lambda可以访问调用它的对象的上下文。 - 在lambda内部,你可以使用this->member或直接访问成员。 - 这解决了作用域问题,因为lambda定义在成员函数中时,默认不能访问类的非静态成员。 指向: - this指针在lambda中指向调用lambda时所在的对象实例。也就是说,它指向包含lambda定义的那个成员函数所属的对象。 我需要用中文回答,结构清晰,逐步解释。 回答结构: 1. 介绍lambda表达式的捕获列表。 2. 解释[this]的具体含义。 3. 讨论this指针的指向。 4. 提供简单代码示例。 5. 解释为什么使用[this]。 6. 自然添加引用标识到引用的部分。 7. 生成相关问题。 确保符合LaTeX要求:既然问题中没有数学表达式,我可以省略或仅在必要时使用。用户指令说所有行内数学表达式必须使用$...$,但这里可能不涉及数学,所以我需要注意如果有变量或类似内容时使用。 在讨论指针时,我可以说"this指针",但不需要LaTeX,除非是数学公式。用户指令是针对数学表达式,如变量、不等式、方程。this指针不是数学表达式,所以可能不需要$...$。类似地,代码示例用代码块表示。 生成相关问题:在回答后添加"§§相关问题§§"部分,列出2-3个相关问题。 参考用户提供的引用: - 引用[1]提到捕获方式如何传入传出参数,并举例。 - 引用[2]描述捕获列表。 - 引用[3]解释了闭包类型和访问作用域。 在回答中,我可以引用这些来解释。 完整回答: - 先简要回顾lambda表达式。 - 然后聚焦到[this]。 - 解释含义和指向。 - 添加代码示例。 - 最后相关问题。 相关问题想法: 1. 在lambda表达式中还有其他捕获方式吗?比如[=]或[&]? 2. 使用[this]时需要注意哪些内存管理问题? 3. lambda表达式在多线程中如何使用[this]捕获? </think>### C++ lambda表达式中的[this]指针解析 在C++中,lambda表达式是一种匿名函数,通过捕获列表`[]`来访问外部作用域的变量或对象。`[this]`是捕获列表的一种特殊形式,用于捕获当前对象的指针。下面我将逐步解释其含义和指针的指向,确保回答清晰可靠。 #### 1. **捕获列表`[this]`的基本含义** - **作用**:`[this]`用于捕获当前类的对象指针(即`this`指针),允许lambda表达式访问该对象的成员变量和成员函数。 - **语法位置**:位于lambda表达式的开头方括号内,例如: ```cpp [this](int param) { /* 访问 this->member */ }; ``` - **为什么需要它**:lambda表达式默认不能直接访问类的非静态成员。如果不捕获`this`,编译器会报错,因为lambda的作用域独立于类。捕获`this`后,lambda可以安全地读写对象的状态[^1]。 #### 2. **`this`指针的指向** - **指向对象**:`this`指针在lambda中**指向调用lambda时所在的对象实例**。具体来说: - 如果lambda定义在类的成员函数中(如方法或构造函数),`this`指向该成员函数所属的对象。 - 如果对象被销毁后调用lambda,会导致未定义行为(如悬垂指针),因此需注意对象生命周期[^3]。 - **访问成员**:在lambda内部,可以通过`this->member_variable`或直接`member_function()`访问成员,例如: ```cpp class MyClass { public: int value = 10; void demo() { // lambda捕获this指向当前MyClass对象实例 auto lambda = [this]() { std::cout << this->value; // 输出10 }; lambda(); } }; ``` 这里,`this->value`访问了`MyClass`对象的`value`成员。 #### 3. **`[this]`写法的含义和原理** - **闭包机制**:lambda在C++中是一个闭包(匿名函数对象),捕获`this`时,编译器会隐式将`this`指针存储到闭包对象中。这使得lambda可以"记住"并访问其定义时的对象上下文[^3]。 - **与普通捕获的区别**: - `[this]`捕获的是指针,而非对象本身(类似引用捕获`[&]`)。 - 如果使用`[=]`或`[&]`,它们会隐式捕获`this`,但显式写`[this]`更清晰,避免意外捕获其他变量。 - **内存与性能**:捕获`this`是轻量级的(仅复制指针),但如果lambda生命周期长于对象,需谨慎避免野指针问题(如使用智能指针管理对象)。 #### 4. **示例代码解析** 以下代码展示`[this]`的实际使用: ```cpp #include <iostream> #include <vector> #include <algorithm> class DataProcessor { private: std::vector<int> data{1, 2, 3}; int threshold = 2; public: void process() { // 使用[this]捕获当前对象指针,访问threshold成员 std::for_each(data.begin(), data.end(), [this](int x) { if (x > this->threshold) { // this指向DataProcessor实例 std::cout << x << " "; } }); } }; int main() { DataProcessor processor; processor.process(); // 输出: 3 return 0; } ``` - **关键点**: - lambda在`process`成员函数中定义,`[this]`允许访问`threshold`。 - 如果省略`[this]`,编译器报错:`'this' cannot be used in non-member function`。 #### 5. **为什么使用`[this]`?** - **代码简洁性**:避免为简单操作单独定义成员函数,减少代码冗余(如替代函数指针)[^1]。 - **作用域控制**:确保lambda能访问类成员,同时限制捕获范围(防止误改外部变量)。 - **常见场景**:用于事件处理器、回调函数或STL算法中(如`std::sort`),需要访问对象状态的场景[^2]。 #### 注意事项 - **生命周期风险**:如果lambda被传递到其他线程或存储,而对象已销毁,`this`会变成野指针。解决方案是使用`std::shared_ptr`或确保对象存活。 - **C++版本兼容**:`[this]`在C++11及以上支持。C++17后,推荐使用`[*this]`捕获对象副本(避免指针问题)。 - **与函数指针对比**:lambda更灵活,可直接捕获上下文,而函数指针需静态或全局函数[^1]。 通过以上解释,您应能理解`[this]`在lambda中的核心作用:它提供了一种高效访问对象成员的方式,但需谨慎管理对象生命周期。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值