一、前言
我们每天都在书写着有关于this的javascript代码,似懂非懂地在用着。前阵子在看了《你不知道的JavaScript上卷》之后,也算是被扫盲了一边关于this绑定的四种方式。
二、绑定规则
关于this应用的是哪条规则,得先找到调用的位置,再判断应用了哪条规则。
1、默认绑定
先上代码:
var a = 2; function foo() {
console.log(this.a);
} foo(); // 结果:2
先来分析下上面的代码声明,
首先我们在全局作用域中定义了一个变量a,而在全局作用域声明的变量,就相当于为window对象声明了同名属性a并赋值为2,接着又在全局作用域下声明了foo函数,最后我们在调用foo函数时,是直接不带任何修饰下调用foo函数,此时的this的绑定规则为默认绑定,this指向window对象,所以结果输入为2。
如果此时使用了严格模式,即在代码中加了"use strict",this指向undefined,调用的结果就会出错而不是输出2。
2、隐式绑定
var a = 2; function foo() {
console.log(this.a);
} var obj = {
a: 4,
foo:foo
}; foo(); // 结果:2
obj.foo(); // 结果4
代码还是差不多的代码,只是加了一个obj对象,对象有一个a属性和一个foo属性引用了foo函数。对于foo();输出结果2,在上一节已经说明,因为是没用带任何修饰的情况下调用,应用了默认绑定。而在obj.foo()的调用中,foo函数的调用上下文是obj对象,obj包含了foo函数,此时this的绑定就发生了隐式绑定,this指向obj,this.a相当于obj.a,所以结果自然而然也输入4。
对于这种方式,我在代码中也经常用到。把可以归类的方法、变量都写成一个对象的形式,就形成了一个模块,也相当于一种设计模式,模块模式。
3、显式绑定
var a = 2; function foo() {
console.log(this.a);
} var obj = {
a: 4
}; foo.call(obj); // 结果:4
foo.apply(obj); // 结果:4
foo.bind(obj)(); // 结果:4
对于call、apply、bind的这三种调用方式都是属于显式绑定,作用是通过显示传入一个对象,改变this的上下文为此对象。call和apply是直接改变上下文对象直接调用,而bind是返回一个已经显示绑定的上下文的函数。
call和apply两个都是显示改变上下文并执行,唯一不同的就是传参方式,call是对象后面可以跟着多个参数,而apply传递参数,需要传递一个数组,即:
foo.call(obj, arg1, arg2, arg3, ...);
foo.apply(obj, [arg1, arg2, arg3, ...])
4、new绑定
学过后端语言的人都知道,通过构造函数,可以new一个对象实例,而在JavaScript中,对象也是通过new构造函数生成的,但,却和面向对象语言的new方式是不一样。下面来看看new操作符到底做了什么。
function foo() {
console.log(this.a);
} var obj = new foo();
当使用new来调用函数时,发生了以下步骤:
a、创建了一个全新的对象,如:var obj = {};
b、连接全新对象与调用函数之间的[[Prototype]],让函数的prototype指向全新对象,如obj.__proto__ = foo.prototype;
c、新对象会绑定到函数调用的this,如:foo.call(obj);
d、返回对象。如果函数没有返回其他对象,那么new操作会自动返回这个新对象。
三、优先级
一般情况下:new绑定 > 显式绑定 > 隐式绑定 > 默认绑定
四、总结
还有其他的一些细节知识点,推荐还是看《你不知道的JavaScript上卷》这本书,真心不错。