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吧加油!!!!!

相关推荐
Mr Xu_29 分钟前
Vue 3 中计算属性的最佳实践:提升可读性、可维护性与性能
前端·javascript
子兮曰44 分钟前
深入理解滑块验证码:那些你不知道的防破解机制
前端·javascript·canvas
Highcharts.js1 小时前
【Highcharts】如何用命令行渲染导出图片?
javascript·导出·开发文档·highcharts·命令行渲染·命令行功能
陈振wx:zchen20082 小时前
JavaScript
javascript·js
我是伪码农2 小时前
Vue 智慧商城项目
前端·javascript·vue.js
不认输的西瓜2 小时前
fetch-event-source源码解读
前端·javascript
天下代码客3 小时前
使用electronc框架调用dll动态链接库流程和避坑
前端·javascript·vue.js·electron·node.js
冰暮流星4 小时前
javascript之数组
java·前端·javascript
xkxnq5 小时前
第五阶段:Vue3核心深度深挖(第74天)(Vue3计算属性进阶)
前端·javascript·vue.js
三小河5 小时前
Agent Skill与Rules的区别——以Cursor为例
前端·javascript·后端