这是我参与新手入门的第1篇文章
this是JavaScript中的关键字,与传统面向对象的语言相比,JavaScript中的this更加灵活,涉及到它出现的位置和代表的含义。在讲述之前,我们先来看一下不使用this的情况。
this的基本使用
-
现在有一个需求,需要在对象的方法中输出一些信息,如下
var obj = { name: "zhangsan", run: function () { console.log(obj.name + " run"); }, study: function () { console.log(obj.name + " study"); }, };上述代码的缺点:如果将obj名称改成info,那么所有方法中的
obj.name都需要换成info.name -
在实际开发中,我们会通过
this关键字进行优化,如下var obj = { name: "zhangsan", run: function () { console.log(this.name + " run"); }, study: function () { console.log(this.name + " study"); }, };经过上述修改后,方法中通过
this引用,解耦了对变量名的依赖。修改对象变量名时,无需修改方法中引用的名称。
this的指向
在全局作用域下,this指向什么?
console.log(this); // window
var name = "zhangsan";
console.log(this.name); // zhangsan
console.log(window.name); // zhangsan
console.log(this === window); // true
- 在浏览器环境中,全局作用域下,this指向window
在实际开发中,很少会直接在全局作用域下使用this,通常都是在函数中使用。函数在被调用时,都会创建一个执行上下文,用于记录
- 函数的调用栈、调用方式、传入参数等信息
- this也是其中的一个属性
定义一个函数、通过3种方式进行调用,产生了3种不同结果
// 定义一个函数
function foo() {
console.log(this);
}
foo(); // window对象
var obj = {
name: "zhangsan",
foo: foo,
};
obj.foo(); // {name: "zhangsan", foo: ƒ}
foo.call("lisi"); // String {"lisi"}
- 函数在调用时,JavaScript会默认给
this绑定一个值 this的绑定和函数定义的位置无关this的绑定和函数调用方式及调用位置有关this是在运行时绑定的
那么,this的绑定规则是怎样的呢?
this绑定规则
1、默认绑定
独立的函数调用,可以理解成函数没有被绑定到某个对象上的调用
案例1:普通函数调用
-
函数直接调用,没有与任何对象关联
-
这种独立的函数调用就会使用默认绑定,通常情况下,函数中的this指向全局对象(window)
function foo() { console.log(this); // window } foo();
案例2:函数调用链
-
所有函数调用都没有绑定到对象上
function foo() { console.log(this); // window bar(); } function bar() { console.log(this); // window baz(); } function baz() { console.log(this); // window } foo();
案例3:将函数作为另一个函数的参数进行调用
-
将函数作为参数,传入另一个函数中,在另一函数中直接调用该函数
function foo(fn) { fn(); } function bar() { console.log(this); // window } foo(bar);
案例4:将对象中的一个方法传入另一个函数中进行调用
-
将对象中的方法传入到另一个函数中,另一个函数直接调用
function foo(fn) { fn(); } var obj = { name: "zhangsan", bar: function () { console.log(this); // window }, }; foo(obj.bar);- 上例中的this之所以是window,是因为在真正调用函数的位置,并没有与对象进行关联,是一个独立的函数调用
2、隐式绑定
比较常见的调用方式,通过某个对象进行调用。也就是它的调用位置中,是通过某个对象发起的函数调用
案例1:通过对象调用函数
- foo的调用位置是
obj.foo()方式进行调用的 - foo调用时的this就会绑定到obj对象上
function foo() {
console.log(this); // obj
console.log(this === obj); // true
}
var obj = {
name: "zhangsan",
foo: foo,
};
obj.foo();
案例2:多层对象调用函数
- 通过obj2引用了obj1,再通过obj1对象调用foo函数
- foo调用时的this还是绑定了obj1对象上
function foo() {
console.log(this); // obj1
console.log(this === obj1); // true
}
var obj1 = {
name: "zhangsan",
foo: foo,
};
var obj2 = {
name: "zhangsan",
obj1: obj1,
};
obj2.obj1.foo();
案例3:隐式丢失
- foo被调用的位置是bar,而bar在调用时没有绑定任何对象,是独立函数调用。
function foo() {
console.log(this); // window
console.log(this === window); // true
}
var obj = {
name: "zhangsan",
foo: foo,
};
var bar = obj.foo;
bar();
隐式绑定的前提条件是
- 必须在调用的对象内部有一个对函数的引用
- 如果没有这样的引用,在进行调用时,会报错找不到该函数
- 正是通过这个引用,间接将this绑定到这个对象上。
如果不希望对象内部包含这个属性,同时又希望在这个对象上强制调用,可以通过call()、apply()进行显式绑定
3、显式绑定
JavaScript所有函数都可以使用call和apply方法
- 二者的区别在于第2个参数不同。call是参数列表、apply是数组.
- 二者的第1个参数,是在函数调用时绑定到的this对象
案例1:通过call、apply绑定this
- 显式绑定后,this就会明确的指向绑定的对象
function foo() {
console.log(this);
}
var obj = {
name: "zhangsan",
};
foo.call(window); // window
foo.call(obj); // obj {name: "zhangsan"}
foo.call("lisi"); // String {"lisi"}
案例2:通过bind函数
- 通过bind绑定this后,调用bind方法返回的函数,该函数的this始终是传入bind函数的参数
function foo() {
console.log(this);
}
var obj = {
name: "zhangsan",
};
var bar = foo.bind(obj);
bar(); // obj {name: "zhangsan"}
bar(); // obj {name: "zhangsan"}
4、new绑定
JavaScript中的函数,可以通过new关键字当作一个类的构造函数来使用。通过new关键字调用函数时,会执行如下操作:
- 创建一个新对象
- 这个新对象会执行prototype连接
- 新对象会绑定到函数调用的this上
- 如果函数没有返回其他对象,则会返回这个新对象
function Person(name) {
console.log(this); // Person {}
this.name = name;
}
var p = new Person("zhangsan");
console.log(p); // Person {name: "zhangsan"}