函数在调用时,javaScript会默认给this绑定一个值,this的绑定和调用方式以及调用的位置有关系,this是在运行时被绑定的。
在全局作用域下this指向
- 在Node环境中this指向的是空对象(内部源码通过call绑定的exports, exports ={})
- 在浏览器之中指向的是window
this的绑定规则
1. 默认绑定
独立函数调用属于默认绑定,独立函数调用是指函数调用无任何前缀的情景。
默认绑定时,
- 在非严格模式下this指向全局对象,再严格模式中,this指向undefined,
2.隐式绑定
通过某对象进行调用,this指向调用它的那个对象
3.显示绑定
隐式绑定有一个前提条件,必须在调用的对象内部有一个对函数的引用;正是通过这个引用,间接的将this绑定到了这个对象上;当我们不在对象内部包含这个函数的引用,同时又希望在这个对象上进行强制调用,这时候我们就可以通过call、apply、bind进行显示绑定。
call、apply、bind
- 这三个函数的第一个参数都是要指向的对象,在调用这个函数时,会将this绑定到这个传入的对象上。如果为null或undefined时,那么这个显示绑定会被忽略,使用默认规则
- bind和call传递的调用参数参数是直接放进去的,第二第三第 n 个参数全都用逗号分隔,而apply的是参数数组
- call和apply都会在绑定后立即执行,但是bind会返回一个永久改变this指针的函数,方便之后调用
- call和apply都只能一次性传入参数,bind可以分开传递参数
call(thisArg, arg1,arg2,arg3, ...)
bind(thisArg,arg1,arg2,arg3,...);
apply(thisArg,[ arg1,arg2,arg3,...])
4.new绑定
javaScript中的函数可以当作一个类的构造函数来使用,也就是new关键字
new关键字会进行如下操作
- 创建一个空的简单JavaScript对象(即{})
- 为步骤1创建的对象添加属性_proto_将该属性链接到构造原型对象
- 将步骤1创建的对象作为this的上下文
- 如果该函数没有返回对象,则返回this
function Person(){
this.name = name
}
var p = new Person(‘小明’)
规则优先级
- 默认规则的优先级最低
默认规则的优先级是最低的,因为存在其他规则时,就会通过其他规则的方式来绑定this - 显示绑定优先级高于隐式绑定
function foo(){
console.log(this)//[String: 'aaa']
}
let obj = {
foo: foo
}
obj.foo.call(‘aaa’)
- new绑定优先级高于隐式绑定
function foo(){
console.log(this) // foo {}
}
let obj = {
foo: foo
}
let item = new obj.foo()
- new绑定优先级高于bind
- new绑定和call、apply是不允许同时使用的,所以不存在谁的优先级更高
- new绑定可以和bind一起使用,new绑定优先级更高
function foo() {
console.log(this)//foo {}
}
var bar = foo.bind("aaa")
var obj = new bar()
内置函数的绑定思考
有时候,我们会调用一些javaScript的内置函数,或者第三方库中的内置函数,这些内置函数会要求我们传入另外一个函数,这些函数中的this是怎么绑定的呢?
1. setTimeout
setTimeout在传入非箭头函数的情况下this始终指向window,无论是严格模式还是非严格模式下,其内部执行使用的是apply将this指向window
setTimeout(function() {
console.log(this) // window
}, 2000)
2. forEach/map/filter/find
第一个参数:必须。函数,数组中的每个元素都会执行这个函数
函数参数。
第二个参数:可选。对象作为该执行回调时使用,传递给函数,用作 “this” 的值。如果省略了 thisValue,或者传入 null、undefined,那么回调函数的 this 为全局对象。
let array = ["11", "22", "33"]
array.forEach(function(item) {
console.log(item, this)
}, "abc")
3. div的点击
this指向的是元素对象
const boxDiv = document.querySelector('.box')
boxDiv.onclick = function() {
console.log(this)
}
this规则之外 - 间接函数引用
创建一个函数的简介引用,这种情况使用默认绑定规则
var obj1 = {
name: "obj1",
foo: function() {
console.log(this)
}
}
var obj2 = {
name: "obj2"
};
console.log(obj2.bar = obj1.foo); // 返回foo
(obj2.bar = obj1.foo)() // window
- 赋值(obj2.bar = obj1.foo)的结果是foo,
- foo函数被直接调用,是默认绑定
this规则之外 – ES6箭头函数
箭头函数不使用this的四种标准规则(也就是不绑定this),而是根据外层作用域来决定this。箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this。
箭头函数
MDN:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/Arrow_functions
基础语法
(param1, param2, …, paramN) => { statements }
当只有一个参数时,圆括号是可选的
singleParam => { statements }
没有参数的函数应该写成一对圆括号
() => { statements }
如果函数执行体只有一行代码, 那么{}也可以省略,并且它会默认将这行代码的执行结果作为返回值
() => statements
如果一个箭头函数, 只有一行代码, 并且返回一个对象
let foo = () => ({ name: "why", age: 18 })
箭头函数与普通函数的区别
- 箭头函数是匿名函数
- 箭头函数不能作为构造函数来使用,不能使用new,没有原型(prototype)属性
- 箭头函数不能绑定arguments,用rest参数解决
- 箭头函数不会绑定this,根据外层作用域来决定