我们先来看一个让人困惑的问题:定义一个函数,我们采用三种不同的方式对它进行调用,它产生了三种不同的结果 。
//第一种情况 普通函数被独立的调用
function foo(){
console.log(this);
};
foo(); //这个this指向window
"use strict"
//第二种情况 在严格模式下
function foo(){
console.log(this);
};
foo(); //这个this指向undefined
//第三种情况 在对象中声明一个函数调用
var obj = {
name:"张三",
foo:function foo(){
console.log(this);
}
}
obj.foo(); //这个this指向obj对象
这个的案例可以给我们什么样的启示呢?
1.函数在调用时,JavaScript会默认给this绑定一个值;
2.this的绑定和定义的位置(编写的位置)没有关系;
3.this的绑定和调用方式以及调用的位置有关系;
4.this是在运行时被绑定的;
那么this到底是怎么样的绑定规则呢?一起来学习一下吧
绑定一:默认绑定;
绑定二:隐式绑定;
绑定三:显示绑定;
绑定四:new绑定;
下面我们来看每个绑定规则到底是什么样子的!
规则一:默认绑定
我们一般在什么情况下使用默认绑定呢?那就是独立函数调用。
那什么叫做独立函数调用呢,独立的函数调用我们可以理解成函数没有被绑定到某个对象上进行调用;
我们通过几个案例来看一下常见的默认绑定
<script>
function foo(){
console.log(this);
}
foo();
// 可以理解为 foo函数挂载在全局对象内 所以这个this指向window
</script>
<script>
function foo(){
console.log(this);
}
foo();
// 正常情况下 这个时候this指向window
// 但如果我们将这个函数当作对象的属性值 函数被挂载到了对象里面 这个时候函数内的this指向为当前这个对象
// 如下所示
var obj = {
name:"张三",
// foo:function foo(){
// console.log(this);
// }
}
obj.foo = foo;
obj.foo();
// 可以看到 第一个this指向的是window 第二个指向的为 obj这个对象
</script>
<script>
function text1(){
console.log(this,"text1");
text2();
}
function text2(){
console.log(this,"text2");
text3();
}
function text3(){
console.log(this,"text3");
}
text1();
// 函数之间的嵌套 都是独立调用 独立调用指向的都是window
// 但也有例外 严格模式
</script>
默认绑定可以理解为就是独立函数调用
调用的时候 如果有前缀 例如 obj.bar(); 那么this指向这个前缀 obj 如果是独立调用 独立函数调用 例fn() 什么都没有 那就是全局对象 window 注意区分严格模式 严格模式下this就是undefined,非严格模式下this就是window
规则二:隐式绑定
另外一种比较常见的调用方式是通过某个对象进行调用的:也就是它的调用位置中,是通过某个对象发起的函数调用,this指向这个对象
我们通过几个案例来看一下,常见的隐式绑定
<script>
// 概念 什么是隐式绑定 所谓隐式绑定 就是以某个对象进行调用的
function foo(){
console.log(this);
}
var obj = {
name:"张三",
bar:foo
}
console.log(obj.bar);
obj.bar();
// 这就是一个简单的隐式绑定
// 通过obj这个对象 简单的调用foo这个函数
// 将这个函数当作属性值赋值给对象里面的属性 即属性:属性值 (函数当作这个属性值)
// 再通过对象的方法 调用这个属性 就是隐式绑定
</script>
<script>
function foo(){
console.log(this);
}
var obj1 = {
name:"张三",
foo:foo
}
var obj2 = {
name:"李四",
obj1 : obj1
}
obj2.obj1.foo();
// 隐式绑定就是 某个对象的调用
// 在这个地方 obj2调用obj1 obj1再调用foo函数 不管他们之间如何嵌套
// 谁调用的这个函数 这个函数里面的this就指向函数的调用者 obj1调用foo函数this就指向obj1
</script>
总计就是一句话:谁调用这个函数this就指向谁。在上面案例中,调用对象中的函数,那么this就指向这个对象。
规则三:显式绑定
隐式绑定有一个前提条件:
1.必须在调用的对象内部有一个对函数的引用(比如一个属性);
2.如果没有这样的引用,在进行调用时,会报找不到该函数的错误;
3.正是通过这个引用,间接的将this绑定到了这个对象上;
如果我们不希望在对象内部包含这个函数的引用,同时又希望在这个对象上进行强制调用,该怎么做呢?
这时候就用到了函数apply与call方法。(函数调用使用这个方法)
这两个方法都是函数调用时使用(第一个参数是强制this指向,写上什么this就指向什么,第二个参数是函数的实参,apply以数组的形式存放实参,call用列表的方式存入实参)
<script>
function preson(name,height,addr){
console.log(name,height,addr);
console.log("this的指向为:",this);
};
preson("曹操",1.88,"许昌");
// 现在我们使用 apply与call
// apply 第一个参数是 this指向的值 第二个参数就是写调用的实参 采用数组的形式
preson.apply("arr",["刘备",2,"成都"]);
// call方法 第一个参数 也是 this指向的值 第二个参数就是写调用的实参 采用列表的方法
preson.call("str","司马懿",1.8,"许都");
</script>
规则四:new绑定 构造函数绑定(this指向构造函数的实例化对象)
JavaScript中的函数可以当做一个类的构造函数来使用,也就是使用new关键字。
◼ 使用new关键字来调用函数是,会执行如下的操作:
1.创建一个全新的对象;
2.这个this就会指向创建的空对象
3.给这个空对象身上追加属性;
4.如果函数没有返回其他对象,表达式会返回这个新对象;
<script>
// 1.声明一个构造函数
function Proson(name,age){
this.name=name;
this.age=age;
console.log(this);
}
// 2. 声明构造函数的实例化对象
var p1 = new Proson("张三",20);
var p2 = new Proson("李四",20);
// 可以看出 构造函数里面 输出this this指向的是 构造函数的实例化对象
</script>
规则优先级
学习了四条规则,接下来开发中我们只需要去查找函数的调用应用了哪条规则即可,但是如果一个函数调用位置应用了多 条规则,优先级谁更高呢?
1.默认规则的优先级最低 毫无疑问,默认规则的优先级是最低的,因为存在其他规则时,就会通过其他规则的方式来绑定this
2.显示绑定优先级高于隐式绑定
3.new绑定优先级高于隐式绑定 new绑定和call、apply是不允许同时使用的,所以不存在谁的优先级更高
当然除了上面四种绑定规则 还有一种就是内置函数的绑定
内置函数的绑定
有些时候,我们会调用一些JavaScript的内置函数,或者一些第三方库中的内置函数。
这些内置函数会要求我们传入另外一个函数;
我们自己并不会显示的调用这些函数,而且JavaScript内部或者第三方库内部会帮助我们执行;
这些函数中的this又是如何绑定的呢?
<button>按钮</button>
<script>
//定时器内置函数 this指向window
setInterval(function(){
// console.log(this);//window
},2000);
// 点击事件的this指向 事件源
var btn = document.querySelector("button");
btn.onclick = function(){
console.log(this);
}
// 这个事件的事件源是btn this指向这个btn按钮
// 高阶函数的this指向 什么是高阶函数 一个函数需要另外一个函数作为他的参数 那就做高阶函数
var arr = ["zhangsan","lisi","wangwu"];
arr.forEach(function(){
console.log(this);
},6020);
// 默认情况下 this指向为window 但是这个forEach 拥有第二个参数 这个参数是什么 this就指向什么;
</script>
内置函数的this指向window
事件监听this指向事件源
高阶函数的this指向默认情况下是window 但高阶函数拥有第二个参数 这个参数是什么 this指向什么