springboot实战电商项目mall4j (https://gitee.com/gz-yami/mall4j)
JavaScript中的this绑定
在日常的开发中,我们会经常使用JavaScript中的一个关键字:this,在常见的编程语言中,几乎都有this这个关键字,但是JavaScript中的this和常见的变成语言中的this不太一样,
在常见的变成语言(java,c++等)中,this通常只会出现在类的方法中,而this指向它当前调用的对象,但是在JavaScript中,this是更加灵活的,无论是它出现的位置还是它代表的含义。
this全局作用下的指向
这个问题非常容易回答,在浏览器中,this的指向为全局对象window
console.log(this) // window 对象
var name = "hhf"
console.log(this.name) // hhf
console.log(window.name) // hhf
但是,开发中很少直接在全局作用于下去使用this,通常都是在函数中使用的
this到底指向什么呢?
下面我们通过一段代码,代码中,我们定义一个函数,对他进行三种不同的方式进行调用,它产生了不同的结果
function foo() {
console.log(this)
}
foo() // window对象
const obj = {
name: "hhf",
foo: foo
}
obj.foo() // obj1
const obj2 = {}
foo.call(obj2) // obj2
从上面代码运行的结果我们得出:
1.函数在调用时,JavaScript会默认给this绑定一个值;
2.this的绑定和定义的位置(编写的位置)没有关系;
3.this的绑定和调用方式以及调用的位置有关系;
4.this是在运行时被绑定的
在JavaScript中,this有四种绑定规则,分别是:
1.默认绑定
2.隐式绑定
3.显式绑定
4.new绑定
下面我们分别对这四种绑定规则进行学习
默认绑定
默认绑定通常是在独立函数进行调用时进行绑定,独立函数调用我们可以理解成没有被绑定到某个对象进行调用,默认绑定在浏览器中指向的是window,当为严格模式(use strict)的时候指向的是undefined
// 案例一
function foo() {
console.log(this)
}
foo() // window对象
// 案例二
function foo(fn) {
fn()
}
const obj = {
name: "hhf",
bar: function() {
console.log(this)
}
}
foo(obj.bar) // window
显示绑定
显示绑定通常是某个对象对它进行调用,通俗来讲:谁调用就指向谁
function foo() {
console.log(this.name);
}
const obj = {
name: "hhf",
bar: foo
}
obj.bar() // hhf
隐示绑定的另一种情况:
当有多层对象嵌套调用某个函数的时候,如 对象.对象.函数
,this 指向的是最后一层对象。
function foo() {
console.log(this.name);
}
const person = {
name: "person"
}
person.foo = foo
const obj = {
name: "hhf",
bar: foo,
person: person
}
obj.person.foo() // person
显式绑定
在JavaScript中,所有的函数都可以使用call、apply、bind三个方法对函数的this进行绑定
使用方式的不同:call、apply在函数调用时对它进行调用,bind会返回一个新的函数
显示绑定的用途: 防抖、节流等
call函数的使用
call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。
它接收的参数为:第一个为绑定的this,后面接上的为所调用的函数的参数
具体使用方法如下
// 基本使用
function foo() {
console.log(this.name);
}
const obj = {
name: "hhf"
}
foo.call(obj) // hhf
// 传入参数
function foo(n, m) {
console.log(this.name);
console.log(n, m)
}
const obj = {
name: "hhf"
}
foo.call(obj, "n", "m") // hhf n m
apply函数的使用
apply方法的语法和作用与 call() 方法类似,只有一个区别,就是 call() 方法接受的是一个参数列表,而 apply() 方法接受的是一个包含多个参数的数组。
具体使用方法如下
function foo(n, m) {
console.log(this.name);
console.log(n, m)
}
const obj = {
name: "hhf"
}
foo.call(obj, ["n", "m"]) // hhf, n m
bind函数的使用
bind函数它所接收的参数和call函数一样,但是它会返回一个新的函数,新的函数的this会指向传入的对象
function foo(n, m) {
console.log(this.name);
console.log(n, m)
}
const obj = {
name: "hhf"
}
const newFoo = foo.bind(obj, "n", "m")
newFoo() // hhf n m
new 绑定
new是JavaScript中的一个关键字,当进行new操作调用函数时,会执行如下的操作
1.函数内部会创建一个新的对象
2.创建的对象的原型(__proto__)会指向函数的prototype
3.所创建的对象会绑定到该函数的this上
4.如果函数没有其他返回值,会默认返回该对象
function Persion() {
console.log(this)
}
new Persion(); // Persion {}
规则优先级
上面我们学习了四种绑定规则,那么我们可能会思考,如果一个函数在调用的时候使用了多种绑定规则,谁的优先级最高呢?
结果如下
1.默认规则的优先级最低p毫无疑问,默认规则的优先级是最低的,因为存在其他规则时,就会通过其他规则的方式来绑定this
2.显示绑定优先级高于隐式绑定
function foo() {
console.log(this.name)
}
const obj1 = {
name: 'obj1',
foo: foo
}
const obj2 = {
name: 'obj2',
}
obj1.foo.call(obj2) // obj2
3.new绑定优先级高于隐式绑定
function foo() {
console.log(this)
}
const obj1 = {
name: 'obj1',
foo: foo
}
const obj2 = {
name: 'obj2',
}
new obj1.foo() // foo {}
4.new绑定优先级高于bind
new绑定和call、apply是不允许同时使用的,所以不存在谁的优先级更高
new绑定可以和bind一起使用,new绑定优先级更高p代码测试
function foo() {
console.log(this)
}
const obj1 = {
name: 'obj1',
foo: foo
}
const newFoo = foo.bind(obj1)
new newFoo() // foo {}
箭头函数的this
箭头函数是ES6中新增的一种函数的写法,但是箭头函数是不绑定this的,当在箭头函数中使用this时,它会随着它的作用域网上找,使用最近的作用域的this来使用
// 使用普通函数
const obj1 = {
name: 'obj1',
foo: function() {
console.log(this)
}
}
obj1.foo() // obj1
// 使用箭头函数
const obj1 = {
name: 'obj1',
foo: ()=> {
console.log(this)
}
}
obj1.foo() // window foo的上层作用域为window
// setTimeout所传入的函数如果是普通函数,那么它绑定的是全局对象window,如果传入的是一个箭头函数,那么它的this执行是它的上层作用域的this指向
const obj1 = {
name: 'obj1',
bar: function() {
setTimeout(()=> {
console.log(this)
})
}
}
obj1.bar() // obj1
下面我们通过一道题,对刚刚所学的进行一个小练习
var name = "window"
function Person(name) {
this.name = name
this.obj = {
name: "obj",
foo1: function() {
return function() {
console.log(this.name)
}
},
foo2: function() {
return ()=>{
console.log(this.name)
}
}
}
}
var person1 = new Person("person1")
var person2 = new Person("person2")
person1.obj.foo1()()
person1.obj.foo1.call(person2)()
person1.obj.foo1().call(person2)
person1.obj.foo2()()
person1.obj.foo2.call(person2)()
person1.obj.foo2().call(person2)
输出结果为
/*
window
window
person2
obj
person2
obj
*/