# 基本调用
var name = 'window'
function getName() {
console.log(this.name)
}
const obj = {
name: 'obj',
getName: getName
}
getName()
obj.getName()
解析:
独立函数调用 getName() 中,this 指向全局对象(浏览器中是 window)
作为对象方法调用 obj.getName() 时,this 指向调用它的对象 obj
# 默认绑定与严格模式
var name = 'window';
function sayName() {
'use strict';
console.log(this.name);
}
sayName();
解析: 在严格模式下,函数的 this 不会默认指向全局对象,而是 undefined。
因此 this.name 试图访问 undefined 的属性,会抛出 TypeError: Cannot read properties of undefined。
如果去掉'use strict',则会输出 window。
# 隐式绑定丢失
var name = 'window';
var obj = {
name: 'obj',
sayName: function() {
console.log(this.name);
}
};
var fn = obj.sayName;
fn();
解析: fn取得的是obj.sayName的函数引用,此时它不再作为对象的方法调用,而是普通的函数调用。在非严格模式下,独立函数调用的this指向全局对象window。
# 显式绑定与new绑定的优先级
function Person(name) {
this.name = name;
}
var obj = {};
var boundPerson = Person.bind(obj);
boundPerson('Alice');
console.log(obj.name); // ①
var p = new boundPerson('Bob');
console.log(p.name); // ②
console.log(obj.name); // ③
解析: bind返回的函数将this绑定为obj。第一次调用boundPerson('Alice'),this指向obj,所以obj.name为Alice。
使用new操作符调用boundPerson时,new绑定的优先级高于显式绑定,this指向新创建的对象p,因此p.name为Bob,而obj.name仍然是Alice未被修改。
# 箭头函数的this
var name = 'window';
var obj = {
name: 'obj',
sayName: () => {
console.log(this.name);
},
greet: function() {
setTimeout(() => {
console.log(this.name);
}, 100);
}
};
obj.sayName();
obj.greet();
解析:
obj.sayName()是箭头函数,箭头函数本身没有this,它会捕获定义时所在上下文的this。
对象字面量本身不产生作用域,所以定义时的this是全局window(非严格模式下)。因此输出window。
obj.greet()中的setTimeout使用箭头函数,它捕获了greet函数执行时的this。因为greet是通过obj调用的,其this指向obj,所以延迟函数输出obj。
# 综合题(多种绑定混合)
var length = 10;
function fn() {
console.log(this.length);
}
var obj = {
length: 5,
method: function(f) {
f();
arguments[0]();
}
};
obj.method(fn, 1);
解析:
第一个调用f():传入的是fn函数,此时是独立函数调用,this指向全局window,输出10。
第二个调用arguments[0]():arguments对象是类数组,arguments[0]是fn函数,但作为arguments对象的方法调用。
此时this指向arguments对象,而arguments对象有length属性,表示实参个数。传入的实参是fn和1,所以arguments.length为2,输出2。
# setTimeout中的this
var name = 'window';
var obj = {
name: 'obj',
sayName: function() {
setTimeout(function() {
console.log(this.name);
}, 100);
}
};
obj.sayName();
解析: setTimeout的回调函数是独立调用,不是通过对象调用的。在浏览器非严格模式下,定时器的回调函数中的this指向全局对象window。
# this与闭包
var name = 'window';
var obj = {
name: 'obj',
sayName: function() {
return function() {
console.log(this.name);
};
}
};
obj.sayName()();
解析: obj.sayName()返回一个函数,然后立即执行()。这个返回的函数是独立调用,不依赖于任何对象,因此this指向全局对象window。
# 保存this引用
var name = 'window';
var obj = {
name: 'obj',
sayName: function() {
var self = this;
return function() {
console.log(self.name);
};
}
};
obj.sayName()();
解析: 这是一个常见的闭包技巧。在sayName方法中,self保存了当前this(指向obj),返回的闭包函数通过作用域链访问self,因此输出obj。
# 事件处理中的this
<button id="btn">点击</button>
<script>
var btn = document.getElementById('btn');
btn.onclick = function() {
console.log(this.id);
};
</script>
解析: 在DOM事件处理中,事件处理函数中的this指向触发事件的元素。所以this.id就是按钮的id属性值"btn"。
# DOM事件与箭头函数
<button id="btn">点击</button>
<script>
var btn = document.getElementById('btn');
btn.addEventListener('click', () => {
console.log(this.id);
});
</script>
解析: 箭头函数没有自己的this,它捕获定义时的this(这里是全局对象)。全局对象没有id属性,所以输出undefined。
# 构造函数返回值的影响
function Person(name) {
this.name = name;
return {};
}
var p = new Person('Alice');
console.log(p.name);
解析: 如果构造函数显式返回一个对象,那么new表达式的结果就是这个返回的对象,而不是默认创建的this。
这里返回了空对象{},所以p指向{},没有name属性,输出undefined。
# 构造函数返回基本类型
function Person(name) {
this.name = name;
return 123;
}
var p = new Person('Bob');
console.log(p.name);
解析: 如果构造函数返回的是基本类型(非对象),new表达式会忽略这个返回值,仍然返回新创建的实例对象。所以p是Person的实例,p.name为Bob。
# 原型链中的this
var name = 'window';
function Animal(name) {
this.name = name;
}
Animal.prototype.sayName = function() {
console.log(this.name);
};
var dog = new Animal('dog');
var cat = new Animal('cat');
dog.sayName();
cat.sayName();
解析: 原型链上的方法在调用时,this指向调用该方法的对象实例。所以dog.sayName()输出dog,cat.sayName()输出cat。
# call/apply与null/undefined
var name = 'window';
function sayName() {
console.log(this.name);
}
sayName.call(null);
sayName.apply(undefined);
解析: 使用call或apply时,如果传入的第一个参数是null或undefined,在非严格模式下会被忽略,
this指向全局对象;在严格模式下则指向传入的值(null或undefined)。本题未指定严格模式,所以输出两次window。
# 多层链式调用
var obj1 = {
name: 'obj1',
fn: function() {
console.log(this.name);
}
};
var obj2 = {
name: 'obj2',
obj1: obj1
};
obj2.obj1.fn();
解析: this指向的是直接调用函数的对象,即.前面的最后一个对象。这里是obj2.obj1.fn(),直接调用者是obj1,所以this指向obj1。
# 隐式绑定丢失的典型场景-函数传参
var name = 'window';
function run(fn) {
fn();
}
var obj = {
name: 'obj',
sayName: function() {
console.log(this.name);
}
};
run(obj.sayName);
解析: 将方法作为参数传递时,只是传递了函数引用,丢失了与对象的绑定。在run函数内部,fn()是独立调用,所以this指向全局对象。
# 箭头函数与普通函数的混合
解析: obj.sayName()执行时,this指向obj,返回的箭头函数捕获了这个this。即使后面独立调用fn(),箭头函数的this仍然是之前捕获的obj。
# 1. class中的this
class Person {
constructor(name) {
this.name = name;
}
sayName() {
console.log(this.name);
}
}
var p = new Person('Tom');
var say = p.sayName;
say();
解析: 类的内部默认是严格模式。将方法赋值给变量后独立调用,this为undefined,试图访问undefined.name会抛出TypeError。
# 2. class中的this
class Person {
constructor(name) {
this.name = name
}
getName() {
console.log(this.name)
}
getNameArrow = () => {
console.log(this.name)
}
}
const p = new Person('Alice')
const fn1 = p.getName
const fn2 = p.getNameArrow
fn1()
fn2()
解析:
getName 是原型方法,赋值后独立调用丢失 this
getNameArrow 是箭头函数(类字段语法),绑定到实例,赋值后仍保留 this
# 多重bind
function fn() {
console.log(this.name);
}
var obj1 = { name: 'obj1' };
var obj2 = { name: 'obj2' };
var boundFn = fn.bind(obj1);
boundFn = boundFn.bind(obj2);
boundFn();
解析: bind绑定是永久性的。一旦函数被bind绑定过,后续的bind不能再改变它的this指向。
所以第一次bind(obj1)后,this已经固定为obj1,再次bind(obj2)无效。