this是函数执行时的上下文对象,它的值取决于函数如何被调用。
function showThis() {
console.log(this);
}
// 不同调用方式,this不同
showThis(); // 全局对象(严格模式为undefined)
obj.showThis(); // obj
showThis.call(obj); // obj
new showThis(); // 新创建的对象
const obj = {
name: "东巴文",
greet: function() {
console.log(this.name);
function inner() {
console.log(this.name); // 不是obj.name
}
inner(); // 普通调用,this是全局或undefined
}
};
obj.greet();
// 东巴文
// undefined(严格模式)
当函数独立调用时,使用默认绑定。
// 非严格模式
function showThis() {
console.log(this); // window(浏览器)
}
showThis();
// 严格模式
"use strict";
function showThisStrict() {
console.log(this); // undefined
}
showThisStrict();
function foo() {
console.log(this);
}
foo(); // 默认绑定
// 嵌套函数
function outer() {
console.log(this); // 默认绑定
function inner() {
console.log(this); // 默认绑定
}
inner();
}
outer();
| 模式 | 独立调用的this | 东巴文建议 |
|---|---|---|
| 非严格 | 全局对象 | - |
| 严格 | undefined | 使用严格模式 |
当函数作为对象方法调用时,使用隐式绑定。
const obj = {
name: "东巴文",
greet: function() {
console.log(this.name);
}
};
obj.greet(); // "东巴文"(this指向obj)
const obj = {
name: "东巴文",
greet: function() {
console.log(this.name);
}
};
// 赋值后丢失绑定
const greet = obj.greet;
greet(); // undefined(this丢失)
// 作为回调传递
function runCallback(callback) {
callback();
}
runCallback(obj.greet); // undefined
const obj = {
name: "东巴文",
greet: function() {
console.log(this.name);
}
};
// 方法1:使用bind
const greet = obj.greet.bind(obj);
greet(); // "东巴文"
// 方法2:使用箭头函数
const obj2 = {
name: "东巴文",
greet: function() {
const arrow = () => {
console.log(this.name);
};
arrow(); // "东巴文"
}
};
// 方法3:保存this引用
const obj3 = {
name: "东巴文",
greet: function() {
const self = this;
setTimeout(function() {
console.log(self.name); // "东巴文"
}, 100);
}
};
使用call、apply、bind显式指定this。
function greet(greeting, punctuation) {
console.log(`${greeting}, ${this.name}${punctuation}`);
}
const obj = { name: "东巴文" };
greet.call(obj, "你好", "!"); // 你好, 东巴文!
function greet(greeting, punctuation) {
console.log(`${greeting}, ${this.name}${punctuation}`);
}
const obj = { name: "东巴文" };
greet.apply(obj, ["你好", "!"]); // 你好, 东巴文!
// call:参数逐个传递
fn.call(thisArg, arg1, arg2, arg3);
// apply:参数以数组传递
fn.apply(thisArg, [arg1, arg2, arg3]);
// 实际应用
const numbers = [5, 6, 2, 3, 7];
const max = Math.max.apply(null, numbers); // 7
const min = Math.min.apply(null, numbers); // 2
// ES6展开运算符替代
const max2 = Math.max(...numbers);
function greet(greeting) {
console.log(`${greeting}, ${this.name}`);
}
const obj = { name: "东巴文" };
// bind返回新函数,不立即执行
const greetDongba = greet.bind(obj);
greetDongba("你好"); // 你好, 东巴文
// 柯里化
function add(a, b) {
return a + b;
}
const addFive = add.bind(null, 5);
console.log(addFive(3)); // 8
// bind绑定后无法再改变
function foo() {
console.log(this.name);
}
const obj1 = { name: "东巴文" };
const obj2 = { name: "JavaScript" };
const boundFoo = foo.bind(obj1);
boundFoo(); // "东巴文"
boundFoo.call(obj2); // "东巴文"(无法改变)
boundFoo.apply(obj2); // "东巴文"
使用new调用构造函数时,this指向新创建的对象。
function Person(name) {
this.name = name;
}
const person = new Person("东巴文");
// new做了什么:
// 1. 创建一个新对象
// 2. 将this绑定到新对象
// 3. 执行构造函数代码
// 4. 返回新对象(除非构造函数返回对象)
function myNew(Constructor, ...args) {
// 1. 创建新对象,原型指向构造函数的prototype
const obj = Object.create(Constructor.prototype);
// 2. 执行构造函数,绑定this
const result = Constructor.apply(obj, args);
// 3. 如果构造函数返回对象,则返回该对象
if (result && typeof result === "object") {
return result;
}
// 4. 否则返回新对象
return obj;
}
const person = myNew(Person, "东巴文");
function Person(name) {
this.name = name;
return { custom: true }; // 返回对象
}
const p = new Person("东巴文");
console.log(p); // { custom: true }(不是Person实例)
function Person2(name) {
this.name = name;
return "字符串"; // 返回原始值
}
const p2 = new Person2("东巴文");
console.log(p2); // Person2 { name: "东巴文" }(忽略原始值返回)
bind方法创建一个新函数,绑定this和部分参数。
const module = {
x: 42,
getX: function() {
return this.x;
}
};
const unboundGetX = module.getX;
console.log(unboundGetX()); // undefined
const boundGetX = unboundGetX.bind(module);
console.log(boundGetX()); // 42
function multiply(a, b) {
return a * b;
}
const double = multiply.bind(null, 2);
console.log(double(5)); // 10
const triple = multiply.bind(null, 3);
console.log(triple(5)); // 15
function Timer() {
this.seconds = 0;
// 不使用bind
setInterval(function() {
this.seconds++; // this不指向Timer实例
}, 1000);
// 使用bind
setInterval(function() {
this.seconds++;
console.log(this.seconds);
}.bind(this), 1000);
}
Function.prototype.myBind = function(thisArg, ...args) {
const fn = this;
const bound = function(...moreArgs) {
// 如果作为构造函数调用,this指向新实例
const isNewCall = this instanceof bound;
return fn.apply(
isNewCall ? this : thisArg,
[...args, ...moreArgs]
);
};
// 维护原型链
bound.prototype = Object.create(fn.prototype);
return bound;
};
call方法立即调用函数,指定this和参数。
function greet(greeting, punctuation) {
console.log(`${greeting}, ${this.name}${punctuation}`);
}
const person = { name: "东巴文" };
greet.call(person, "你好", "!"); // 你好, 东巴文!
// 借用数组方法
const arrayLike = {
0: "东",
1: "巴",
2: "文",
length: 3
};
const arr = Array.prototype.slice.call(arrayLike);
console.log(arr); // ["东", "巴", "文"]
// 借用Object方法
const obj = { a: 1, b: 2 };
const hasA = Object.prototype.hasOwnProperty.call(obj, "a");
console.log(hasA); // true
const numbers = [5, 6, 2, 3, 7];
// 使用apply
const max = Math.max.apply(null, numbers);
// 使用call和展开运算符
const max2 = Math.max.call(null, ...numbers);
apply方法与call类似,但参数以数组形式传递。
function greet(greeting, punctuation) {
console.log(`${greeting}, ${this.name}${punctuation}`);
}
const person = { name: "东巴文" };
greet.apply(person, ["你好", "!"]); // 你好, 东巴文!
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
// 合并数组
arr1.push.apply(arr1, arr2);
console.log(arr1); // [1, 2, 3, 4, 5, 6]
// 或使用展开运算符
const merged = [...arr1, ...arr2];
function getType(value) {
return Object.prototype.toString.apply(value);
}
getType([]); // "[object Array]"
getType({}); // "[object Object]"
getType(new Date()); // "[object Date]"
getType(null); // "[object Null]"
getType(undefined); // "[object Undefined]"
执行上下文是JavaScript代码执行的环境。
// 全局执行上下文
var globalVar = "全局";
// 函数执行上下文
function foo() {
var localVar = "局部";
}
// Eval执行上下文(不推荐使用)
eval("var evalVar = 'eval'");
创建阶段:
1. 创建变量对象(VO)
2. 建立作用域链
3. 确定this值
执行阶段:
1. 变量赋值
2. 函数引用
3. 执行其他代码
function foo(a, b) {
var c = 10;
function d() {}
var e = function() {};
}
foo(10, 20);
// 变量对象(创建阶段)
VO = {
arguments: { 0: 10, 1: 20, length: 2 },
a: 10,
b: 20,
c: undefined,
d: <function d>,
e: undefined
}
// 执行阶段
VO = {
arguments: { 0: 10, 1: 20, length: 2 },
a: 10,
b: 20,
c: 10,
d: <function d>,
e: <function>
}
调用栈用于管理函数调用的LIFO结构。
function first() {
console.log("first开始");
second();
console.log("first结束");
}
function second() {
console.log("second开始");
third();
console.log("second结束");
}
function third() {
console.log("third");
}
first();
// 调用栈变化:
// 1. first入栈
// 2. second入栈
// 3. third入栈
// 4. third出栈
// 5. second出栈
// 6. first出栈
// 输出:
// first开始
// second开始
// third
// second结束
// first结束
function recurse() {
recurse(); // 无限递归
}
recurse(); // RangeError: Maximum call stack size exceeded
function foo() {
bar();
}
function bar() {
console.trace(); // 打印调用栈
}
foo();
// 输出:
// bar
// foo
// <anonymous>
this绑定的优先级从高到低:
// 显式绑定 > 隐式绑定
function foo() {
console.log(this.name);
}
const obj1 = { name: "东巴文", foo };
const obj2 = { name: "JavaScript" };
obj1.foo(); // "东巴文"(隐式绑定)
obj1.foo.call(obj2); // "JavaScript"(显式绑定优先)
// new绑定 > 显式绑定
function Person(name) {
this.name = name;
}
const boundPerson = Person.bind({ name: "绑定的" });
const p = new boundPerson("东巴文");
console.log(p.name); // "东巴文"(new绑定优先)
1. 函数是否用new调用?→ this是新创建的对象
2. 函数是否用call/apply/bind调用?→ this是绑定的对象
3. 函数是否作为对象方法调用?→ this是该对象
4. 默认绑定 → 严格模式undefined,非严格模式全局对象
掌握了this与执行上下文后,让我们继续学习:
东巴文(db-w.cn) - 让编程学习更简单
🎯 东巴文寄语:理解this是JavaScript进阶的关键,掌握this的四种绑定规则和优先级,能让你准确预测和控制this的值。在 db-w.cn,我们帮你彻底搞懂this!