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

相关推荐
梓沂4 小时前
playEdu自定义接口需要满足的格式
前端·javascript·react.js
古韵4 小时前
深入alova3服务端能力:分布式BFF层到API网关的最佳实践
javascript·redis·node.js
m0_740043734 小时前
JavaScript
开发语言·javascript·ecmascript
艾小码4 小时前
Vue开发三年,我才发现依赖注入的TypeScript正确打开方式
前端·javascript·vue.js
云中飞鸿13 小时前
函数:委托
javascript
老前端的功夫14 小时前
前端技术选型的理性之道:构建可量化的ROI评估模型
前端·javascript·人工智能·ubuntu·前端框架
狮子座的男孩14 小时前
js函数高级:04、详解执行上下文与执行上下文栈(变量提升与函数提升、执行上下文、执行上下文栈)及相关面试题
前端·javascript·经验分享·变量提升与函数提升·执行上下文·执行上下文栈·相关面试题
爱学习的程序媛14 小时前
《JavaScript权威指南》核心知识点梳理
开发语言·前端·javascript·ecmascript
乐观主义现代人15 小时前
go 面试
java·前端·javascript
2501_9418868615 小时前
多语言微服务架构下的微服务熔断与限流优化实践
javascript