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

相关推荐
有点笨的蛋2 小时前
JavaScript Promise 机制解析
前端·javascript
不一样的少年_3 小时前
【前端效率工具】再也不用 APIfox 联调!零侵入 Mock,全程不改代码、不开代理
前端·javascript·浏览器
艾小码4 小时前
Vue组件通信不再难!这8种方式让你彻底搞懂父子兄弟传值
前端·javascript·vue.js
lcc1874 小时前
Vue 数据代理
前端·javascript·vue.js
青衫码上行5 小时前
【Java Web学习 | 第七篇】JavaScript(1) 基础知识1
java·开发语言·前端·javascript·学习
咖啡の猫5 小时前
Vue编程式路由导航
前端·javascript·vue.js
视图猿人14 小时前
RxJS基本使用及在next.js中使用的例子
开发语言·javascript
bitbitDown14 小时前
从零打造一个 Vite 脚手架工具:比你想象的简单多了
前端·javascript·面试
冴羽16 小时前
为什么在 JavaScript 中 NaN !== NaN?背后藏着 40 年的技术故事
前端·javascript·node.js