this知多少
什么是this?
在 JavaScript 中,this
是一个指向对象的引用,这个对象是在运行时基于函数的执行环境动态绑定的。换句话说,this
的值取决于函数被调用时的上下文。它允许函数在不同的上下文中使用相同的代码。
为什么要有this?
this在JS中发挥着很重要的作用,具体体现如下:
1. 上下文感知
this
允许函数根据调用它的方式来引用不同的对象,使得代码更具灵活性和通用性。这意味着同一个函数可以在不同的上下文中执行不同的操作。
示例场景: 在一个事件处理函数中,根据触发事件的对象不同,执行相应的操作。
js
const button1 = document.getElementById('button1');
const button2 = document.getElementById('button2');
function handleClick() {
console.log(`Button "${this.textContent}" was clicked.`);
}
button1.addEventListener('click', handleClick);
button2.addEventListener('click', handleClick);
2. 简化代码
通过使用 this
,可以避免硬编码特定对象的引用,使代码更易于维护和重用。这样可以使函数更加通用,适用于不同的情况。
示例场景: 创建一个通用的计算器对象,可以执行加法和乘法操作。
js
const calculator = {
add: function(a, b) {
return this.sum(a, b);
},
multiply: function(a, b) {
return this.product(a, b);
},
sum: function(a, b) {
return a + b;
},
product: function(a, b) {
return a * b;
}
};
console.log(calculator.add(5, 3)); // 输出: 8
console.log(calculator.multiply(2, 4)); // 输出: 8
3. 面向对象编程
在面向对象的编程中,this
是一种常见的机制,用于访问对象的属性和方法。它允许在对象内部引用对象自身的属性和方法。
示例场景: 创建一个 Person 对象,其中包含姓名和打招呼的方法。
js
function Person(name) {
this.name = name;
}
Person.prototype.greet = function() {
console.log(`Hello, my name is ${this.name}.`);
};
const john = new Person('John');
john.greet(); // 输出: Hello, my name is John.
如果不使用this::
在许多其他编程语言中,对象的方法通常可以直接访问对象的属性。
但是在js中,对象方法内部的 尝试访问的是对象属性,但是在该函数内部并没有定义该变量,会抛出一个 ReferenceError。要访问对象的属性,应该使用 this
关键字来引用对象的属性,而不是直接引用变量名。
4. 函数重用
通过动态绑定 this
,可以使函数适用于不同的对象上,从而实现代码的重用。这使得同一个函数可以在不同的上下文中被调用,执行不同的任务。
示例场景: 使用构造函数创建多个对象,并共享同一个方法。
js
function Car(make, model) {
this.make = make;
this.model = model;
}
Car.prototype.start = function() {
console.log(`${this.make} ${this.model} is starting...`);
};
const car1 = new Car('Toyota', 'Corolla');
const car2 = new Car('Honda', 'Civic');
car1.start(); // 输出: Toyota Corolla is starting...
car2.start(); // 输出: Honda Civic is starting...
this绑定规则
默认绑定:
当一个函数独立调用,不带任何修饰符的时候
- 函数在哪个词法作用域下生效(调用),函数中的this就指向哪里
(词法作用域是指在代码写作时定义变量的位置所确定的作用域。)
- 只要是默认绑定,this一定指向window
js
function sayName() {
console.log(this.name);
}
var name = "Global";
sayName(); // 输出: Global
注意点:
在严格模式下(非浏览器运行环境下),默认绑定不会指向全局对象,而是会绑定到 undefined
。
隐式绑定:
当函数的引用有上下文对象时(当函数被某个对象拥有)
- 函数中的this指向引用它的对象
- 当一个函数被多个对象链式调用时,函数的this指向就近的那个对象
例如:
js
var obj = {
a: 1,
foo: foo
}
var obj2 = {
a: 2,
obj: obj
}
function foo() {
console.log(this.a);
}
var a = 3
obj2.obj.foo();
在这种情况下,foo
函数内的 this
关键字将指向 obj
对象,因为它是通过 obj2.obj.foo()
调用的。因此,this.a
将输出 1
,而不是 3
。
- 隐式丢失:当一个函数作为对象的方法被调用时,函数内部引用了其他对象的方法,导致
this
指向丢失的现象。
隐式丢失通常发生在以下几种情况下:
- 回调函数中的隐式丢失 :当将一个函数作为回调传递给另一个函数,并且在回调函数中引用了对象的方法时,由于回调函数的执行环境不再是该对象,
this
指向会丢失。
js
var obj = {
name: "Alice",
sayName: function() {
console.log(this.name);
},
greet: function() {
setTimeout(function() {
console.log("Hello, " + this.name); // 隐式丢失
}, 1000);
}
};
obj.greet(); // 输出: "Hello, undefined"(在非严格模式下输出: "Hello, [全局对象的某个属性]")
在这个例子中,setTimeout
函数内部的回调函数中的 this
指向了全局对象,而不是 obj
对象,导致了隐式丢失。
- 事件处理函数中的隐式丢失 :当将一个对象的方法作为事件处理函数,绑定到 DOM 元素的事件上时,由于事件处理函数的执行环境是 DOM 元素,而不是对象,
this
指向会丢失。
xml
<button id="myButton">Click Me</button>
<script>
var obj = {
name: "Alice",
handleClick: function() {
console.log("Button clicked by " + this.name); // 隐式丢失
}
};
document.getElementById("myButton").addEventListener("click", obj.handleClick);
</script>
在这个例子中,当按钮被点击时,handleClick
方法中的 this
会指向按钮元素,而不是 obj
对象,导致了隐式丢失。
- 函数赋值后的隐式丢失 :当将一个对象的方法赋值给一个变量,并在其他上下文中调用该变量时,由于执行环境的改变,
this
指向会丢失。
js
var person = {
name: "Alice",
sayName: function() {
console.log(this.name);
}
};
var say = person.sayName;
say(); // 输出: undefined(在非严格模式下输出: [全局对象的某个属性])
在这个例子中,say
变量在调用时没有指定上下文对象,导致 this
指向了全局对象,而不是 person
对象,发生了隐式丢失。
显式绑定:
通过call、apply、bind方法来指定this指向
-
call 方法:
call
方法立即调用函数,并将一个指定的this
值和若干个指定的参数传递给函数。call
方法的语法为function.call(thisArg, arg1, arg2, ...)
。
js
var person1 = { name: "Alice" };
var person2 = { name: "Bob" };
function sayName() {
console.log("My name is " + this.name);
}
sayName.call(person1); // 输出: "My name is Alice"
sayName.call(person2); // 输出: "My name is Bob"
-
apply 方法:
apply
方法与call
方法类似,但它接受一个包含参数的数组作为参数。apply
方法的语法为function.apply(thisArg, [argsArray])
。
js
var person = { name: "Alice" };
function sayName(greeting) {
console.log(greeting + ", my name is " + this.name);
}
sayName.apply(person, ["Hello"]); // 输出: "Hello, my name is Alice"
-
bind 方法:
bind
方法创建一个新函数,在调用时将其this
值绑定到传递给bind
的值,并在调用时指定任何预设的参数。bind
方法的语法为function.bind(thisArg[, arg1[, arg2[, ...]]])
。
js
var person = { name: "Alice" };
function sayName() {
console.log("My name is " + this.name);
}
var sayNameWithPerson = sayName.bind(person);
sayNameWithPerson(); // 输出: "My name is Alice"
不同之处:
- 在使用
call
和apply
方法时,函数会立即执行,并且可以传入单个参数或者一个参数数组; call
和apply
的区别在于参数的传递方式:call
是逐个传参,而apply
是以数组形式传参。而bind
方法可以预先绑定this
和部分参数,并在需要时执行。bind
方法返回的是一个绑定了指定上下文的新函数,不会立即执行,需要手动调用执行。
new绑定:
this指向创建的实例对象
ini
function Person(name) {
this.name = name;
}
var john = new Person("John");
console.log(john.name); // 输出: John
在这个例子中,this
被绑定到了新创建的 john
对象,因此在构造函数中可以正确地设置对象的属性。
错误示例:
js
function Person(name) {
this.name = name;
}
// 错误示例:忘记使用 new 关键字
var john = Person("John");
console.log(john); // undefined
console.log(name); // 输出: John
// 错误示例:忘记返回对象
function createPerson(name) {
this.name = name;
}
var alice = new createPerson("Alice");
console.log(alice); // 输出: { name: 'Alice' }
console.log(name); // 输出: Alice
在这些错误示例中,忘记使用 new
关键字或者忘记在构造函数中返回对象都会导致意料之外的结果,因为此时 this
指向全局对象,而不是新创建的实例对象。
this绑定优先级:new绑定 > 显式绑定 > 隐式绑定 > 默认绑定
箭头函数中的this
箭头函数中的this
是在定义函数时确定的,而不是在运行时。箭头函数没有自己的this
绑定,它会捕获其所在上下文的this
值(写在箭头函数中的this也是它外层非箭头函数的this ),并在整个函数生命周期内保持不变。
js
const obj = {
name: 'Alice',
sayName: function() {
setTimeout(() => {
console.log(this.name);
}, 1000);
}
};
obj.sayName(); // 输出 'Alice',箭头函数捕获了 sayName 方法的 this 值
总结
掌握this,好好学习,天天向上