this函数的指向问题

总结:

函数内部的this指向取决于函数的调用方式,而不是它被定义的位置。当函数被独立调用时,在非严格模式下,this指向全局对象。在严格模式下,独立调用的函数中的this是undefined。

因此,当你看到函数内部的this指向全局对象时,通常是因为这个函数被独立调用了。要改变这种行为,可以使用箭头函数、显式绑定或闭包(保存this)等方法

核心原因:调用方式决定 this

关键点this 的指向不取决于函数在哪里定义,而取决于函数如何被调用

1. 独立函数调用(最常见的陷阱)

javascript

javascript 复制代码
var name = 'Global Name';

function showThis() {
  console.log(this); // window
  console.log(this.name); // 'Global Name'
}

// 独立调用 - this 指向 window
showThis(); 

// 等同于:
window.showThis(); // 浏览器环境下实际上是这样

为什么?

  • 当函数被独立调用时(前面没有 对象.),JavaScript 使用默认绑定规则
  • 在非严格模式下,默认绑定会将 this 指向全局对象

2. 嵌套函数中的陷阱

javascript

javascript 复制代码
var name = 'Global';

var obj = {
  name: 'Object',
  outer: function() {
    console.log('outer this:', this.name); // 'Object'
    
    function inner() {
      console.log('inner this:', this.name); // 'Global' - 为什么??
    }
    
    inner(); // 独立调用!
  }
};

obj.outer(); // outer 作为方法调用,inner 作为独立函数调用

解析

  • obj.outer()outerthis 指向 obj
  • inner() → 独立调用,innerthis 指向 window

3. 回调函数中的陷阱

javascript

javascript 复制代码
var name = 'Global';

var obj = {
  name: 'Object',
  handleClick: function() {
    console.log('handleClick this:', this.name); // 'Object'
  },
  setupEvent: function() {
    console.log('setupEvent this:', this.name); // 'Object'
    
    // 回调函数 - 独立调用!
    setTimeout(function() {
      console.log('setTimeout this:', this.name); // 'Global'
    }, 100);
  }
};

obj.setupEvent();

解析

  • obj.setupEvent()this 指向 obj
  • setTimeout 的回调函数被独立调用this 指向 window

为什么会这样设计?

历史原因和设计哲学

  1. 早期设计决策:JavaScript 最初设计时,函数被认为是"独立"的实体
  2. 动态语言特性:JavaScript 是动态语言,函数可以很容易地在不同上下文中重用
  3. 灵活性:这种设计让函数可以在不同对象间共享

javascript

javascript 复制代码
// 同一个函数可以在不同对象上使用
function introduce() {
  return `Hello, I'm ${this.name}`;
}

var person = { name: 'Alice' };
var company = { name: 'Tech Corp' };

// 显式绑定
console.log(introduce.call(person));  // "Hello, I'm Alice"
console.log(introduce.call(company)); // "Hello, I'm Tech Corp"

如何解决这个问题?

方法1:使用箭头函数(推荐)

箭头函数没有自己的 this,会继承外层作用域的 this

javascript

javascript 复制代码
var obj = {
  name: 'Object',
  outer: function() {
    console.log('outer this:', this.name); // 'Object'
    
    const inner = () => {
      console.log('inner this:', this.name); // 'Object' - 继承 outer 的 this
    };
    
    inner();
  }
};

obj.outer();

方法2:保存 this 引用(传统方式)

javascript

javascript 复制代码
var obj = {
  name: 'Object',
  outer: function() {
    var self = this; // 保存 this 的引用
    console.log('outer this:', this.name); // 'Object'
    
    function inner() {
      console.log('inner self:', self.name); // 'Object' - 使用保存的引用
    }
    
    inner();
  }
};

obj.outer();

方法3:使用 bind()

javascript

javascript 复制代码
var obj = {
  name: 'Object',
  outer: function() {
    console.log('outer this:', this.name); // 'Object'
    
    const inner = function() {
      console.log('inner this:', this.name); // 'Object'
    }.bind(this); // 绑定 outer 的 this
    
    inner();
  }
};

obj.outer();

严格模式的影响

在严格模式下,独立调用的函数中 thisundefined,而不是 window

javascript

javascript 复制代码
'use strict';

var name = 'Global';

function showThis() {
  console.log(this); // undefined
  console.log(this?.name); // TypeError: Cannot read property 'name' of undefined
}

showThis(); // 严格模式下,独立调用的 this 是 undefined

总结

场景 this 指向 原因
func() window 独立调用,默认绑定
obj.method() obj 方法调用,隐式绑定
嵌套函数 inner() window 独立调用,不是方法调用
回调函数 window 被事件循环独立调用
箭头函数 外层 this 词法作用域,继承父级

核心要点

  1. 调用方式决定 this,不是定义位置
  2. 独立调用的函数this 指向全局对象
  3. 使用箭头函数保存 this 引用来避免这个问题
  4. 严格模式 下独立调用的 thisundefined

理解这个机制后,你就能预测和控制 this 的行为了!

JavaScript this 指向练习题

以下是一系列关于 this 指向的练习题,涵盖了各种常见场景。请先尝试自己解答,然后再查看解析。

基础题目

题目 1

javascript

ini 复制代码
var name = 'Global';

function sayName() {
  console.log(this.name);
}

sayName();

题目 2

javascript

javascript 复制代码
var obj = {
  name: 'Object',
  sayName: function() {
    console.log(this.name);
  }
};

obj.sayName();

题目 3

javascript

ini 复制代码
var name = 'Global';

var obj = {
  name: 'Object',
  sayName: function() {
    console.log(this.name);
  }
};

var fn = obj.sayName;
fn();

题目 4

javascript

ini 复制代码
var obj1 = {
  name: 'Object1',
  sayName: function() {
    console.log(this.name);
  }
};

var obj2 = {
  name: 'Object2'
};

obj2.sayName = obj1.sayName;
obj2.sayName();

嵌套函数题目

题目 5

javascript

javascript 复制代码
var name = 'Global';

var obj = {
  name: 'Object',
  outer: function() {
    console.log('outer:', this.name);
    
    function inner() {
      console.log('inner:', this.name);
    }
    
    inner();
  }
};

obj.outer();

题目 6

javascript

ini 复制代码
var name = 'Global';

var obj = {
  name: 'Object',
  outer: function() {
    console.log('outer:', this.name);
    
    const inner = () => {
      console.log('inner:', this.name);
    };
    
    inner();
  }
};

obj.outer();

回调函数题目

题目 7

javascript

javascript 复制代码
var name = 'Global';

var obj = {
  name: 'Object',
  handleClick: function() {
    setTimeout(function() {
      console.log(this.name);
    }, 100);
  }
};

obj.handleClick();

题目 8

javascript

ini 复制代码
var name = 'Global';

var obj = {
  name: 'Object',
  handleClick: function() {
    setTimeout(() => {
      console.log(this.name);
    }, 100);
  }
};

obj.handleClick();

构造函数题目

题目 9

javascript

ini 复制代码
function Person(name) {
  this.name = name;
  console.log(this.name);
}

var person = new Person('Alice');

题目 10

javascript

javascript 复制代码
function Person(name) {
  this.name = name;
  
  setTimeout(function() {
    console.log('Timeout:', this.name);
  }, 100);
}

var person = new Person('Alice');

显式绑定题目

题目 11

javascript

ini 复制代码
var obj1 = {
  name: 'Object1'
};

var obj2 = {
  name: 'Object2'
};

function sayName() {
  console.log(this.name);
}

sayName.call(obj1);
sayName.call(obj2);

题目 12

javascript

javascript 复制代码
var obj = {
  name: 'Object',
  sayName: function(greeting) {
    console.log(greeting + ', ' + this.name);
  }
};

var boundFn = obj.sayName.bind({ name: 'Bound' });
boundFn('Hello');

综合题目

题目 13

javascript

javascript 复制代码
var name = 'Global';

var obj = {
  name: 'Object',
  method: function() {
    console.log('1:', this.name);
    
    const arrow = () => {
      console.log('2:', this.name);
    };
    
    function regular() {
      console.log('3:', this.name);
    }
    
    arrow();
    regular();
    
    setTimeout(() => {
      console.log('4:', this.name);
    }, 10);
    
    setTimeout(function() {
      console.log('5:', this.name);
    }, 10);
  }
};

obj.method();

题目 14

javascript

ini 复制代码
var length = 10;

function fn() {
  console.log(this.length);
}

var obj = {
  length: 5,
  method: function(fn) {
    fn();
    arguments[0]();
  }
};

obj.method(fn, 1);

题目 15

javascript

javascript 复制代码
var name = 'Global';

function Person(name) {
  this.name = name;
  
  this.sayName = function() {
    console.log('1:', this.name);
  };
  
  this.sayNameArrow = () => {
    console.log('2:', this.name);
  };
}

Person.prototype.delayedSayName = function() {
  setTimeout(function() {
    console.log('3:', this.name);
  }, 10);
};

Person.prototype.delayedSayNameArrow = function() {
  setTimeout(() => {
    console.log('4:', this.name);
  }, 10);
};

var person = new Person('Alice');
person.sayName();
person.sayNameArrow();
person.delayedSayName();
person.delayedSayNameArrow();

做完再去问ai吧加油!!!!!

相关推荐
多看书少吃饭5 小时前
从Vue到Nuxt.js
前端·javascript·vue.js
前端一小卒5 小时前
从 v5 到 v6:这次 Ant Design 升级真的香
前端·javascript
前端不太难6 小时前
《Vue 项目路由 + Layout 的最佳实践》
前端·javascript·vue.js
想学后端的前端工程师6 小时前
【Vue3组合式API实战指南:告别Options API的烦恼】
前端·javascript·vue.js
一勺-_-7 小时前
mermaid图片如何保存成svg格式
开发语言·javascript·ecmascript
GISer_Jing8 小时前
深入拆解Taro框架多端适配原理
前端·javascript·taro
毕设源码-邱学长8 小时前
【开题答辩全过程】以 基于VUE的藏品管理系统的设计与实现为例,包含答辩的问题和答案
前端·javascript·vue.js
San30.9 小时前
深入理解 JavaScript:手写 `instanceof` 及其背后的原型链原理
开发语言·javascript·ecmascript
北冥有一鲲9 小时前
LangChain.js:RAG 深度解析与全栈实践
开发语言·javascript·langchain
狗狗摇屁屁11 小时前
JS手写防抖
开发语言·javascript·ecmascript