嘿,加油😍
文章目录
- 一、this?
- 二、this 的指向
- 三、默认绑定(独立函数调用)
- 四、隐式绑定
- 五、显式绑定 (apply call bind)
- 六、new绑定 (后面会详细再补充)
- 七、apply call bind 区别
- 八、内置函数的绑定思考
- 九、规则优先级
- 十、说明
- 十一、this 规则之外
- 1. 忽略显示绑定
- 2. ES6箭头函数
一、this?
在其他面向对象编程中,this 一般会出现在类的方法里,但是在js中,this更灵活,无论是它出现的位置还是它代表的含义。首先this在全局里指向window, 但是,开发中很少直接在全局作用于下去使用this,通常都是在函数中使用,在之前文章里说过,当我们函数调用时,会创建一个执行上下文,这个上下文记录着函数的调用栈,AO对象,还有其他,当然还记录着一个很重要的东西,this
二、this 的指向
function foo() {
console.log(this);
}
// 1.调用方式一:直接调用;
foo(); //window
// 2.调用方式二:将foo放到一个对象中,再调用
var obj = { name: "why", foo2: foo };
obj.foo2(); //obj对象
// 3.调用方式三:通过ca1l/apply调用
foo.call("abc"); // String {"abc"子对象}
根据这个案例,我们可以发现
- 函数在调用时,JavaScript会默认给this绑定一个值; windows
- this的绑定和定义的位置(编写的位置)没有关系,和调用方式以及调用的位置有关系
- this是在运行时被绑定的
那么this到底是怎么样的绑定规则呢?
- 绑定一:默认绑定;
- 绑定二:隐式绑定;
- 绑定三:显示绑定;
- 绑定四:new绑定
三、默认绑定(独立函数调用)
当独立函数调用时,JavaScript会默认给this绑定一个值: windows
独立的函数调用我们可以理解成 函数没有被绑定到某个对象上进行调用
四、隐式绑定
另外一种比较常见的调用方式是通过某个对象进行调用,也就是它的调用位置中,是通过某个对象发起的函数调用。
五、显式绑定 (apply call bind)
隐式绑定有一个前提条件:
- 必须在调用的对象内部有一个对函数的引用(比如一个属性);
- 如果没有这样的引用,在进行调用时,会报找不到该函数的错误;
- 正是通过这个引用,间接的将this绑定到了这个对象上;
如果我们不希望在 对象内部 包含这个函数的引用,同时又希望在这个对象上进行强制调用,该怎么做呢?
-
JavaScript所有的函数都可以使用call和apply方法(这个和
Prototype
有关)。 -
这两个函数的第一个参数都要求是一个对象,这个对象的作用是什么呢?就是给this准备的。
-
在调用这个函数时,会将this绑定到这个传入的对象上。
-
因为上面的过程,我们明确的绑定了this指向的对象,所以称之为 显示绑定。
六、new绑定 (后面会详细再补充)
JavaScript中的函数可以当做一个类的构造函数来使用,也就是使用new关键字。
使用new关键字来调用函数是,会执行如下的操作:
- 首先他会创建一个全新的对象;
- 这个新对象会被执行prototype连接;
- 这个新对象会绑定到函数调用的this上(this的绑定在这个步骤完成);
- 如果函数没有返回其他对象,表达式会返回这个新对象;
我们通过一个new关键字调用一个函数时(构造器),这个时候this是再调用这个构造器时创建出来的对象,this=创建出来的对象,这个绑定过程也称为new绑定
七、apply call bind 区别
//比如夏夏有个充电宝,它可以使用charge这个充电宝给自己的手机电池phoneBattery任意充电
const xiaxia={
name:'xiaxia',
phoneBattery:70,
charge:function (level){
this.phoneBattery=level
}
},
xiaxia.charge(100)
//但是haha手机没电了,也不想买充电宝,但是它可以借xiaxia的
const haha={
name:'haha',
phoneBattery:40,
},
xiaxia.charge.call(haha,100)
//方便演示apply 的用法,假设充电宝必须冲两次
const xiaxia={
name:'xiaxia',
phoneBattery:70,
charge:function (level,newlevel){
this.phoneBattery=level+newlevel
}
}
// 如果使用 call 方式
xiaxia.charge.call(haha,20,50)
// 如果使用 apply 方式
xiaxia.charge.apply(haha,[20,50])
// bind 方式,其实是借了充电宝,但不是立即使用,而是过一会使用
const hahaMethods=xiaxia.charge.apply(haha)
haha.hahaMethods(20,50)
八、内置函数的绑定思考
有些时候,我们会调用一些JavaScript的内置函数,或者一些第三方库中的内置函数。这些内置函数会要求我们传入另外一个函数; 我们自己并不会显示的调用这些函数,而且JavaScript内部或者第三方库内部会帮助我们执行;这些函数中的this又是如何绑定的呢?
可以通过上图发现,对于内置函数,他们的this情况,比如对于setTimeout ,他是一个独立执行,默认绑定;看第四张图,它有提示thisArg ,其实是说我们可以自己更改他的this指向,比如第三张图,如果我们传入第二个参数为obj,就把他的this执行为obj了
九、规则优先级
学习了四条规则,接下来开发中我们只需要去查找函数的调用应用了哪条规则即可,但是如果一个函数调用位置应用了多条规则,优先级谁更高呢?
- 默认规则的优先级最低
毫无疑问,默认规则的优先级是最低的,因为存在其他规则时,就会通过其他规则的方式来绑定this - 显示绑定优先级高于隐式绑定 (案例一) (案例二 )
- new绑定优先级高于隐式绑定 (案例三)
- new绑定优先级高于bind ( 因为new关键字是不能和apply/call 一起使用的)
new绑定和call、apply是不允许同时使用的,所以不存在谁的优先级更高
new绑定可以和bind一起使用,new绑定优先级更高
var obj={
name:'obj'
foo:function(){
console.log(this)
}
}
// 当这个有显示绑定(call)和隐式绑定(obj)时,发现他的this指向为 abc
// 说明显示绑定优先级高于隐式绑定
obj.foo.call('abc')
var obj={
name:'obj'
foo:function(){
console.log(this)
}
}
// 虽然这个指向指向的是 abc,但是并不能体现 显示绑定优先级高于隐式绑定
// 因为现在执行的是 obj 的返回的函数,绑定到abc上
// 当foo2执行时 与obj其实是没关系的,只是执行的时候引用了而已
var foo2 = obj.foo.bind('abc')
foo2()
function foo(){
console.log(this)
}
var obj={
name:'obj'
foo:foo.bind('aaa')
}
obj.foo() //这样会更明显 指向的是aaa
var obj={
name:'obj'
foo:function(){
console.log(this)
}
}
var f = new obj.foo() //指向的是foo 说明new的高于隐式
十、说明
不管函数在什么位置声明,在内存中都是在堆内存中单独分配的空间,得到该函数地址的变量都可以对函数进行调用,所以函数中的this指向与调用位置无关,也就是谁调用就指向谁
十一、this 规则之外
1. 忽略显示绑定
apply / call /bind : 当传入 null / undefined 时, 那么这个显示绑定会被忽略,使用默认规则
function foo(){
console.log(this)
}
foo.apply('abc')
foo.apply({})
foo.apply(null)
foo.apply(undefined)
2. ES6箭头函数
箭头函数并不绑定this对象,那么this引用就会从上层作用于中找到对应的this